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 }