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 * 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().clone; 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().clone, 113 session_info, 114 next_protocols.move)); 115 116 state.resume_master_secret = session_info.masterSecret().clone; 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().clone, 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 // Setup channelID 381 bool supports_channel_id = state.clientHello().supportsChannelID() && state.serverHello().supportsChannelID(); 382 if (supports_channel_id) { 383 state.channelID(new ChannelID(state.handshakeIo(), 384 state.hash(), 385 m_creds, 386 m_info.hostname, 387 state.hash().flushInto(state.Version(), state.ciphersuite().prfAlgo()) 388 )); 389 } 390 391 state.clientFinished(new Finished(state.handshakeIo(), state, CLIENT)); 392 393 if (supports_channel_id) 394 state.setOriginalHandshakeHash(state.hash().flushInto(state.Version(), state.ciphersuite().prfAlgo())); 395 396 if (state.serverHello().supportsSessionTicket()) 397 state.setExpectedNext(NEW_SESSION_TICKET); 398 else 399 state.setExpectedNext(HANDSHAKE_CCS); 400 } 401 else if (type == NEW_SESSION_TICKET) 402 { 403 state.newSessionTicket(new NewSessionTicket(contents)); 404 405 state.setExpectedNext(HANDSHAKE_CCS); 406 } 407 else if (type == HANDSHAKE_CCS) 408 { 409 state.setExpectedNext(FINISHED); 410 411 changeCipherSpecReader(CLIENT); 412 413 } 414 else if (type == FINISHED) 415 { 416 state.serverFinished(new Finished(contents.clone)); 417 418 if (!state.serverFinished().verify(state, SERVER)) 419 throw new TLSException(TLSAlert.DECRYPT_ERROR, "Finished message didn't verify"); 420 421 state.hash().update(state.handshakeIo().format(contents, type)); 422 423 if (!state.clientFinished()) // session resume case 424 { 425 state.handshakeIo().send(scoped!ChangeCipherSpec()); 426 427 changeCipherSpecWriter(CLIENT); 428 429 // Setup Resumption ChannelID 430 if (state.clientHello().supportsChannelID() && state.serverHello().supportsChannelID()) 431 { 432 TLSSession sess; 433 scope(exit) if (sess) sess.destroy(); 434 if (sessionManager() && sessionManager().loadFromSessionId(state.serverHello().sessionId(), sess)) 435 { 436 state.setOriginalHandshakeHash(sess.originalHandshakeHash().clone()); 437 state.channelID(new ChannelID(state.handshakeIo(), 438 state.hash(), 439 m_creds, 440 m_info.hostname, 441 state.hash().flushInto(state.Version(), state.ciphersuite().prfAlgo()), 442 state.originalHandshakeHash().clone 443 )); 444 } 445 } 446 // todo: Else if the session was supposed to use it, fail? 447 state.clientFinished(new Finished(state.handshakeIo(), state, CLIENT)); 448 } 449 450 Vector!ubyte session_id = state.serverHello().sessionId().clone; 451 452 Vector!ubyte session_ticket = state.sessionTicket(); 453 454 if (session_id.empty && !session_ticket.empty) 455 session_id = makeHelloRandom(rng(), m_policy); 456 457 auto session_info = new TLSSession(session_id.clone, 458 state.sessionKeys().masterSecret().clone, 459 state.originalHandshakeHash().clone(), 460 state.serverHello().Version(), 461 state.serverHello().ciphersuite(), 462 state.serverHello().compressionMethod(), 463 CLIENT, 464 state.serverHello().fragmentSize(), 465 state.serverHello().supportsExtendedMasterSecret(), 466 getPeerCertChain(state), 467 session_ticket.move(), 468 m_info, 469 ""); 470 scope(exit) destroy(session_info); 471 const bool should_save = saveSession(session_info); 472 473 if (!session_id.empty) 474 { 475 if (should_save) 476 sessionManager().save(session_info); 477 else { 478 auto entry = &session_info.sessionId(); 479 sessionManager().removeEntry(*entry); 480 } 481 } 482 483 activateSession(); 484 } 485 else 486 throw new TLSUnexpectedMessage("Unknown handshake message received"); 487 } 488 489 override HandshakeState newHandshakeState(HandshakeIO io) 490 { 491 return new ClientHandshakeState(io); 492 } 493 494 private: 495 const TLSPolicy m_policy; 496 TLSCredentialsManager m_creds; 497 const TLSServerInformation m_info; 498 } 499 500 501 private final class ClientHandshakeState : HandshakeState 502 { 503 public: 504 505 this(HandshakeIO io, void delegate(in HandshakeMessage) msg_callback = null) 506 { 507 super(io, msg_callback); 508 } 509 510 const(PublicKey) getServerPublicKey() const 511 { 512 assert(server_public_key, "TLSServer sent us a certificate"); 513 return *server_public_key; 514 } 515 516 // Used during session resumption 517 SecureVector!ubyte resume_master_secret; 518 Unique!PublicKey server_public_key; 519 }