1 /** 2 * TLS Client 3 * 4 * Copyright: 5 * (C) 2004-2011,2015 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.tls.client; 12 13 import botan.constants; 14 static if (BOTAN_HAS_TLS): 15 16 public import botan.tls.channel; 17 public import botan.tls.credentials_manager; 18 public import botan.tls.server_info; 19 public import botan.rng.rng; 20 import botan.tls.handshake_state; 21 import botan.tls.messages; 22 import memutils.dictionarylist; 23 import botan.utils.types; 24 25 /** 26 * SSL/TLS Client 27 */ 28 final class TLSClient : TLSChannel 29 { 30 public: 31 /** 32 * Set up a new TLS client session 33 * 34 * Params: 35 * socket_output_fn = is called with data for the outbound socket 36 * proc_cb = is called when new application data is received 37 * alert_cb = is called when a TLS alert is received 38 * handshake_cb = is called when a handshake is completed 39 * session_manager = manages session state 40 * creds = manages application/user credentials 41 * policy = specifies other connection policy information 42 * rng = a random number generator 43 * server_info = is identifying information about the TLS server 44 * offer_version = specifies which version we will offer 45 * to the TLS server. 46 * next_protocols = specifies protocols to advertise with ALPN 47 * reserved_io_buffer_size = This many bytes of memory will 48 * be preallocated for the read and write buffers. Smaller 49 * values just mean reallocations and copies are more likely. 50 */ 51 this(void delegate(in ubyte[]) socket_output_fn, 52 void delegate(in ubyte[]) proc_cb, 53 void delegate(in TLSAlert, in ubyte[]) alert_cb, 54 bool delegate(in TLSSession) handshake_cb, 55 TLSSessionManager session_manager, 56 TLSCredentialsManager creds, 57 in TLSPolicy policy, 58 RandomNumberGenerator rng, 59 in TLSServerInformation server_info = TLSServerInformation(), 60 in TLSProtocolVersion offer_version = TLSProtocolVersion.latestTlsVersion(), 61 Vector!string next_protocols = Vector!string(), 62 size_t reserved_io_buffer_size = 16*1024) 63 { 64 super(socket_output_fn, proc_cb, alert_cb, handshake_cb, session_manager, rng, offer_version.isDatagramProtocol(), reserved_io_buffer_size); 65 m_policy = policy; 66 m_creds = creds; 67 m_info = server_info; 68 const string srp_identifier = m_creds.srpIdentifier("tls-client", m_info.hostname()); 69 HandshakeState state = createHandshakeState(offer_version); 70 sendClientHello(state, false, offer_version, srp_identifier, next_protocols.move()); 71 } 72 73 protected: 74 override Vector!X509Certificate getPeerCertChain(in HandshakeState state) const 75 { 76 if (state.serverCerts()) 77 return state.serverCerts().certChain().dup; 78 return Vector!X509Certificate(); 79 } 80 81 /** 82 * Send a new client hello to renegotiate 83 */ 84 override void initiateHandshake(HandshakeState state, bool force_full_renegotiation) 85 { 86 sendClientHello(state, force_full_renegotiation, state.Version()); 87 } 88 89 void sendClientHello(HandshakeState state_base, 90 bool force_full_renegotiation, 91 TLSProtocolVersion _version, 92 in string srp_identifier = "", 93 Vector!string next_protocols = Vector!string()) 94 { 95 ClientHandshakeState state = cast(ClientHandshakeState)(state_base); 96 if (state.Version().isDatagramProtocol()) 97 state.setExpectedNext(HELLO_VERIFY_REQUEST); // optional 98 state.setExpectedNext(SERVER_HELLO); 99 100 if (!force_full_renegotiation && !m_info.empty) 101 { 102 TLSSession session_info; 103 if (sessionManager().loadFromServerInfo(m_info, session_info)) 104 { 105 if (srp_identifier == "" || session_info.srpIdentifier() == srp_identifier) 106 { 107 state.clientHello(new ClientHello( 108 state.handshakeIo(), 109 state.hash(), 110 m_policy, 111 rng(), 112 secureRenegotiationDataForClientHello().dup, 113 session_info, 114 next_protocols.move)); 115 116 state.resume_master_secret = session_info.masterSecret().dup; 117 } 118 } 119 } 120 121 if (!state.clientHello()) // not resuming 122 { 123 state.clientHello(new ClientHello(state.handshakeIo(), 124 state.hash(), 125 _version, 126 m_policy, 127 rng(), 128 secureRenegotiationDataForClientHello().dup, 129 next_protocols.move, 130 m_info.hostname(), 131 srp_identifier)); 132 } 133 134 secureRenegotiationCheck(state.clientHello()); 135 } 136 137 /** 138 * Process a handshake message 139 */ 140 override void processHandshakeMsg(in HandshakeState active_state, 141 HandshakeState state_base, 142 HandshakeType type, 143 const ref Vector!ubyte contents) 144 { 145 ClientHandshakeState state = cast(ClientHandshakeState)(state_base); 146 147 if (type == HELLO_REQUEST && active_state) 148 { 149 auto hello_request = scoped!HelloRequest(contents); 150 151 // Ignore request entirely if we are currently negotiating a handshake 152 if (state.clientHello()) 153 return; 154 155 if (!m_policy.allowServerInitiatedRenegotiation() || 156 (!m_policy.allowInsecureRenegotiation() && !secureRenegotiationSupported())) 157 { 158 // RFC 5746 section 4.2 159 sendWarningAlert(TLSAlert.NO_RENEGOTIATION); 160 return; 161 } 162 163 this.initiateHandshake(state, false); 164 165 return; 166 } 167 168 state.confirmTransitionTo(type); 169 170 if (type != HANDSHAKE_CCS && type != FINISHED && type != HELLO_VERIFY_REQUEST) 171 state.hash().update(state.handshakeIo().format(contents, type)); 172 173 if (type == HELLO_VERIFY_REQUEST) 174 { 175 state.setExpectedNext(SERVER_HELLO); 176 state.setExpectedNext(HELLO_VERIFY_REQUEST); // might get it again 177 178 auto hello_verify_request = scoped!HelloVerifyRequest(contents); 179 180 state.helloVerifyRequest(hello_verify_request.Scoped_payload); 181 } 182 else if (type == SERVER_HELLO) 183 { 184 state.serverHello(new ServerHello(contents)); 185 186 if (!state.clientHello().offeredSuite(state.serverHello().ciphersuite())) 187 { 188 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSServer replied with ciphersuite we didn't send"); 189 } 190 191 if (!valueExists(state.clientHello().compressionMethods(), 192 state.serverHello().compressionMethod())) 193 { 194 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSServer replied with compression method we didn't send"); 195 } 196 197 auto client_extn = state.clientHello().extensionTypes(); 198 auto server_extn = state.serverHello().extensionTypes(); 199 200 import std.algorithm : setDifference; 201 import std.range : empty, array; 202 auto diff = setDifference(server_extn[].sort(), client_extn[].sort()); 203 if (!diff.empty) 204 { 205 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, 206 "TLSServer sent extension(s) " ~ diff.array.to!(string[]).joiner(", ").to!string ~ " but we did not request it"); 207 } 208 209 state.setVersion(state.serverHello().Version()); 210 m_application_protocol = state.serverHello().nextProtocol(); 211 212 secureRenegotiationCheck(state.serverHello()); 213 214 const bool server_returned_same_session_id = !state.serverHello().sessionId().empty && 215 (state.serverHello().sessionId() == state.clientHello().sessionId()); 216 217 if (server_returned_same_session_id) 218 { 219 // successful resumption 220 221 /* 222 * In this case, we offered the version used in the original 223 * session, and the server must resume with the same version. 224 */ 225 if (state.serverHello().Version() != state.clientHello().Version()) 226 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSServer resumed session but with wrong version"); 227 228 state.computeSessionKeys(state.resume_master_secret); 229 230 if (state.serverHello().supportsSessionTicket()) 231 state.setExpectedNext(NEW_SESSION_TICKET); 232 else 233 state.setExpectedNext(HANDSHAKE_CCS); 234 } 235 else 236 { 237 // new session 238 239 if (state.clientHello().Version().isDatagramProtocol() != 240 state.serverHello().Version().isDatagramProtocol()) 241 { 242 throw new TLSException(TLSAlert.PROTOCOL_VERSION, "TLSServer replied with different protocol type than we offered"); 243 } 244 245 if (state.Version() > state.clientHello().Version()) 246 { 247 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSServer replied with later version than in hello"); 248 } 249 250 if (!m_policy.acceptableProtocolVersion(state.Version())) 251 { 252 throw new TLSException(TLSAlert.PROTOCOL_VERSION, "TLSServer version " ~ state.Version().toString() ~ " is unacceptable by policy"); 253 } 254 255 if (state.ciphersuite().sigAlgo() != "") 256 { 257 state.setExpectedNext(CERTIFICATE); 258 } 259 else if (state.ciphersuite().kexAlgo() == "PSK") 260 { 261 /* PSK is anonymous so no certificate/cert req message is 262 ever sent. The server may or may not send a server kex, 263 depending on if it has an identity hint for us. 264 265 (EC)DHE_PSK always sends a server key exchange for the 266 DH exchange portion. 267 */ 268 269 state.setExpectedNext(SERVER_KEX); 270 state.setExpectedNext(SERVER_HELLO_DONE); 271 } 272 else if (state.ciphersuite().kexAlgo() != "RSA") 273 { 274 state.setExpectedNext(SERVER_KEX); 275 } 276 else 277 { 278 state.setExpectedNext(CERTIFICATE_REQUEST); // optional 279 state.setExpectedNext(SERVER_HELLO_DONE); 280 } 281 } 282 } 283 else if (type == CERTIFICATE) 284 { 285 if (state.ciphersuite().kexAlgo() != "RSA") 286 { 287 state.setExpectedNext(SERVER_KEX); 288 } 289 else 290 { 291 state.setExpectedNext(CERTIFICATE_REQUEST); // optional 292 state.setExpectedNext(SERVER_HELLO_DONE); 293 } 294 295 state.serverCerts(new Certificate(contents)); 296 297 const Vector!X509Certificate* server_certs = &state.serverCerts().certChain(); 298 299 if (server_certs.empty) 300 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSClient: No certificates sent by server"); 301 302 try 303 { 304 m_creds.verifyCertificateChain("tls-client", m_info.hostname(), *server_certs); 305 } 306 catch(Exception e) 307 { 308 throw new TLSException(TLSAlert.BAD_CERTIFICATE, e.msg); 309 } 310 311 PublicKey peer_key = (*server_certs)[0].subjectPublicKey(); 312 313 if (peer_key.algoName != state.ciphersuite().sigAlgo()) 314 throw new TLSException(TLSAlert.ILLEGAL_PARAMETER, "Certificate key type did not match ciphersuite"); 315 316 state.server_public_key = peer_key; 317 } 318 else if (type == SERVER_KEX) 319 { 320 state.setExpectedNext(CERTIFICATE_REQUEST); // optional 321 state.setExpectedNext(SERVER_HELLO_DONE); 322 323 state.serverKex(new ServerKeyExchange(contents, 324 state.ciphersuite().kexAlgo(), 325 state.ciphersuite().sigAlgo(), 326 state.Version())); 327 328 if (state.ciphersuite().sigAlgo() != "") 329 { 330 const PublicKey server_key = state.getServerPublicKey(); 331 if (!state.serverKex().verify(server_key, state)) 332 { 333 throw new TLSException(TLSAlert.DECRYPT_ERROR, "Bad signature on server key exchange"); 334 } 335 } 336 } 337 else if (type == CERTIFICATE_REQUEST) 338 { 339 state.setExpectedNext(SERVER_HELLO_DONE); 340 state.certReq(new CertificateReq(contents, state.Version())); 341 } 342 else if (type == SERVER_HELLO_DONE) 343 { 344 state.serverHelloDone(new ServerHelloDone(contents)); 345 346 if (state.receivedHandshakeMsg(CERTIFICATE_REQUEST)) 347 { 348 const(Vector!string)* types = &state.certReq().acceptableCertTypes(); 349 350 Vector!X509Certificate client_certs = m_creds.certChain(*types, "tls-client", m_info.hostname()); 351 352 state.clientCerts(new Certificate(state.handshakeIo(), state.hash(), client_certs)); 353 } 354 355 state.clientKex(new ClientKeyExchange(state.handshakeIo(), 356 state, 357 m_policy, 358 m_creds, 359 state.server_public_key.get(), 360 m_info.hostname(), 361 rng())); 362 363 state.computeSessionKeys(); 364 365 if (state.receivedHandshakeMsg(CERTIFICATE_REQUEST) && !state.clientCerts().empty) 366 { 367 PrivateKey priv_key = m_creds.privateKeyFor(state.clientCerts().certChain()[0], "tls-client", m_info.hostname()); 368 369 state.clientVerify(new CertificateVerify(state.handshakeIo(), 370 state, 371 m_policy, 372 rng(), 373 priv_key)); 374 } 375 376 state.handshakeIo().send(scoped!ChangeCipherSpec()); 377 378 changeCipherSpecWriter(CLIENT); 379 380 state.clientFinished(new Finished(state.handshakeIo(), state, CLIENT)); 381 382 if (state.serverHello().supportsSessionTicket()) 383 state.setExpectedNext(NEW_SESSION_TICKET); 384 else 385 state.setExpectedNext(HANDSHAKE_CCS); 386 } 387 else if (type == NEW_SESSION_TICKET) 388 { 389 state.newSessionTicket(new NewSessionTicket(contents)); 390 391 state.setExpectedNext(HANDSHAKE_CCS); 392 } 393 else if (type == HANDSHAKE_CCS) 394 { 395 state.setExpectedNext(FINISHED); 396 397 changeCipherSpecReader(CLIENT); 398 } 399 else if (type == FINISHED) 400 { 401 state.serverFinished(new Finished(contents.dup)); 402 403 if (!state.serverFinished().verify(state, SERVER)) 404 throw new TLSException(TLSAlert.DECRYPT_ERROR, "Finished message didn't verify"); 405 406 state.hash().update(state.handshakeIo().format(contents, type)); 407 408 if (!state.clientFinished()) // session resume case 409 { 410 state.handshakeIo().send(scoped!ChangeCipherSpec()); 411 412 changeCipherSpecWriter(CLIENT); 413 414 state.clientFinished(new Finished(state.handshakeIo(), state, CLIENT)); 415 } 416 417 Vector!ubyte session_id = state.serverHello().sessionId().dup; 418 419 Vector!ubyte session_ticket = state.sessionTicket(); 420 421 if (session_id.empty && !session_ticket.empty) 422 session_id = makeHelloRandom(rng(), m_policy); 423 424 auto session_info = new TLSSession(session_id.dup, 425 state.sessionKeys().masterSecret().dup, 426 state.serverHello().Version(), 427 state.serverHello().ciphersuite(), 428 state.serverHello().compressionMethod(), 429 CLIENT, 430 state.serverHello().fragmentSize(), 431 getPeerCertChain(state), 432 session_ticket.move(), 433 m_info, 434 ""); 435 scope(exit) destroy(session_info); 436 const bool should_save = saveSession(session_info); 437 438 if (!session_id.empty) 439 { 440 if (should_save) 441 sessionManager().save(session_info); 442 else { 443 auto entry = &session_info.sessionId(); 444 sessionManager().removeEntry(*entry); 445 } 446 } 447 448 activateSession(); 449 } 450 else 451 throw new TLSUnexpectedMessage("Unknown handshake message received"); 452 } 453 454 override HandshakeState newHandshakeState(HandshakeIO io) 455 { 456 return new ClientHandshakeState(io); 457 } 458 459 private: 460 const TLSPolicy m_policy; 461 TLSCredentialsManager m_creds; 462 const TLSServerInformation m_info; 463 } 464 465 466 private final class ClientHandshakeState : HandshakeState 467 { 468 public: 469 470 this(HandshakeIO io, void delegate(in HandshakeMessage) msg_callback = null) 471 { 472 super(io, msg_callback); 473 } 474 475 const(PublicKey) getServerPublicKey() const 476 { 477 assert(server_public_key, "TLSServer sent us a certificate"); 478 return *server_public_key; 479 } 480 481 // Used during session resumption 482 SecureVector!ubyte resume_master_secret; 483 484 Unique!PublicKey server_public_key; 485 }