/ lib / sslSession.c
sslSession.c
  1  /*
  2   * Copyright (c) 2000-2001,2005-2008,2010-2012 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   *
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   *
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   *
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  
 25  #include "tls_handshake_priv.h"
 26  #include "sslSession.h"
 27  #include "sslMemory.h"
 28  #include "sslUtils.h"
 29  #include "sslDebug.h"
 30  #include "sslCipherSpecs.h"
 31  #include "sslAlertMessage.h"
 32  #include "sslHandshake_priv.h"
 33  
 34  #include <assert.h>
 35  #include <string.h>
 36  #include <stddef.h>
 37  
 38  typedef struct
 39  {
 40      size_t                sessionIDLen;
 41      uint8_t               sessionID[32];
 42      tls_protocol_version  negProtocolVersion;    /* negotiated version */
 43      tls_protocol_version  reqProtocolVersion;    /* version requested by client */
 44      uint16_t              cipherSuite;
 45  	uint16_t			  padding;          /* so remainder is word aligned */
 46      uint8_t               masterSecret[48];
 47      size_t                ticketLen;
 48      size_t                ocspResponseLen;
 49      size_t                sctListLen;
 50      size_t                certListLen;
 51      bool                  sessExtMSSet;
 52      uint8_t               data[0];         /* Actually, variable length */
 53      /* data contain the session ticket (if any), followed by stappled ocsp response (if any),
 54         sct list (if any), and finally certs list */
 55  } ResumableSession;
 56  
 57  /* Special fixed sessionID when using a session ticket */
 58  #define SESSION_TICKET_ID "SESSION-TICKET"
 59  
 60  /*
 61   * Given a sessionData blob, perform sanity check
 62   */
 63  static bool
 64  SSLSessionDataCheck(const tls_buffer sessionData)
 65  {
 66      ResumableSession *session;
 67      if(sessionData.length < sizeof(ResumableSession))
 68          return false;
 69  
 70      session = (ResumableSession *)sessionData.data;
 71  
 72      return (sessionData.length == (sizeof(ResumableSession) + session->ticketLen + session->ocspResponseLen + session->sctListLen + session->certListLen));
 73  }
 74  /*
 75   * Cook up a (private) resumable session blob, based on the
 76   * specified ctx, store it with ctx->peerID (client) or ctx->sessionID (server) as the key.
 77   */
 78  int
 79  SSLAddSessionData(const tls_handshake_t ctx)
 80  {   int                 err;
 81  	size_t              sessionDataLen;
 82  	tls_buffer          sessionData;
 83  	ResumableSession    *session;
 84      size_t              sctListLen;
 85      size_t              certListLen;
 86      uint8_t             *p;
 87  
 88  	/* If we don't have session ID or a ticket, we can't store a session */
 89  	if (!ctx->sessionID.data && !ctx->sessionTicket.data)
 90  		return errSSLSessionNotFound;
 91  
 92      sctListLen = SSLEncodedBufferListSize(ctx->sct_list, 2);
 93      certListLen = SSLEncodedBufferListSize((tls_buffer_list_t *)ctx->peerCert, 3);
 94  
 95      sessionDataLen = sizeof(ResumableSession) + ctx->sessionTicket.length
 96                      + ctx->ocsp_response.length + sctListLen + certListLen;
 97  
 98      if ((err = SSLAllocBuffer(&sessionData, sessionDataLen)))
 99          return err;
100      
101      session = (ResumableSession*)sessionData.data;
102  
103      if(ctx->sessionID.data==NULL) {
104          session->sessionIDLen = strlen(SESSION_TICKET_ID);
105          memcpy(session->sessionID, SESSION_TICKET_ID, session->sessionIDLen);
106      } else {
107          session->sessionIDLen = ctx->sessionID.length;
108          memcpy(session->sessionID, ctx->sessionID.data, session->sessionIDLen);
109      }
110      session->negProtocolVersion = ctx->negProtocolVersion;
111      session->reqProtocolVersion = ctx->clientReqProtocol;
112      session->cipherSuite = ctx->selectedCipher;
113      memcpy(session->masterSecret, ctx->masterSecret, 48);
114      session->ticketLen = ctx->sessionTicket.length;
115      session->ocspResponseLen = ctx->ocsp_response.length;
116      session->sctListLen = sctListLen;
117      session->certListLen = certListLen;
118      session->padding = 0;
119      p = session->data;
120  
121      memcpy(p, ctx->sessionTicket.data, ctx->sessionTicket.length); p += ctx->sessionTicket.length;
122      memcpy(p, ctx->ocsp_response.data, ctx->ocsp_response.length); p += ctx->ocsp_response.length;
123      p = SSLEncodeBufferList(ctx->sct_list, 2, p);
124      p = SSLEncodeBufferList((tls_buffer_list_t *)ctx->peerCert, 3, p);
125  
126      if(!SSLSessionDataCheck(sessionData)) {
127          SSLFreeBuffer(&sessionData);
128          return errSSLInternal;
129      }
130  
131      if (ctx->extMSEnabled && ctx->extMSReceived)
132          session->sessExtMSSet = true;
133      else
134          session->sessExtMSSet = false;
135  
136      if(ctx->isServer)
137          err = ctx->callbacks->save_session_data(ctx->callback_ctx, ctx->sessionID, sessionData);
138      else
139          err = ctx->callbacks->save_session_data(ctx->callback_ctx, ctx->peerID, sessionData);
140  
141  
142      SSLFreeBuffer(&sessionData);
143  
144  	return err;
145  }
146  
147  int
148  SSLDeleteSessionData(const tls_handshake_t ctx)
149  {   int      err;
150  
151      if (ctx->sessionID.data == 0)
152          return errSSLSessionNotFound;
153  
154      err = ctx->callbacks->delete_session_data(ctx->callback_ctx, ctx->sessionID);
155      return err;
156  }
157  
158  /*
159   * Given a sessionData blob, obtain the associated sessionTicket (if Any).
160   */
161  int
162  SSLRetrieveSessionTicket(
163                       const tls_buffer sessionData,
164                       tls_buffer *ticket)
165  {
166      ResumableSession    *session;
167  
168      if(!SSLSessionDataCheck(sessionData))
169          return errSSLInternal;
170  
171      session = (ResumableSession*) sessionData.data;
172      ticket->data = session->data;
173      ticket->length = session->ticketLen;
174      return errSSLSuccess;
175  }
176  
177  /*
178   * Given a sessionData blob, obtain the associated sessionID (NOT the key...).
179   */
180  int
181  SSLRetrieveSessionID(
182  		const tls_buffer sessionData,
183  		tls_buffer *identifier)
184  {
185      ResumableSession    *session;
186  
187      if(!SSLSessionDataCheck(sessionData))
188          return errSSLInternal;
189  
190      session = (ResumableSession*) sessionData.data;
191      identifier->data = session->sessionID;
192      identifier->length = session->sessionIDLen;
193      return errSSLSuccess;
194  }
195  
196  /*
197   * Validate session data on the server side, after receiving ClientHello.
198   * If this fails, the server will attempt a full handshake.
199   */
200  int SSLServerValidateSessionData(const tls_buffer sessionData, tls_handshake_t ctx)
201  {
202      ResumableSession *session = (ResumableSession *)sessionData.data;
203  
204      if(!SSLSessionDataCheck(sessionData))
205          return errSSLInternal;
206  
207      /*
208         Currently, the session cache is looked up by sessionID on the server side,
209         so these two checks are mostly redundant and act as sanity check, in case
210         the session cache callbacks are badly implemented.
211         It would also be possible to lookup by more than sessionID, eg: you could
212         lookup by sessionID + protocol version, to handle session resumption
213         with in various version fallback cases.
214       */
215  
216      require(session->sessionIDLen == ctx->proposedSessionID.length, out);
217      require(memcmp(session->sessionID, ctx->proposedSessionID.data, ctx->proposedSessionID.length) == 0, out);
218  
219      /*
220          If for some reason a session was cached with a different protocol version,
221          then the server will fallback to a full handshake. We could accept to resume the session with
222          the cached version, but we prefer to negotiate the best possible version instead.
223       */
224      require(session->negProtocolVersion == ctx->negProtocolVersion, out);
225  
226      /*
227          We also check that the session cipherSuite is in the list of enabled ciphersuites,
228          and also in the list of ciphersuites requested by the client.
229       */
230  
231      require(cipherSuiteInSet(session->cipherSuite, ctx->enabledCipherSuites, ctx->numEnabledCipherSuites), out);
232      require(cipherSuiteInSet(session->cipherSuite, ctx->requestedCipherSuites, ctx->numRequestedCipherSuites), out);
233  
234      if (session->sessExtMSSet) {
235          if (!ctx->extMSReceived) {
236              SSLFatalSessionAlert(SSL_AlertHandshakeFail, ctx);
237              return errSSLFatalAlert;
238          }
239      } else {
240          if (ctx->extMSReceived) {
241              goto out;
242          }
243      }
244      ctx->selectedCipher = session->cipherSuite;
245      InitCipherSpecParams(ctx);
246  
247      return 0;
248  
249  out:
250      return errSSLSessionNotFound;
251  }
252  
253  /*
254   * Validate session data on the client side, before sending the ClientHello.
255   * If this fails, the client will no attempt resumption.
256   */
257  int SSLClientValidateSessionDataBefore(const tls_buffer sessionData, tls_handshake_t ctx)
258  {
259      ResumableSession *session = (ResumableSession *)sessionData.data;
260  
261      if(!SSLSessionDataCheck(sessionData))
262          return errSSLInternal;
263  
264      /*
265          If the current requested version is higher than the session one,
266          we do not re-use this session (see: rdar://23329369)
267       */
268      require(ctx->maxProtocolVersion <= session->reqProtocolVersion, out);
269  
270      /*
271          Make sure that the session version is within our enabled versions.
272       */
273      require(session->negProtocolVersion <= ctx->maxProtocolVersion, out);
274      require(session->negProtocolVersion >= ctx->minProtocolVersion, out);
275  
276      /*
277          Make sure that the session ciphersuite is within our enabled ciphers
278       */
279      require(cipherSuiteInSet(session->cipherSuite, ctx->enabledCipherSuites, ctx->numEnabledCipherSuites), out);
280  
281      return 0;
282  out:
283      return errSSLSessionNotFound;
284  }
285  
286  /*
287   * Validate session data on the client side, after receiving the ServerHello.
288   * If this fails, the client will abort the connection.
289   */
290  int SSLClientValidateSessionDataAfter(const tls_buffer sessionData, tls_handshake_t ctx)
291  {
292      ResumableSession *session = (ResumableSession *)sessionData.data;
293  
294      if(!SSLSessionDataCheck(sessionData))
295          return errSSLInternal;
296  
297      /* Make sure that the session version and server version match. */
298      require(session->negProtocolVersion == ctx->negProtocolVersion, out);
299      /* Make sure that the session ciphersuite and server ciphersuite match. */
300      require(session->cipherSuite == ctx->selectedCipher, out);
301      require(session->sessExtMSSet == ctx->extMSReceived, out);
302      return 0;
303  
304  out:
305      return errSSLProtocol;
306  }
307  
308  
309  /*
310   * Retrieve session state from specified sessionData blob, install into
311   * ctx. Presumably, ctx->sessionID and
312   * ctx->negProtocolVersion are already init'd (from the above two functions).
313   */
314  int
315  SSLInstallSessionFromData(const tls_buffer sessionData, tls_handshake_t ctx)
316  {   int            err;
317      ResumableSession    *session;
318      uint8_t             *p;
319  
320      if(!SSLSessionDataCheck(sessionData))
321          return errSSLInternal;
322  
323      session = (ResumableSession*)sessionData.data;
324  
325      /*
326       * selectedCipher and negProtocolVersion should already be validated.
327       */
328      assert(ctx->selectedCipher == session->cipherSuite);
329      assert(ctx->negProtocolVersion == session->negProtocolVersion);
330  
331      memcpy(ctx->masterSecret, session->masterSecret, 48);
332      p = session->data + session->ticketLen;
333  
334      // Get the stappled ocsp_response (if any)
335      SSLFreeBuffer(&ctx->ocsp_response);
336      ctx->ocsp_response_received = false;
337      if(session->ocspResponseLen) {
338          ctx->ocsp_response_received =  true;
339          SSLCopyBufferFromData(p, session->ocspResponseLen, &ctx->ocsp_response);
340      }
341      p += session->ocspResponseLen;
342  
343      // Get the SCT list (if any)
344      tls_free_buffer_list(ctx->sct_list);
345      ctx->sct_list = NULL;
346      if(session->sctListLen) {
347          if((err=SSLDecodeBufferList(p, session->sctListLen, 2, &ctx->sct_list))) {
348              return err;
349          }
350      }
351      p += session->sctListLen;
352  
353      // Get the certs
354      SSLFreeCertificates(ctx->peerCert);
355      ctx->peerCert = NULL;
356      if(session->certListLen) {
357          if((err=SSLDecodeBufferList(p, session->certListLen, 3, (tls_buffer_list_t **)&ctx->peerCert))) {
358              return err;
359          }
360      }
361  
362      return errSSLSuccess;
363  }