1 /****************************************************************************
2 * *
3 * cryptlib Session Attribute Routines *
4 * Copyright Peter Gutmann 1998-2011 *
5 * *
6 ****************************************************************************/
7
8 #include <stdio.h>
9 #include "crypt.h"
10 #ifdef INC_ALL
11 #include "session.h"
12 #else
13 #include "session/session.h"
14 #endif /* Compiler-specific includes */
15
16 #ifdef USE_SESSIONS
17
18 /****************************************************************************
19 * *
20 * Utility Functions *
21 * *
22 ****************************************************************************/
23
24 /* Exit after setting extended error information */
25
26 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
exitError(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus,IN_ENUM (CRYPT_ERRTYPE)const CRYPT_ERRTYPE_TYPE errorType,IN_ERROR const int status)27 static int exitError( INOUT SESSION_INFO *sessionInfoPtr,
28 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus,
29 IN_ENUM( CRYPT_ERRTYPE ) const CRYPT_ERRTYPE_TYPE errorType,
30 IN_ERROR const int status )
31 {
32 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
33
34 REQUIRES( isAttribute( errorLocus ) || \
35 isInternalAttribute( errorLocus ) );
36 REQUIRES( errorType > CRYPT_ERRTYPE_NONE && \
37 errorType < CRYPT_ERRTYPE_LAST );
38 REQUIRES( cryptStatusError( status ) );
39
40 setErrorInfo( sessionInfoPtr, errorLocus, errorType );
41 return( status );
42 }
43
44 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
exitErrorInited(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus)45 static int exitErrorInited( INOUT SESSION_INFO *sessionInfoPtr,
46 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
47 {
48 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
49
50 REQUIRES( isAttribute( errorLocus ) || \
51 isInternalAttribute( errorLocus ) );
52
53 return( exitError( sessionInfoPtr, errorLocus, CRYPT_ERRTYPE_ATTR_PRESENT,
54 CRYPT_ERROR_INITED ) );
55 }
56
57 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
exitErrorNotInited(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus)58 static int exitErrorNotInited( INOUT SESSION_INFO *sessionInfoPtr,
59 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
60 {
61 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
62
63 REQUIRES( isAttribute( errorLocus ) || \
64 isInternalAttribute( errorLocus ) );
65
66 return( exitError( sessionInfoPtr, errorLocus, CRYPT_ERRTYPE_ATTR_ABSENT,
67 CRYPT_ERROR_NOTINITED ) );
68 }
69
70 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
exitErrorNotFound(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus)71 static int exitErrorNotFound( INOUT SESSION_INFO *sessionInfoPtr,
72 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
73 {
74 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
75
76 REQUIRES( isAttribute( errorLocus ) || \
77 isInternalAttribute( errorLocus ) );
78
79 return( exitError( sessionInfoPtr, errorLocus, CRYPT_ERRTYPE_ATTR_ABSENT,
80 CRYPT_ERROR_NOTFOUND ) );
81 }
82
83 /* Add the contents of an encoded URL to a session. This requires parsing
84 the individual session attribute components out of the URL and then
85 adding each one in turn */
86
87 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
addUrl(INOUT SESSION_INFO * sessionInfoPtr,IN_BUFFER (urlLength)const void * url,IN_LENGTH_DNS const int urlLength)88 static int addUrl( INOUT SESSION_INFO *sessionInfoPtr,
89 IN_BUFFER( urlLength ) const void *url,
90 IN_LENGTH_DNS const int urlLength )
91 {
92 const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
93 URL_INFO urlInfo;
94 int status;
95
96 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
97 assert( isReadPtr( url, urlLength ) );
98
99 REQUIRES( urlLength > 0 && urlLength < MAX_URL_SIZE );
100
101 /* If there's already a transport session or network socket specified
102 then we can't set a server name as well */
103 if( sessionInfoPtr->transportSession != CRYPT_ERROR )
104 return( exitErrorInited( sessionInfoPtr, CRYPT_SESSINFO_SESSION ) );
105 if( sessionInfoPtr->networkSocket != CRYPT_ERROR )
106 return( exitErrorInited( sessionInfoPtr,
107 CRYPT_SESSINFO_NETWORKSOCKET ) );
108
109 /* Parse the server name. The PKI protocols all use HTTP as their
110 substrate so if it's not SSH or SSL/TLS we require HTTP */
111 status = sNetParseURL( &urlInfo, url, urlLength,
112 ( sessionInfoPtr->type == CRYPT_SESSION_SSH ) ? \
113 URL_TYPE_SSH : \
114 ( sessionInfoPtr->type == CRYPT_SESSION_SSL ) ? \
115 URL_TYPE_HTTPS : URL_TYPE_HTTP );
116 if( cryptStatusError( status ) )
117 return( exitError( sessionInfoPtr, CRYPT_SESSINFO_SERVER_NAME,
118 CRYPT_ERRTYPE_ATTR_VALUE, CRYPT_ARGERROR_STR1 ) );
119
120 /* We can only use autodetection with PKI services */
121 if( urlInfo.hostLen == 12 && \
122 !strCompare( urlInfo.host, "[Autodetect]", urlInfo.hostLen ) && \
123 !protocolInfoPtr->isReqResp )
124 return( exitError( sessionInfoPtr, CRYPT_SESSINFO_SERVER_NAME,
125 CRYPT_ERRTYPE_ATTR_VALUE, CRYPT_ARGERROR_STR1 ) );
126
127 /* Remember the server name */
128 if( urlInfo.hostLen + urlInfo.locationLen >= MAX_URL_SIZE )
129 {
130 /* This should never happen since the overall URL size has to be
131 less than MAX_URL_SIZE */
132 assert( INTERNAL_ERROR );
133 return( exitError( sessionInfoPtr, CRYPT_SESSINFO_SERVER_NAME,
134 CRYPT_ERRTYPE_ATTR_VALUE, CRYPT_ARGERROR_STR1 ) );
135 }
136 if( ( sessionInfoPtr->protocolInfo->flags & SESSION_ISHTTPTRANSPORT ) && \
137 urlInfo.locationLen > 0 )
138 {
139 char urlBuffer[ MAX_URL_SIZE + 8 ];
140
141 /* We only remember the location if the session uses HTTP transport.
142 This is to deal with situations where the caller specifies a URL
143 like https://www.server.com/index.html for an SSL session, which
144 should be treated as valid even though it's not really a pure
145 FQDN */
146 ENSURES( rangeCheck( urlInfo.hostLen, urlInfo.locationLen,
147 MAX_URL_SIZE ) );
148 memcpy( urlBuffer, urlInfo.host, urlInfo.hostLen );
149 memcpy( urlBuffer + urlInfo.hostLen, urlInfo.location,
150 urlInfo.locationLen );
151 status = addSessionInfoS( &sessionInfoPtr->attributeList,
152 CRYPT_SESSINFO_SERVER_NAME, urlBuffer,
153 urlInfo.hostLen + urlInfo.locationLen );
154 }
155 else
156 {
157 status = addSessionInfoS( &sessionInfoPtr->attributeList,
158 CRYPT_SESSINFO_SERVER_NAME,
159 urlInfo.host, urlInfo.hostLen );
160 }
161 if( cryptStatusError( status ) )
162 return( exitError( sessionInfoPtr, CRYPT_SESSINFO_SERVER_NAME,
163 CRYPT_ERRTYPE_ATTR_VALUE, CRYPT_ARGERROR_STR1 ) );
164
165 /* If there's a port or user name specified in the URL, remember them.
166 We have to add the user name after we add any other attributes
167 because it's paired with a password, so adding the user name and then
168 following it with something that isn't a password will cause an error
169 return */
170 if( urlInfo.port > 0 )
171 {
172 ( void ) krnlSendMessage( sessionInfoPtr->objectHandle,
173 IMESSAGE_DELETEATTRIBUTE, NULL,
174 CRYPT_SESSINFO_SERVER_PORT );
175 status = krnlSendMessage( sessionInfoPtr->objectHandle,
176 IMESSAGE_SETATTRIBUTE, &urlInfo.port,
177 CRYPT_SESSINFO_SERVER_PORT );
178 }
179 if( cryptStatusOK( status ) && urlInfo.userInfoLen > 0 )
180 {
181 MESSAGE_DATA userInfoMsgData;
182
183 ( void ) krnlSendMessage( sessionInfoPtr->objectHandle,
184 IMESSAGE_DELETEATTRIBUTE, NULL,
185 CRYPT_SESSINFO_USERNAME );
186 setMessageData( &userInfoMsgData, ( MESSAGE_CAST ) urlInfo.userInfo,
187 urlInfo.userInfoLen );
188 status = krnlSendMessage( sessionInfoPtr->objectHandle,
189 IMESSAGE_SETATTRIBUTE_S, &userInfoMsgData,
190 CRYPT_SESSINFO_USERNAME );
191 }
192 if( cryptStatusError( status ) )
193 return( exitError( sessionInfoPtr, CRYPT_SESSINFO_SERVER_NAME,
194 CRYPT_ERRTYPE_ATTR_VALUE, CRYPT_ARGERROR_STR1 ) );
195
196 /* Remember the transport type */
197 if( sessionInfoPtr->protocolInfo->flags & SESSION_ISHTTPTRANSPORT )
198 sessionInfoPtr->flags |= SESSION_ISHTTPTRANSPORT;
199
200 return( CRYPT_OK );
201 }
202
203 /****************************************************************************
204 * *
205 * Get Attributes *
206 * *
207 ****************************************************************************/
208
209 /* Get a numeric/boolean attribute */
210
211 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
getSessionAttribute(INOUT SESSION_INFO * sessionInfoPtr,OUT_INT_Z int * valuePtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute)212 int getSessionAttribute( INOUT SESSION_INFO *sessionInfoPtr,
213 OUT_INT_Z int *valuePtr,
214 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
215 {
216 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
217 assert( isWritePtr( valuePtr, sizeof( int ) ) );
218
219 REQUIRES( isAttribute( attribute ) || \
220 isInternalAttribute( attribute ) );
221
222 /* Clear return value */
223 *valuePtr = 0;
224
225 /* Handle the various information types */
226 switch( attribute )
227 {
228 case CRYPT_ATTRIBUTE_CURRENT:
229 case CRYPT_ATTRIBUTE_CURRENT_GROUP:
230 {
231 CRYPT_ATTRIBUTE_TYPE attributeID;
232 int status;
233
234 status = getSessionAttributeCursor( sessionInfoPtr->attributeList,
235 sessionInfoPtr->attributeListCurrent,
236 attribute, &attributeID );
237 if( status == OK_SPECIAL )
238 {
239 /* The attribute list wasn't initialised yet, initialise it
240 now */
241 sessionInfoPtr->attributeListCurrent = \
242 sessionInfoPtr->attributeList;
243 }
244 else
245 {
246 if( cryptStatusError( status ) )
247 return( exitError( sessionInfoPtr, attribute,
248 CRYPT_ERRTYPE_ATTR_ABSENT, status ) );
249 }
250 *valuePtr = attributeID;
251
252 return( CRYPT_OK );
253 }
254
255 case CRYPT_OPTION_NET_CONNECTTIMEOUT:
256 if( sessionInfoPtr->connectTimeout == CRYPT_ERROR )
257 return( exitErrorNotInited( sessionInfoPtr,
258 CRYPT_OPTION_NET_CONNECTTIMEOUT ) );
259 *valuePtr = sessionInfoPtr->connectTimeout;
260 return( CRYPT_OK );
261
262 case CRYPT_OPTION_NET_READTIMEOUT:
263 if( sessionInfoPtr->readTimeout == CRYPT_ERROR )
264 return( exitErrorNotInited( sessionInfoPtr,
265 CRYPT_OPTION_NET_READTIMEOUT ) );
266 *valuePtr = sessionInfoPtr->readTimeout;
267 return( CRYPT_OK );
268
269 case CRYPT_OPTION_NET_WRITETIMEOUT:
270 if( sessionInfoPtr->writeTimeout == CRYPT_ERROR )
271 return( exitErrorNotInited( sessionInfoPtr,
272 CRYPT_OPTION_NET_WRITETIMEOUT ) );
273 *valuePtr = sessionInfoPtr->writeTimeout;
274 return( CRYPT_OK );
275
276 case CRYPT_ATTRIBUTE_ERRORTYPE:
277 *valuePtr = sessionInfoPtr->errorType;
278 return( CRYPT_OK );
279
280 case CRYPT_ATTRIBUTE_ERRORLOCUS:
281 *valuePtr = sessionInfoPtr->errorLocus;
282 return( CRYPT_OK );
283
284 case CRYPT_ATTRIBUTE_BUFFERSIZE:
285 *valuePtr = sessionInfoPtr->receiveBufSize;
286 return( CRYPT_OK );
287
288 case CRYPT_SESSINFO_ACTIVE:
289 /* Only secure transport sessions can be persistently active,
290 request/response sessions are only active while the
291 transaction is in progress. Note that this differs from the
292 connection-active state below, which records the fact that
293 there's a network-level connection established but not whether
294 there's any messages or a secure session active across it.
295 See the comment in setSessionAttribute() for more on this */
296 *valuePtr = sessionInfoPtr->iCryptInContext != CRYPT_ERROR && \
297 ( sessionInfoPtr->flags & SESSION_ISOPEN ) ? \
298 TRUE : FALSE;
299 return( CRYPT_OK );
300
301 case CRYPT_SESSINFO_CONNECTIONACTIVE:
302 *valuePtr = ( sessionInfoPtr->flags & SESSION_ISOPEN ) ? \
303 TRUE : FALSE;
304 return( CRYPT_OK );
305
306 case CRYPT_SESSINFO_SERVER_PORT:
307 case CRYPT_SESSINFO_CLIENT_PORT:
308 {
309 const ATTRIBUTE_LIST *attributeListPtr = \
310 findSessionInfo( sessionInfoPtr->attributeList,
311 attribute );
312 if( attributeListPtr == NULL )
313 return( exitErrorNotInited( sessionInfoPtr, attribute ) );
314 *valuePtr = attributeListPtr->intValue;
315 return( CRYPT_OK );
316 }
317
318 case CRYPT_SESSINFO_VERSION:
319 *valuePtr = sessionInfoPtr->version;
320 return( CRYPT_OK );
321
322 case CRYPT_SESSINFO_AUTHRESPONSE:
323 if( sessionInfoPtr->authResponse == AUTHRESPONSE_NONE )
324 return( exitErrorNotFound( sessionInfoPtr,
325 CRYPT_SESSINFO_AUTHRESPONSE ) );
326 *valuePtr = \
327 ( sessionInfoPtr->authResponse == AUTHRESPONSE_SUCCESS ) ? \
328 TRUE : FALSE;
329 return( CRYPT_OK );
330 }
331
332 retIntError();
333 }
334
335 /* Get a string attribute */
336
337 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
getSessionAttributeS(INOUT SESSION_INFO * sessionInfoPtr,INOUT MESSAGE_DATA * msgData,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute)338 int getSessionAttributeS( INOUT SESSION_INFO *sessionInfoPtr,
339 INOUT MESSAGE_DATA *msgData,
340 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
341 {
342 const ATTRIBUTE_LIST *attributeListPtr;
343
344 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
345 assert( isWritePtr( msgData, sizeof( MESSAGE_DATA ) ) );
346
347 /* Handle the various information types */
348 switch( attribute )
349 {
350 case CRYPT_OPTION_NET_SOCKS_SERVER:
351 case CRYPT_OPTION_NET_SOCKS_USERNAME:
352 case CRYPT_OPTION_NET_HTTP_PROXY:
353 /* These aren't implemented on a per-session level yet since
354 they're almost never user */
355 return( exitErrorNotFound( sessionInfoPtr, attribute ) );
356
357 case CRYPT_ATTRIBUTE_ERRORMESSAGE:
358 {
359 #ifdef USE_ERRMSGS
360 ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
361
362 if( errorInfo->errorStringLength > 0 )
363 {
364 return( attributeCopy( msgData, errorInfo->errorString,
365 errorInfo->errorStringLength ) );
366 }
367 #endif /* USE_ERRMSGS */
368
369 /* We don't set extended error information for this atribute
370 because it's usually read in response to an existing error,
371 which would overwrite the existing error information */
372 return( CRYPT_ERROR_NOTFOUND );
373 }
374
375 case CRYPT_SESSINFO_USERNAME:
376 case CRYPT_SESSINFO_PASSWORD:
377 case CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1:
378 case CRYPT_SESSINFO_SERVER_NAME:
379 case CRYPT_SESSINFO_CLIENT_NAME:
380 attributeListPtr = findSessionInfo( sessionInfoPtr->attributeList,
381 attribute );
382 if( attributeListPtr == NULL )
383 return( exitErrorNotInited( sessionInfoPtr, attribute ) );
384 return( attributeCopy( msgData, attributeListPtr->value,
385 attributeListPtr->valueLength ) );
386 }
387
388 retIntError();
389 }
390
391 /****************************************************************************
392 * *
393 * Set Attributes *
394 * *
395 ****************************************************************************/
396
397 /* Set a numeric/boolean attribute */
398
399 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
setSessionAttribute(INOUT SESSION_INFO * sessionInfoPtr,IN const int value,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute)400 int setSessionAttribute( INOUT SESSION_INFO *sessionInfoPtr,
401 IN const int value,
402 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
403 {
404 int status;
405
406 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
407
408 REQUIRES( ( attribute == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
409 attribute == CRYPT_ATTRIBUTE_CURRENT ) ||
410 /* CURRENT = cursor positioning code */
411 ( attribute == CRYPT_SESSINFO_NETWORKSOCKET ) ||
412 /* Socket = non-typed value */
413 ( value >= 0 && value < MAX_INTLENGTH ) );
414 REQUIRES( isAttribute( attribute ) || \
415 isInternalAttribute( attribute ) );
416
417 /* Handle the various information types */
418 switch( attribute )
419 {
420 case CRYPT_ATTRIBUTE_CURRENT:
421 case CRYPT_ATTRIBUTE_CURRENT_GROUP:
422 {
423 ATTRIBUTE_LIST *attributeListPtr = \
424 sessionInfoPtr->attributeListCurrent;
425
426 REQUIRES( value >= CRYPT_CURSOR_LAST && \
427 value <= CRYPT_CURSOR_FIRST ); /* Values are -ve */
428 status = setSessionAttributeCursor( sessionInfoPtr->attributeList,
429 &attributeListPtr, attribute, value );
430 if( cryptStatusError( status ) )
431 return( exitError( sessionInfoPtr, attribute,
432 CRYPT_ERRTYPE_ATTR_ABSENT, status ) );
433 sessionInfoPtr->attributeListCurrent = attributeListPtr;
434 return( CRYPT_OK );
435 }
436
437 case CRYPT_OPTION_NET_CONNECTTIMEOUT:
438 sessionInfoPtr->connectTimeout = value;
439 return( CRYPT_OK );
440
441 case CRYPT_OPTION_NET_READTIMEOUT:
442 sessionInfoPtr->readTimeout = value;
443 return( CRYPT_OK );
444
445 case CRYPT_OPTION_NET_WRITETIMEOUT:
446 sessionInfoPtr->writeTimeout = value;
447 return( CRYPT_OK );
448
449 case CRYPT_ATTRIBUTE_BUFFERSIZE:
450 REQUIRES( !( sessionInfoPtr->flags & SESSION_ISOPEN ) );
451
452 sessionInfoPtr->receiveBufSize = value;
453 return( CRYPT_OK );
454
455 case CRYPT_SESSINFO_ACTIVE:
456 {
457 CRYPT_ATTRIBUTE_TYPE missingInfo;
458
459 /* Session state and persistent sessions are handled as follows:
460 The CRYPT_SESSINFO_ACTIVE attribute records the active state
461 of the session as a whole, and the CRYPT_SESSINFO_-
462 CONNECTIONACTIVE attribute records the state of the
463 underlying comms session. Setting CRYPT_SESSINFO_ACTIVE for
464 the first time activates the comms session and leaves it
465 active if the underlying mechanism (e.g. HTTP 1.1 persistent
466 connections) supports it. The CRYPT_SESSINFO_ACTIVE
467 attribute is reset once the transaction completes, and
468 further transactions can be initiated as long as
469 CRYPT_SESSINFO_CONNECTIONACTIVE is set:
470
471 Obj.state _active _connactive
472 --------- ------- -----------
473 create 0 0 0
474 setattr 0 0 0
475 (clear out_param)
476 activate 1 0 -> 1 -> 0 1
477 (clear in_param)
478 setattr 1 0 1
479 (clear out_param)
480 activate 1 0 -> 1 -> 0 1
481 (clear in_param)
482 (peer closes conn) 1 0 0
483 setattr CRYPT_ERROR_COMPLETE */
484 if( value == FALSE )
485 return( CRYPT_OK ); /* No-op */
486
487 /* If the session is in the partially-open state while we wait
488 for the caller to allow or disallow the session authentication
489 they have to provide a clear yes or no indication by setting
490 CRYPT_SESSINFO_AUTHRESPONSE to TRUE or FALSE before they can
491 try to continue the session activation */
492 if( ( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) && \
493 sessionInfoPtr->authResponse == AUTHRESPONSE_NONE )
494 return( exitErrorNotInited( sessionInfoPtr,
495 CRYPT_SESSINFO_AUTHRESPONSE ) );
496
497 /* Make sure that all of the information that we need to proceed
498 is present */
499 missingInfo = checkMissingInfo( sessionInfoPtr->attributeList,
500 isServer( sessionInfoPtr ) ? TRUE : FALSE );
501 if( missingInfo != CRYPT_ATTRIBUTE_NONE )
502 return( exitErrorNotInited( sessionInfoPtr, missingInfo ) );
503
504 status = activateSession( sessionInfoPtr );
505 if( cryptArgError( status ) )
506 {
507 /* Catch leaked low-level status values. The session
508 management code does a large amount of work involving
509 other cryptlib objects so it's possible that an
510 unexpected failure at some point will leak through an
511 inappropriate status value */
512 DEBUG_DIAG(( "Session activate returned argError status" ));
513 assert( DEBUG_WARN );
514 status = CRYPT_ERROR_FAILED;
515 }
516 return( status );
517 }
518
519 case CRYPT_SESSINFO_SERVER_PORT:
520 /* If there's already a transport session or network socket
521 specified then we can't set a port as well */
522 if( sessionInfoPtr->transportSession != CRYPT_ERROR )
523 return( exitErrorInited( sessionInfoPtr,
524 CRYPT_SESSINFO_SESSION ) );
525 if( sessionInfoPtr->networkSocket != CRYPT_ERROR )
526 return( exitErrorInited( sessionInfoPtr,
527 CRYPT_SESSINFO_NETWORKSOCKET ) );
528
529 return( addSessionInfo( &sessionInfoPtr->attributeList,
530 CRYPT_SESSINFO_SERVER_PORT, value ) );
531
532 case CRYPT_SESSINFO_VERSION:
533 if( value < sessionInfoPtr->protocolInfo->minVersion || \
534 value > sessionInfoPtr->protocolInfo->maxVersion )
535 return( CRYPT_ARGERROR_VALUE );
536 sessionInfoPtr->version = value;
537 return( CRYPT_OK );
538
539 case CRYPT_SESSINFO_PRIVATEKEY:
540 {
541 const int requiredAttributeFlags = isServer( sessionInfoPtr ) ? \
542 sessionInfoPtr->serverReqAttrFlags : \
543 sessionInfoPtr->clientReqAttrFlags;
544
545 /* Make sure that it's a private key */
546 status = krnlSendMessage( value, IMESSAGE_CHECK, NULL,
547 MESSAGE_CHECK_PKC_PRIVATE );
548 if( cryptStatusError( status ) )
549 {
550 if( sessionInfoPtr->type != CRYPT_SESSION_SSL )
551 return( CRYPT_ARGERROR_NUM1 );
552
553 /* SSL can also do key agreement-based key exchange so we
554 fall back to this if key transport-based exchange isn't
555 possible */
556 status = krnlSendMessage( value, IMESSAGE_CHECK, NULL,
557 MESSAGE_CHECK_PKC_KA_EXPORT );
558 if( cryptStatusError( status ) )
559 return( CRYPT_ARGERROR_NUM1 );
560 }
561
562 /* If we need a private key with certain capabilities, make sure
563 that it has these capabilities. This is a more specific check
564 than that allowed by the kernel ACLs */
565 if( requiredAttributeFlags & SESSION_NEEDS_PRIVKEYSIGN )
566 {
567 status = krnlSendMessage( value, IMESSAGE_CHECK, NULL,
568 MESSAGE_CHECK_PKC_SIGN );
569 if( cryptStatusError( status ) )
570 {
571 setErrorInfo( sessionInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
572 CRYPT_ERRTYPE_ATTR_VALUE );
573 return( CRYPT_ARGERROR_NUM1 );
574 }
575 }
576 if( requiredAttributeFlags & SESSION_NEEDS_PRIVKEYCRYPT )
577 {
578 status = krnlSendMessage( value, IMESSAGE_CHECK, NULL,
579 MESSAGE_CHECK_PKC_DECRYPT );
580 if( cryptStatusError( status ) )
581 {
582 setErrorInfo( sessionInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
583 CRYPT_ERRTYPE_ATTR_VALUE );
584 return( CRYPT_ARGERROR_NUM1 );
585 }
586 }
587
588 /* If we need a private key with a certificate, make sure that
589 the appropriate type of initialised certificate object is
590 present. This is a more specific check than that allowed by
591 the kernel ACLs */
592 if( requiredAttributeFlags & SESSION_NEEDS_PRIVKEYCERT )
593 {
594 int attrValue;
595
596 status = krnlSendMessage( value, IMESSAGE_GETATTRIBUTE,
597 &attrValue, CRYPT_CERTINFO_IMMUTABLE );
598 if( cryptStatusError( status ) || !attrValue )
599 return( CRYPT_ARGERROR_NUM1 );
600 status = krnlSendMessage( value, IMESSAGE_GETATTRIBUTE,
601 &attrValue, CRYPT_CERTINFO_CERTTYPE );
602 if( cryptStatusError( status ) || \
603 ( attrValue != CRYPT_CERTTYPE_CERTIFICATE && \
604 attrValue != CRYPT_CERTTYPE_CERTCHAIN ) )
605 return( CRYPT_ARGERROR_NUM1 );
606 }
607 if( requiredAttributeFlags & SESSION_NEEDS_PRIVKEYCACERT )
608 {
609 status = krnlSendMessage( value, IMESSAGE_CHECK, NULL,
610 MESSAGE_CHECK_CA );
611 if( cryptStatusError( status ) )
612 return( CRYPT_ARGERROR_NUM1 );
613 }
614
615 /* If we're using a certificate, make sure that it's currently
616 valid. This self-check avoids ugly silent failures where
617 everything appears to work just fine on the server side but
618 the client gets invalid data back */
619 if( requiredAttributeFlags & ( SESSION_NEEDS_PRIVKEYCERT | \
620 SESSION_NEEDS_PRIVKEYCACERT ) )
621 {
622 status = checkServerCertValid( value, SESSION_ERRINFO );
623 if( cryptStatusError( status ) )
624 return( CRYPT_ARGERROR_NUM1 );
625 }
626
627 /* Perform any protocol-specific additional checks if necessary */
628 if( FNPTR_ISSET( sessionInfoPtr->checkAttributeFunction ) )
629 {
630 const SES_CHECKATTRIBUTE_FUNCTION checkAttributeFunction = \
631 FNPTR_GET( sessionInfoPtr->checkAttributeFunction );
632
633 REQUIRES( checkAttributeFunction != NULL );
634
635 status = checkAttributeFunction( sessionInfoPtr, &value,
636 attribute );
637 if( status == OK_SPECIAL )
638 {
639 /* The value was dealt with as a side-effect of the check
640 function, there's nothing more to do */
641 return( CRYPT_OK );
642 }
643 if( cryptStatusError( status ) )
644 return( status );
645 }
646
647 /* Add the private key and increment its reference count */
648 krnlSendNotifier( value, IMESSAGE_INCREFCOUNT );
649 sessionInfoPtr->privateKey = value;
650
651 return( CRYPT_OK );
652 }
653
654 case CRYPT_SESSINFO_KEYSET:
655 {
656 int type;
657
658 /* Make sure that it's either a certificate store (rather than
659 just a generic keyset) if required, or specifically not a
660 certificate store if not required. This is to prevent a
661 session running with unnecessary privileges, we should only
662 be using a certificate store if it's actually required. The
663 checking is already performed by the kernel but we do it
664 again here just to be safe */
665 status = krnlSendMessage( value, IMESSAGE_GETATTRIBUTE, &type,
666 CRYPT_IATTRIBUTE_SUBTYPE );
667 if( cryptStatusError( status ) )
668 return( CRYPT_ARGERROR_NUM1 );
669 if( sessionInfoPtr->serverReqAttrFlags & SESSION_NEEDS_CERTSTORE )
670 {
671 if( type != SUBTYPE_KEYSET_DBMS_STORE )
672 return( CRYPT_ARGERROR_NUM1 );
673 }
674 else
675 {
676 if( type != SUBTYPE_KEYSET_DBMS )
677 return( CRYPT_ARGERROR_NUM1 );
678 }
679
680 /* Add the keyset and increment its reference count */
681 krnlSendNotifier( value, IMESSAGE_INCREFCOUNT );
682 sessionInfoPtr->cryptKeyset = value;
683
684 return( CRYPT_OK );
685 }
686
687 case CRYPT_SESSINFO_AUTHRESPONSE:
688 sessionInfoPtr->authResponse = value ? AUTHRESPONSE_SUCCESS : \
689 AUTHRESPONSE_FAILURE;
690 return( CRYPT_OK );
691
692 case CRYPT_SESSINFO_SESSION:
693 /* If there's already a host or network socket specified then we
694 can't set a transport session as well */
695 if( findSessionInfo( sessionInfoPtr->attributeList,
696 CRYPT_SESSINFO_SERVER_NAME ) != NULL )
697 return( exitErrorInited( sessionInfoPtr,
698 CRYPT_SESSINFO_SERVER_NAME ) );
699 if( sessionInfoPtr->networkSocket != CRYPT_ERROR )
700 return( exitErrorInited( sessionInfoPtr,
701 CRYPT_SESSINFO_NETWORKSOCKET ) );
702
703 /* Add the transport mechanism and increment its reference
704 count */
705 krnlSendNotifier( value, IMESSAGE_INCREFCOUNT );
706 sessionInfoPtr->transportSession = value;
707
708 return( CRYPT_OK );
709
710 case CRYPT_SESSINFO_NETWORKSOCKET:
711 {
712 NET_CONNECT_INFO connectInfo;
713 STREAM stream;
714
715 /* If there's already a host or session specified then we can't
716 set a network socket as well */
717 if( findSessionInfo( sessionInfoPtr->attributeList,
718 CRYPT_SESSINFO_SERVER_NAME ) != NULL )
719 return( exitErrorInited( sessionInfoPtr,
720 CRYPT_SESSINFO_SERVER_NAME ) );
721 if( sessionInfoPtr->transportSession != CRYPT_ERROR )
722 return( exitErrorInited( sessionInfoPtr,
723 CRYPT_SESSINFO_SESSION ) );
724
725 /* Create a dummy network stream to make sure that the network
726 socket is OK */
727 initNetConnectInfo( &connectInfo, sessionInfoPtr->ownerHandle,
728 sessionInfoPtr->readTimeout,
729 sessionInfoPtr->connectTimeout,
730 NET_OPTION_NETWORKSOCKET_DUMMY );
731 connectInfo.networkSocket = value;
732 status = sNetConnect( &stream, STREAM_PROTOCOL_TCP,
733 &connectInfo, &sessionInfoPtr->errorInfo );
734 if( cryptStatusError( status ) )
735 return( status );
736 sNetDisconnect( &stream );
737
738 /* Add the network socket */
739 sessionInfoPtr->networkSocket = value;
740
741 return( CRYPT_OK );
742 }
743 }
744
745 retIntError();
746 }
747
748 /* Set a string attribute */
749
750 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
setSessionAttributeS(INOUT SESSION_INFO * sessionInfoPtr,IN_BUFFER (dataLength)const void * data,IN_LENGTH const int dataLength,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute)751 int setSessionAttributeS( INOUT SESSION_INFO *sessionInfoPtr,
752 IN_BUFFER( dataLength ) const void *data,
753 IN_LENGTH const int dataLength,
754 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
755 {
756 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
757 assert( isReadPtr( data, dataLength ) );
758
759 REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH );
760 REQUIRES( isAttribute( attribute ) || \
761 isInternalAttribute( attribute ) );
762
763 /* Handle the various information types */
764 switch( attribute )
765 {
766 case CRYPT_OPTION_NET_SOCKS_SERVER:
767 case CRYPT_OPTION_NET_SOCKS_USERNAME:
768 case CRYPT_OPTION_NET_HTTP_PROXY:
769 /* These aren't implemented on a per-session level yet since
770 they're almost never used */
771 return( CRYPT_ARGERROR_VALUE );
772
773 case CRYPT_SESSINFO_USERNAME:
774 case CRYPT_SESSINFO_PASSWORD:
775 {
776 ATTRIBUTE_LIST *attributeListPtr = NULL;
777 int flags = isServer( sessionInfoPtr ) ? \
778 ATTR_FLAG_MULTIVALUED : ATTR_FLAG_NONE;
779 int status;
780
781 REQUIRES( dataLength > 0 && dataLength <= CRYPT_MAX_TEXTSIZE );
782
783 /* If this is a client session then we can only have a single
784 instance of this attribute */
785 if( !isServer( sessionInfoPtr ) && \
786 findSessionInfo( sessionInfoPtr->attributeList,
787 attribute ) != NULL )
788 return( exitErrorInited( sessionInfoPtr, attribute ) );
789
790 /* Get the last-added attribute so that we can check that it's
791 consistent with what's being added now */
792 status = setSessionAttributeCursor( sessionInfoPtr->attributeList,
793 &attributeListPtr,
794 CRYPT_ATTRIBUTE_CURRENT_GROUP,
795 CRYPT_CURSOR_LAST );
796 if( attribute == CRYPT_SESSINFO_USERNAME )
797 {
798 /* It's a username make sure that the last attribute added
799 wasn't also a username and that it doesn't duplicate an
800 existing name */
801 if( cryptStatusOK( status ) && \
802 attributeListPtr->attributeID == CRYPT_SESSINFO_USERNAME )
803 return( exitErrorInited( sessionInfoPtr,
804 CRYPT_SESSINFO_USERNAME ) );
805 if( findSessionInfoEx( sessionInfoPtr->attributeList,
806 attribute, data, dataLength ) != NULL )
807 {
808 return( exitError( sessionInfoPtr, attribute,
809 CRYPT_ERRTYPE_ATTR_PRESENT,
810 CRYPT_ERROR_DUPLICATE ) );
811 }
812 }
813 else
814 {
815 /* It's a password, make sure that there's an associated
816 username to go with it. There are two approaches that
817 we can take here, the first simply requires that the
818 current cursor position is a username, implying that
819 the last-added attribute was a username. The other is
820 to try and move the cursor to the last username in the
821 attribute list and check that the next attribute isn't
822 a password and then add it there, however this is doing
823 a bit too much behind the user's back, is somewhat
824 difficult to back out of, and leads to exceptions to
825 exceptions, so we keep it simple and only allow passwords
826 to be added if there's an immediately preceding
827 username */
828 if( cryptStatusError( status ) || \
829 attributeListPtr->attributeID != CRYPT_SESSINFO_USERNAME )
830 return( exitErrorNotInited( sessionInfoPtr,
831 CRYPT_SESSINFO_USERNAME ) );
832 }
833
834 /* If it could be an encoded PKI value, check its validity */
835 if( dataLength >= 15 && isPKIUserValue( data, dataLength ) )
836 {
837 BYTE decodedValue[ CRYPT_MAX_TEXTSIZE + 8 ];
838 int decodedValueLen;
839
840 /* It's an encoded value, make sure that it's in order */
841 status = decodePKIUserValue( decodedValue,
842 CRYPT_MAX_TEXTSIZE,
843 &decodedValueLen, data,
844 dataLength );
845 zeroise( decodedValue, CRYPT_MAX_TEXTSIZE );
846 if( cryptStatusError( status ) )
847 return( status );
848 flags = ATTR_FLAG_ENCODEDVALUE;
849 }
850
851 /* Perform any protocol-specific additional checks if necessary */
852 if( FNPTR_ISSET( sessionInfoPtr->checkAttributeFunction ) )
853 {
854 const SES_CHECKATTRIBUTE_FUNCTION checkAttributeFunction = \
855 FNPTR_GET( sessionInfoPtr->checkAttributeFunction );
856 MESSAGE_DATA msgData;
857
858 REQUIRES( checkAttributeFunction != NULL );
859
860 setMessageData( &msgData, ( MESSAGE_CAST ) data, dataLength );
861 status = checkAttributeFunction( sessionInfoPtr, &msgData,
862 attribute );
863 if( status == OK_SPECIAL )
864 {
865 /* The value was dealt with as a side-effect of the check
866 function, there's nothing more to do */
867 return( CRYPT_OK );
868 }
869 if( cryptStatusError( status ) )
870 return( status );
871 }
872
873 /* Remember the value */
874 return( addSessionInfoEx( &sessionInfoPtr->attributeList,
875 attribute, data, dataLength, flags ) );
876 }
877
878 case CRYPT_SESSINFO_SERVER_FINGERPRINT_SHA1:
879 /* Remember the value */
880 return( addSessionInfoS( &sessionInfoPtr->attributeList,
881 attribute, data, dataLength ) );
882
883 case CRYPT_SESSINFO_SERVER_NAME:
884 return( addUrl( sessionInfoPtr, data, dataLength ) );
885 }
886
887 retIntError();
888 }
889
890 /****************************************************************************
891 * *
892 * Delete Attributes *
893 * *
894 ****************************************************************************/
895
896 /* Delete an attribute */
897
898 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
deleteSessionAttribute(INOUT SESSION_INFO * sessionInfoPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute)899 int deleteSessionAttribute( INOUT SESSION_INFO *sessionInfoPtr,
900 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
901 {
902 const ATTRIBUTE_LIST *attributeListPtr;
903
904 assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
905
906 REQUIRES( isAttribute( attribute ) || \
907 isInternalAttribute( attribute ) );
908
909 /* Handle the various information types */
910 switch( attribute )
911 {
912 case CRYPT_OPTION_NET_CONNECTTIMEOUT:
913 if( sessionInfoPtr->connectTimeout == CRYPT_ERROR )
914 return( exitErrorNotFound( sessionInfoPtr,
915 CRYPT_OPTION_NET_CONNECTTIMEOUT ) );
916 sessionInfoPtr->connectTimeout = CRYPT_ERROR;
917 return( CRYPT_OK );
918
919 case CRYPT_OPTION_NET_READTIMEOUT:
920 if( sessionInfoPtr->readTimeout == CRYPT_ERROR )
921 return( exitErrorNotFound( sessionInfoPtr,
922 CRYPT_OPTION_NET_READTIMEOUT ) );
923 sessionInfoPtr->readTimeout = CRYPT_ERROR;
924 return( CRYPT_OK );
925
926 case CRYPT_OPTION_NET_WRITETIMEOUT:
927 if( sessionInfoPtr->writeTimeout == CRYPT_ERROR )
928 return( exitErrorNotFound( sessionInfoPtr,
929 CRYPT_OPTION_NET_WRITETIMEOUT ) );
930 sessionInfoPtr->writeTimeout = CRYPT_ERROR;
931 return( CRYPT_OK );
932
933 case CRYPT_SESSINFO_USERNAME:
934 case CRYPT_SESSINFO_PASSWORD:
935 case CRYPT_SESSINFO_SERVER_NAME:
936 case CRYPT_SESSINFO_SERVER_PORT:
937 /* Make sure that the attribute to delete is actually present */
938 attributeListPtr = \
939 findSessionInfo( sessionInfoPtr->attributeList, attribute );
940 if( attributeListPtr == NULL )
941 return( exitErrorNotFound( sessionInfoPtr, attribute ) );
942
943 /* Delete the attribute. If we're in the middle of a paired-
944 attribute add then the delete affects the paired attribute.
945 This can get quite complex because the user could (for
946 example) add a { username, password } pair, then add a second
947 username (but not password), and then delete the first
948 password, leaving an orphaned password followed by an
949 orphaned username. There isn't any easy way to fix this
950 short of forcing some form of group delete of paired
951 attributes, but this gets too complicated both to implement
952 and to explain to the user in an error status. What we do
953 here is handle the simple case and let the pre-session-
954 activation sanity check catch situations where the user's
955 gone out of their way to be difficult */
956 deleteSessionInfo( &sessionInfoPtr->attributeList,
957 &sessionInfoPtr->attributeListCurrent,
958 ( ATTRIBUTE_LIST * ) attributeListPtr );
959 return( CRYPT_OK );
960
961 case CRYPT_SESSINFO_REQUEST:
962 if( sessionInfoPtr->iCertRequest == CRYPT_ERROR )
963 return( exitErrorNotFound( sessionInfoPtr,
964 CRYPT_SESSINFO_REQUEST ) );
965 krnlSendNotifier( sessionInfoPtr->iCertRequest,
966 IMESSAGE_DECREFCOUNT );
967 sessionInfoPtr->iCertRequest = CRYPT_ERROR;
968
969 return( CRYPT_OK );
970
971 case CRYPT_SESSINFO_TSP_MSGIMPRINT:
972 if( sessionInfoPtr->sessionTSP->imprintAlgo == CRYPT_ALGO_NONE || \
973 sessionInfoPtr->sessionTSP->imprintSize <= 0 )
974 return( exitErrorNotFound( sessionInfoPtr,
975 CRYPT_SESSINFO_TSP_MSGIMPRINT ) );
976 sessionInfoPtr->sessionTSP->imprintAlgo = CRYPT_ALGO_NONE;
977 sessionInfoPtr->sessionTSP->imprintSize = 0;
978
979 return( CRYPT_OK );
980 }
981
982 retIntError();
983 }
984 #endif /* USE_SESSIONS */
985