1 /****************************************************************************
2 *																			*
3 *			cryptlib Session-specific Attribute Support Routines			*
4 *					  Copyright Peter Gutmann 1998-2008						*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "crypt.h"
10   #include "session.h"
11 #else
12   #include "crypt.h"
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 /* Helper function used to access internal attributes within an attribute
25    group */
26 
27 #if 0	/* Currently unused, may be enabled in a later version with a move
28 		   to composite attributes for host/client information */
29 
30 /* Reset the internal virtual cursor in a attribute-list item after we've
31    moved the attribute cursor */
32 
33 #define resetVirtualCursor( attributeListPtr ) \
34 		if( attributeListPtr != NULL ) \
35 			attributeListPtr->flags |= ATTR_FLAG_CURSORMOVED
36 
37 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
38 static int accessFunction( INOUT ATTRIBUTE_LIST *attributeListPtr,
39 						   IN_ENUM( ATTR ) const ATTR_TYPE attrGetType )
40 	{
41 	static const CRYPT_ATTRIBUTE_TYPE attributeOrderList[] = {
42 				CRYPT_SESSINFO_NAME, CRYPT_SESSINFO_PASSWORD,
43 				CRYPT_SESSINFO_KEY, CRYPT_ATTRIBUTE_NONE,
44 				CRYPT_ATTRIBUTE_NONE };
45 	USER_INFO *userInfoPtr = attributeListPtr->value;
46 	CRYPT_ATTRIBUTE_TYPE attributeID = userInfoPtr->cursorPos;
47 	BOOLEAN doContinue;
48 	int iterationCount = 0;
49 
50 	assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
51 
52 	REQUIRES( attrGetType > ATTR_NONE && attrGetType < ATTR_LAST );
53 
54 	/* If we've just moved the cursor onto this attribute, reset the
55 	   position to the first internal attribute */
56 	if( attributeListPtr->flags & ATTR_FLAG_CURSORMOVED )
57 		{
58 		attributeID = userInfoPtr->cursorPos = \
59 						CRYPT_ENVINFO_SIGNATURE_RESULT;
60 		attributeListPtr->flags &= ~ATTR_FLAG_CURSORMOVED;
61 		}
62 
63 	/* If it's an information fetch, return the currently-selected
64 	   attribute */
65 	if( attrGetType == ATTR_NONE )
66 		return( attributeID );
67 
68 	do
69 		{
70 		int i;
71 
72 		/* Find the position of the current sub-attribute in the attribute
73 		   order list and use that to get its successor/predecessor sub-
74 		   attribute */
75 		for( i = 0;
76 			 attributeOrderList[ i ] != attributeID && \
77 				attributeOrderList[ i ] != CRYPT_ATTRIBUTE_NONE && \
78 				i < FAILSAFE_ARRAYSIZE( attributeOrderList, CRYPT_ATTRIBUTE_TYPE );
79 			 i++ );
80 		ENSURES_B( i < FAILSAFE_ARRAYSIZE( attributeOrderList, \
81 										   CRYPT_ATTRIBUTE_TYPE ) );
82 		if( attributeOrderList[ i ] == CRYPT_ATTRIBUTE_NONE )
83 			attributeID = CRYPT_ATTRIBUTE_NONE;
84 		else
85 			{
86 			if( attrGetType == ATTR_PREV )
87 				{
88 				attributeID = ( i < 1 ) ? CRYPT_ATTRIBUTE_NONE : \
89 										  attributeOrderList[ i - 1 ];
90 				}
91 			else
92 				attributeID = attributeOrderList[ i + 1 ];
93 			}
94 		if( attributeID == CRYPT_ATTRIBUTE_NONE )
95 			{
96 			/* We've reached the first/last sub-attribute within the current
97 			   item/group, tell the caller that there are no more sub-
98 			   attributes present and they have to move on to the next
99 			   group */
100 			return( FALSE );
101 			}
102 
103 		/* Check whether the required sub-attribute is present.  If not, we
104 		   continue and try the next one */
105 		doContinue = FALSE;
106 		switch( attributeID )
107 			{
108 			case CRYPT_SESSINFO_NAME:
109 				break;	/* Always present */
110 
111 			case CRYPT_SESSINFO_PASSWORD:
112 				if( userInfoPtr->passwordLen <= 0 )
113 					doContinue = TRUE;
114 				break;
115 
116 			case CRYPT_SESSINFO_KEY:
117 				if( userInfoPtr->key == CRYPT_ERROR )
118 					doContinue = TRUE;
119 				break;
120 
121 			default:
122 				retIntError_Boolean();
123 			}
124 		}
125 	while( doContinue && iterationCount++ < FAILSAFE_ITERATIONS_SMALL );
126 	ENSURES_B( iterationCount < FAILSAFE_ITERATIONS_SMALL );
127 	attributeListPtr->attributeCursorEntry = attributeID;
128 
129 	return( TRUE );
130 	}
131 #else
132   #define resetVirtualCursor( attributeListPtr )
133 #endif /* 0 */
134 
135 /* Callback function used to provide external access to attribute list-
136    internal fields */
137 
138 CHECK_RETVAL_PTR \
getAttrFunction(IN_OPT TYPECAST (ATTRIBUTE_LIST *)const void * attributePtr,OUT_OPT_ATTRIBUTE_Z CRYPT_ATTRIBUTE_TYPE * groupID,OUT_OPT_ATTRIBUTE_Z CRYPT_ATTRIBUTE_TYPE * attributeID,OUT_OPT_ATTRIBUTE_Z CRYPT_ATTRIBUTE_TYPE * instanceID,IN_ENUM (ATTR)const ATTR_TYPE attrGetType)139 static const void *getAttrFunction( IN_OPT TYPECAST( ATTRIBUTE_LIST * ) \
140 										const void *attributePtr,
141 									OUT_OPT_ATTRIBUTE_Z \
142 										CRYPT_ATTRIBUTE_TYPE *groupID,
143 									OUT_OPT_ATTRIBUTE_Z \
144 										CRYPT_ATTRIBUTE_TYPE *attributeID,
145 									OUT_OPT_ATTRIBUTE_Z \
146 										CRYPT_ATTRIBUTE_TYPE *instanceID,
147 									IN_ENUM( ATTR ) const ATTR_TYPE attrGetType )
148 	{
149 	ATTRIBUTE_LIST *attributeListPtr = ( ATTRIBUTE_LIST * ) attributePtr;
150 	ATTRACCESS_FUNCTION accessFunction = \
151 					FNPTR_GET( attributeListPtr->accessFunction );
152 	BOOLEAN subGroupMove;
153 	int value, status;
154 
155 	assert( attributeListPtr == NULL || \
156 			isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
157 	assert( groupID == NULL || \
158 			isWritePtr( groupID, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
159 	assert( attributeID == NULL || \
160 			isWritePtr( attributeID, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
161 	assert( instanceID == NULL || \
162 			isWritePtr( instanceID, sizeof( CRYPT_ATTRIBUTE_TYPE ) ) );
163 
164 	REQUIRES_N( attrGetType > ATTR_NONE && attrGetType < ATTR_LAST );
165 
166 	/* Clear return values */
167 	if( groupID != NULL )
168 		*groupID = CRYPT_ATTRIBUTE_NONE;
169 	if( attributeID != NULL )
170 		*attributeID = CRYPT_ATTRIBUTE_NONE;
171 	if( instanceID != NULL )
172 		*instanceID = CRYPT_ATTRIBUTE_NONE;
173 
174 	/* Move to the next or previous attribute if required.  This isn't just a
175 	   case of following the previous/next links because some attribute-list
176 	   items contain an entire attribute group so that positioning by
177 	   attribute merely changes the current selection within the group
178 	   (== attribute-list item) rather than moving to the previous/next
179 	   entry.  Because of this we have to special-case the code for
180 	   composite items and allow virtual positioning within the item */
181 	if( attributeListPtr == NULL )
182 		return( NULL );
183 	subGroupMove = ( attrGetType == ATTR_PREV || \
184 					 attrGetType == ATTR_NEXT ) && \
185 				   ( attributeListPtr->flags & ATTR_FLAG_COMPOSITE );
186 	if( subGroupMove )
187 		{
188 		REQUIRES_N( attrGetType == ATTR_NEXT || attrGetType == ATTR_PREV );
189 		REQUIRES_N( attributeListPtr->flags & ATTR_FLAG_COMPOSITE );
190 
191 		accessFunction = FNPTR_GET( attributeListPtr->accessFunction );
192 		REQUIRES_N( accessFunction != NULL );
193 		status = accessFunction( attributeListPtr, attrGetType, &value );
194 		if( cryptStatusError( status ) )
195 			return( NULL );
196 		subGroupMove = value;
197 		}
198 
199 	/* If we're moving by group, move to the next/previous attribute list
200 	   item and reset the internal virtual cursor.  Note that we always
201 	   advance the cursor to the next/previous attribute, it's up to the
202 	   calling code to manage attribute-by-attribute vs. group-by-group
203 	   moves */
204 	if( !subGroupMove && attrGetType != ATTR_CURRENT )
205 		{
206 		attributeListPtr = ( attrGetType == ATTR_PREV ) ? \
207 						   attributeListPtr->prev : attributeListPtr->next;
208 		resetVirtualCursor( attributeListPtr );
209 		}
210 	if( attributeListPtr == NULL )
211 		return( NULL );
212 
213 	/* Return ID information to the caller.  We only return the group ID if
214 	   we've moved within the attribute group, if we've moved from one group
215 	   to another we leave it cleared because sessions can contain multiple
216 	   groups with the same ID and returning an ID identical to the one from
217 	   the group that we've moved out of would make it look as if we're still
218 	   within the same group.  Note that this relies somewhat on the
219 	   implementation behaviour of the attribute-move functions, which first
220 	   get the current group using ATTR_CURRENT and then move to the next or
221 	   previous using ATTR_NEXT/PREV */
222 	if( groupID != NULL && ( attrGetType == ATTR_CURRENT || subGroupMove ) )
223 		*groupID = attributeListPtr->groupID;
224 	if( attributeID != NULL )
225 		{
226 		if( attributeListPtr->flags & ATTR_FLAG_COMPOSITE )
227 			{
228 			accessFunction = FNPTR_GET( attributeListPtr->accessFunction );
229 			REQUIRES_N( accessFunction != NULL );
230 			status = accessFunction( attributeListPtr, ATTR_NONE, &value );
231 			if( cryptStatusError( status ) )
232 				return( NULL );
233 			*attributeID = value;
234 			}
235 		else
236 			*attributeID = attributeListPtr->attributeID;
237 		}
238 
239 	return( attributeListPtr );
240 	}
241 
242 /* Lock ephemeral attributes so that they can't be deleted any more by
243    resetEphemeralAttributes().  This just clears the ephemeral flag so that
244    they're treated as normal attributes */
245 
246 STDC_NONNULL_ARG( ( 1 ) ) \
lockEphemeralAttributes(INOUT ATTRIBUTE_LIST * attributeListHead)247 void lockEphemeralAttributes( INOUT ATTRIBUTE_LIST *attributeListHead )
248 	{
249 	ATTRIBUTE_LIST *attributeListCursor;
250 	int iterationCount;
251 
252 	assert( isWritePtr( attributeListHead, sizeof( ATTRIBUTE_LIST * ) ) );
253 
254 	/* Clear the ATTR_FLAG_EPHEMERAL flag on all attributes */
255 	for( attributeListCursor = attributeListHead, iterationCount = 0;
256 		 attributeListCursor != NULL && \
257 			iterationCount < FAILSAFE_ITERATIONS_MAX;
258 		 attributeListCursor = attributeListCursor->next, iterationCount++ )
259 		{
260 		attributeListCursor->flags &= ~ATTR_FLAG_EPHEMERAL;
261 		}
262 	ENSURES_V( iterationCount < FAILSAFE_ITERATIONS_MAX );
263 	}
264 
265 /* Check that a set of attributes is well-formed.  We can perform most of
266    the checking as the attributes are added but some checks (for example
267    whether each username has a corresponding password) aren't possible
268    until all of the attributes are present */
269 
CHECK_RETVAL_ENUM(CRYPT_ATTRIBUTE)270 CHECK_RETVAL_ENUM( CRYPT_ATTRIBUTE ) \
271 CRYPT_ATTRIBUTE_TYPE checkMissingInfo( IN_OPT const ATTRIBUTE_LIST *attributeListHead,
272 									   const BOOLEAN isServer )
273 	{
274 	const ATTRIBUTE_LIST *attributeListPtr = attributeListHead;
275 
276 	assert( attributeListHead == NULL || \
277 			isReadPtr( attributeListHead, sizeof( ATTRIBUTE_LIST * ) ) );
278 
279 	if( attributeListPtr == NULL )
280 		return( CRYPT_ATTRIBUTE_NONE );
281 
282 	/* Make sure that every username attribute is paired up with a
283 	   corresponding authentication attribute.  This only applies to
284 	   servers because clients use a session-wide private key for
285 	   authentication, the presence of which is checked elsewhere */
286 	if( isServer )
287 		{
288 		int iterationCount;
289 
290 		for( iterationCount = 0;
291 			 ( attributeListPtr = \
292 					attributeFind( attributeListPtr, getAttrFunction,
293 								   CRYPT_SESSINFO_USERNAME ) ) != NULL && \
294 				iterationCount < FAILSAFE_ITERATIONS_MAX;
295 			 iterationCount++ )
296 			{
297 			/* Make sure that there's a matching authentication attribute.
298 			   This is currently a password but in future versions could
299 			   also be a public key */
300 			attributeListPtr = attributeListPtr->next;
301 			if( attributeListPtr == NULL || \
302 				attributeListPtr->attributeID != CRYPT_SESSINFO_PASSWORD )
303 				return( CRYPT_SESSINFO_PASSWORD );
304 
305 			/* Move on to the next attribute */
306 			attributeListPtr = attributeListPtr->next;
307 			}
308 		ENSURES_EXT( ( iterationCount < FAILSAFE_ITERATIONS_MAX ), \
309 					 CRYPT_SESSINFO_ACTIVE );
310 		}
311 
312 	return( CRYPT_ATTRIBUTE_NONE );
313 	}
314 
315 /****************************************************************************
316 *																			*
317 *					Attribute Cursor Management Routines					*
318 *																			*
319 ****************************************************************************/
320 
321 /* Get/set the attribute cursor */
322 
323 CHECK_RETVAL STDC_NONNULL_ARG( ( 4 ) ) \
getSessionAttributeCursor(IN_OPT ATTRIBUTE_LIST * attributeListHead,IN_OPT ATTRIBUTE_LIST * attributeListCursor,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE sessionInfoType,OUT_ATTRIBUTE_Z CRYPT_ATTRIBUTE_TYPE * valuePtr)324 int getSessionAttributeCursor( IN_OPT ATTRIBUTE_LIST *attributeListHead,
325 							   IN_OPT ATTRIBUTE_LIST *attributeListCursor,
326 							   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE sessionInfoType,
327 							   OUT_ATTRIBUTE_Z CRYPT_ATTRIBUTE_TYPE *valuePtr )
328 	{
329 	BOOLEAN initAttributeList = FALSE;
330 
331 	assert( attributeListHead == NULL || \
332 			isWritePtr( attributeListHead, sizeof( ATTRIBUTE_LIST ) ) );
333 	assert( attributeListCursor == NULL || \
334 			isWritePtr( attributeListCursor, sizeof( ATTRIBUTE_LIST ) ) );
335 	assert( isWritePtr( valuePtr, sizeof( int ) ) );
336 
337 	REQUIRES( sessionInfoType == CRYPT_ATTRIBUTE_CURRENT || \
338 			  sessionInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
339 			  ( sessionInfoType > CRYPT_SESSINFO_FIRST && \
340 				sessionInfoType < CRYPT_SESSINFO_LAST ) );
341 
342 	/* Clear return value */
343 	*valuePtr = CRYPT_ATTRIBUTE_NONE;
344 
345 	/* We're querying something that resides in the attribute list, make
346 	   sure that there's an attribute list present.  If it's present but
347 	   nothing is selected, select the first entry */
348 	if( attributeListCursor == NULL )
349 		{
350 		if( attributeListHead == NULL )
351 			return( CRYPT_ERROR_NOTFOUND );
352 		attributeListCursor = attributeListHead;
353 		resetVirtualCursor( attributeListCursor );
354 		initAttributeList = TRUE;
355 		}
356 
357 	/* If we're reading the group, return the group type */
358 	if( sessionInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP )
359 		*valuePtr = attributeListCursor->groupID;
360 	else
361 		{
362 		/* If it's a single-attribute group, return the attribute type */
363 		if( !( attributeListCursor->flags & ATTR_FLAG_COMPOSITE ) )
364 			*valuePtr = attributeListCursor->groupID;
365 		else
366 			{
367 			const ATTRACCESS_FUNCTION accessFunction = \
368 					FNPTR_GET( attributeListCursor->accessFunction );
369 			int value, status;
370 
371 			REQUIRES( accessFunction != NULL );
372 
373 			/* It's a composite type, get the currently-selected sub-attribute */
374 			status = accessFunction( attributeListCursor, ATTR_NONE, &value );
375 			if( cryptStatusError( status ) )
376 				return( status );
377 			*valuePtr = value;
378 			}
379 		}
380 	return( initAttributeList ? OK_SPECIAL : CRYPT_OK );
381 	}
382 
383 CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
setSessionAttributeCursor(IN_OPT const ATTRIBUTE_LIST * attributeListHead,INOUT_PTR ATTRIBUTE_LIST ** attributeListCursorPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE sessionInfoType,IN_RANGE (CRYPT_CURSOR_LAST,CRYPT_CURSOR_FIRST)const int position)384 int setSessionAttributeCursor( IN_OPT const ATTRIBUTE_LIST *attributeListHead,
385 							   INOUT_PTR ATTRIBUTE_LIST **attributeListCursorPtr,
386 							   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE sessionInfoType,
387 							   IN_RANGE( CRYPT_CURSOR_LAST, \
388 										 CRYPT_CURSOR_FIRST ) /* Values are -ve */
389 									const int position )
390 	{
391 	const ATTRIBUTE_LIST *attributeListPtr = *attributeListCursorPtr;
392 
393 	assert( attributeListHead == NULL || \
394 			isReadPtr( attributeListHead, sizeof( ATTRIBUTE_LIST ) ) );
395 	assert( isWritePtr( attributeListCursorPtr, sizeof( ATTRIBUTE_LIST * ) ) );
396 
397 	REQUIRES( sessionInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
398 			  sessionInfoType == CRYPT_ATTRIBUTE_CURRENT );
399 	REQUIRES( position <= CRYPT_CURSOR_FIRST && \
400 			  position >= CRYPT_CURSOR_LAST );
401 
402 	/* If it's an absolute positioning code, pre-set the attribute cursor if
403 	   required */
404 	if( position == CRYPT_CURSOR_FIRST || position == CRYPT_CURSOR_LAST )
405 		{
406 		if( attributeListHead == NULL )
407 			return( CRYPT_ERROR_NOTFOUND );
408 
409 		/* If it's an absolute attribute positioning code reset the
410 		   attribute cursor to the start of the list before we try to move
411 		   it, and if it's an attribute positioning code initialise the
412 		   attribute cursor if necessary */
413 		if( sessionInfoType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
414 			attributeListPtr == NULL )
415 			{
416 			attributeListPtr = attributeListHead;
417 			resetVirtualCursor( attributeListPtr );
418 			}
419 
420 		/* If there are no attributes present return the appropriate error
421 		   code */
422 		if( attributeListPtr == NULL )
423 			{
424 			return( ( position == CRYPT_CURSOR_FIRST || \
425 					  position == CRYPT_CURSOR_LAST ) ? \
426 					 CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_NOTINITED );
427 			}
428 		}
429 	else
430 		{
431 		/* It's a relative positioning code, return a not-inited error
432 		   rather than a not-found error if the cursor isn't set since there
433 		   may be attributes present but the cursor hasn't been initialised
434 		   yet by selecting the first or last absolute attribute */
435 		if( attributeListPtr == NULL )
436 			return( CRYPT_ERROR_NOTINITED );
437 		}
438 
439 	/* Move the cursor */
440 	attributeListPtr = ( const ATTRIBUTE_LIST * ) \
441 					   attributeMoveCursor( attributeListPtr, getAttrFunction,
442 											sessionInfoType, position );
443 	if( attributeListPtr == NULL )
444 		return( CRYPT_ERROR_NOTFOUND );
445 	*attributeListCursorPtr = ( ATTRIBUTE_LIST * ) attributeListPtr;
446 	return( CRYPT_OK );
447 	}
448 
449 /****************************************************************************
450 *																			*
451 *								Find an Attribute							*
452 *																			*
453 ****************************************************************************/
454 
455 /* Find a session attribute by type */
456 
457 CHECK_RETVAL_PTR \
findSessionInfo(IN_OPT const ATTRIBUTE_LIST * attributeListPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID)458 const ATTRIBUTE_LIST *findSessionInfo( IN_OPT const ATTRIBUTE_LIST *attributeListPtr,
459 									   IN_ATTRIBUTE \
460 											const CRYPT_ATTRIBUTE_TYPE attributeID )
461 	{
462 	assert( attributeListPtr == NULL || \
463 			isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
464 
465 	REQUIRES_N( attributeID > CRYPT_SESSINFO_FIRST && \
466 				attributeID < CRYPT_SESSINFO_LAST );
467 
468 	return( attributeFind( attributeListPtr, getAttrFunction, attributeID ) );
469 	}
470 
471 /* Find a session attribute by type and content */
472 
473 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 3 ) ) \
findSessionInfoEx(IN_OPT const ATTRIBUTE_LIST * attributeListPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,IN_BUFFER (valueLength)const void * value,IN_LENGTH_SHORT const int valueLength)474 const ATTRIBUTE_LIST *findSessionInfoEx( IN_OPT const ATTRIBUTE_LIST *attributeListPtr,
475 										 IN_ATTRIBUTE \
476 											const CRYPT_ATTRIBUTE_TYPE attributeID,
477 										 IN_BUFFER( valueLength ) const void *value,
478 										 IN_LENGTH_SHORT const int valueLength )
479 	{
480 	const ATTRIBUTE_LIST *attributeListCursor;
481 	int iterationCount;
482 
483 	assert( attributeListPtr == NULL || \
484 			isReadPtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
485 	assert( isReadPtr( value, valueLength ) );
486 
487 	REQUIRES_N( attributeID > CRYPT_SESSINFO_FIRST && \
488 				attributeID < CRYPT_SESSINFO_LAST );
489 	REQUIRES_N( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
490 
491 	/* Find the first attribute of this type */
492 	attributeListCursor = attributeFind( attributeListPtr, getAttrFunction,
493 										 attributeID );
494 	if( attributeListCursor == NULL )
495 		return( NULL );
496 
497 	/* Walk down the rest of the list looking for an attribute entry whose
498 	   contents match the requested contents.  Unfortunately we can't use
499 	   attributeFindNextInstance() to help us because that finds the next
500 	   instance of the current attribute in an attribute group, not the next
501 	   instance in an interleaved set of attributes */
502 	for( iterationCount = 0;
503 		 attributeListCursor != NULL && \
504 			iterationCount < FAILSAFE_ITERATIONS_MAX;
505 		 iterationCount++ )
506 		{
507 		if( attributeListCursor->attributeID == attributeID && \
508 			attributeListCursor->valueLength == valueLength && \
509 			!memcmp( attributeListCursor->value, value, valueLength ) )
510 			break;
511 		attributeListCursor = attributeListCursor->next;
512 		}
513 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
514 
515 	return( attributeListCursor );
516 	}
517 
518 /****************************************************************************
519 *																			*
520 *								Add an Attribute							*
521 *																			*
522 ****************************************************************************/
523 
524 /* Add a session attribute.  There are three versions of this function, the
525    standard version and two extended versions that allow the caller to
526    specify an access function to access session subtype-specific internal
527    attributes when the data being added is structured session-type-specific
528    data, and that allow the use of a set of ATTR_FLAG_xxx flags to provide
529    precise control over the attribute handling */
530 
531 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
addInfo(INOUT_PTR ATTRIBUTE_LIST ** listHeadPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE groupID,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,IN_BUFFER_OPT (dataLength)const void * data,IN_LENGTH_SHORT const int dataLength,IN_LENGTH_SHORT_Z const int dataMaxLength,IN_OPT const ATTRACCESS_FUNCTION accessFunction,IN_FLAGS_Z (ATTR)const int flags)532 static int addInfo( INOUT_PTR ATTRIBUTE_LIST **listHeadPtr,
533 					IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE groupID,
534 					IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,
535 					IN_BUFFER_OPT( dataLength ) const void *data,
536 					IN_LENGTH_SHORT const int dataLength,
537 					IN_LENGTH_SHORT_Z const int dataMaxLength,
538 					IN_OPT const ATTRACCESS_FUNCTION accessFunction,
539 					IN_FLAGS_Z( ATTR ) const int flags )
540 	{
541 	ATTRIBUTE_LIST *newElement, *insertPoint = NULL;
542 
543 	assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
544 	assert( ( data == NULL ) || \
545 			( isReadPtr( data, dataLength ) && \
546 			  dataLength <= dataMaxLength ) );
547 
548 	REQUIRES( groupID > CRYPT_SESSINFO_FIRST && \
549 			  groupID < CRYPT_SESSINFO_LAST );
550 	REQUIRES( attributeID > CRYPT_SESSINFO_FIRST && \
551 			  attributeID < CRYPT_SESSINFO_LAST );
552 	REQUIRES( ( data == NULL && dataMaxLength == 0 ) || \
553 			  ( data != NULL && \
554 				dataLength > 0 && dataLength <= dataMaxLength && \
555 				dataMaxLength > 0 && dataMaxLength < MAX_INTLENGTH_SHORT ) );
556 			  /* String = { data, dataLength, dataMaxLength },
557 			     int = dataLength */
558 	REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
559 	REQUIRES( !( flags & ATTR_FLAG_COMPOSITE ) || \
560 			  accessFunction != NULL );
561 
562 	/* Find the correct insertion point and make sure that the attribute
563 	   isn't already present */
564 	if( *listHeadPtr != NULL )
565 		{
566 		ATTRIBUTE_LIST *prevElement = NULL;
567 		int iterationCount;
568 
569 		for( insertPoint = *listHeadPtr, iterationCount = 0;
570 			 insertPoint != NULL && \
571 				iterationCount < FAILSAFE_ITERATIONS_MAX;
572 			 insertPoint = insertPoint->next, iterationCount++ )
573 			{
574 			/* If this is a non-multivalued attribute, make sure that it
575 			   isn't already present */
576 			if( !( flags & ATTR_FLAG_MULTIVALUED ) && \
577 				insertPoint->attributeID == attributeID )
578 				return( CRYPT_ERROR_INITED );
579 
580 			prevElement = insertPoint;
581 			}
582 		ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );
583 		insertPoint = prevElement;
584 		}
585 
586 	/* Allocate memory for the new element and copy the information across.
587 	   The data is stored in storage ... storage + dataLength, with storage
588 	   reserved up to dataMaxLength (if it's greater than dataLength) to
589 	   allow the contents to be replaced with a new fixed-length value  */
590 	if( ( newElement = ( ATTRIBUTE_LIST * ) \
591 					   clAlloc( "addSessionAttribute", sizeof( ATTRIBUTE_LIST ) + \
592 													   dataMaxLength ) ) == NULL )
593 		return( CRYPT_ERROR_MEMORY );
594 	initVarStruct( newElement, ATTRIBUTE_LIST, dataMaxLength );
595 	newElement->groupID = groupID;
596 	newElement->attributeID = attributeID;
597 	FNPTR_SET( newElement->accessFunction, accessFunction );
598 	newElement->flags = flags;
599 	if( data == NULL )
600 		newElement->intValue = dataLength;
601 	else
602 		{
603 		assert( isReadPtr( data, dataLength ) );
604 
605 		memcpy( newElement->value, data, dataLength );
606 		newElement->valueLength = dataLength;
607 		}
608 	insertDoubleListElement( listHeadPtr, insertPoint, newElement );
609 
610 	return( CRYPT_OK );
611 	}
612 
613 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
addSessionInfo(INOUT_PTR ATTRIBUTE_LIST ** listHeadPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,IN_INT_Z const int value)614 int addSessionInfo( INOUT_PTR ATTRIBUTE_LIST **listHeadPtr,
615 					IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,
616 					IN_INT_Z const int value )
617 	{
618 	assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
619 
620 	REQUIRES( attributeID > CRYPT_SESSINFO_FIRST && \
621 			  attributeID < CRYPT_SESSINFO_LAST );
622 	REQUIRES( value >= 0 && value < MAX_INTLENGTH );
623 
624 	/* Pre-3.3 kludge: Set the groupID to the attributeID since groups
625 	   aren't defined yet */
626 	return( addInfo( listHeadPtr, attributeID, attributeID, NULL,
627 					 value, 0, NULL, ATTR_FLAG_NONE ) );
628 	}
629 
630 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
addSessionInfoS(INOUT_PTR ATTRIBUTE_LIST ** listHeadPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,IN_BUFFER (dataLength)const void * data,IN_LENGTH_SHORT const int dataLength)631 int addSessionInfoS( INOUT_PTR ATTRIBUTE_LIST **listHeadPtr,
632 					IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,
633 					IN_BUFFER( dataLength ) const void *data,
634 					IN_LENGTH_SHORT const int dataLength )
635 	{
636 	assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
637 	assert( isReadPtr( data, dataLength ) );
638 
639 	REQUIRES( attributeID > CRYPT_SESSINFO_FIRST && \
640 			  attributeID < CRYPT_SESSINFO_LAST );
641 	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
642 
643 	/* Pre-3.3 kludge: Set the groupID to the attributeID since groups
644 	   aren't defined yet */
645 	return( addInfo( listHeadPtr, attributeID, attributeID, data,
646 					 dataLength, dataLength, NULL, ATTR_FLAG_NONE ) );
647 	}
648 
649 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
addSessionInfoEx(INOUT_PTR ATTRIBUTE_LIST ** listHeadPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,IN_BUFFER (dataLength)const void * data,IN_LENGTH_SHORT const int dataLength,IN_FLAGS_Z (ATTR)const int flags)650 int addSessionInfoEx( INOUT_PTR ATTRIBUTE_LIST **listHeadPtr,
651 					  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,
652 					  IN_BUFFER( dataLength ) const void *data,
653 					  IN_LENGTH_SHORT const int dataLength,
654 					  IN_FLAGS_Z( ATTR ) const int flags )
655 	{
656 	assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
657 	assert( isReadPtr( data, dataLength ) );
658 
659 	REQUIRES( attributeID > CRYPT_SESSINFO_FIRST && \
660 			  attributeID < CRYPT_SESSINFO_LAST );
661 	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
662 assert( ( flags & ~( ATTR_FLAG_NONE | ATTR_FLAG_ENCODEDVALUE | ATTR_FLAG_MULTIVALUED ) ) == 0 );
663 	REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
664 
665 	/* Pre-3.3 kludge: Set the groupID to the attributeID since groups
666 	   aren't defined yet */
667 	return( addInfo( listHeadPtr, attributeID, attributeID, data,
668 					 dataLength, dataLength, NULL, flags ) );
669 	}
670 
671 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
addSessionInfoComposite(INOUT_PTR ATTRIBUTE_LIST ** listHeadPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,const ATTRACCESS_FUNCTION accessFunction,IN_BUFFER (dataLength)const void * data,IN_LENGTH_SHORT const int dataLength,IN_FLAGS (ATTR)const int flags)672 int addSessionInfoComposite( INOUT_PTR ATTRIBUTE_LIST **listHeadPtr,
673 							 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,
674 							 const ATTRACCESS_FUNCTION accessFunction,
675 							 IN_BUFFER( dataLength ) const void *data,
676 							 IN_LENGTH_SHORT const int dataLength,
677 							 IN_FLAGS( ATTR ) const int flags )
678 	{
679 	assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
680 	assert( isReadPtr( data, dataLength ) );
681 
682 	REQUIRES( attributeID > CRYPT_SESSINFO_FIRST && \
683 			  attributeID < CRYPT_SESSINFO_LAST );
684 	REQUIRES( accessFunction != NULL );
685 	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
686 assert( flags == ATTR_FLAG_MULTIVALUED || flags == ATTR_FLAG_COMPOSITE || \
687 		flags == ( ATTR_FLAG_MULTIVALUED | ATTR_FLAG_COMPOSITE ) );
688 	REQUIRES( flags > ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
689 
690 	/* For composite attributes the groupID is the attributeID, with the
691 	   actual attributeID being returned by the accessFunction */
692 	return( addInfo( listHeadPtr, attributeID, attributeID, data,
693 					 dataLength, dataLength, accessFunction, flags ) );
694 	}
695 
696 /* Update a session attribute, either by replacing an existing entry if it
697    already exists or by adding a new entry.  Since we can potentially update
698    the entry later we specify two length values, the length of the data
699    currently being added and the maximum length that this value may take in
700    the future */
701 
702 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
updateSessionInfo(INOUT_PTR ATTRIBUTE_LIST ** listHeadPtr,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,IN_BUFFER (dataLength)const void * data,IN_LENGTH_SHORT const int dataLength,IN_LENGTH_SHORT const int dataMaxLength,IN_FLAGS_Z (ATTR)const int flags)703 int updateSessionInfo( INOUT_PTR ATTRIBUTE_LIST **listHeadPtr,
704 					   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID,
705 					   IN_BUFFER( dataLength ) const void *data,
706 					   IN_LENGTH_SHORT const int dataLength,
707 					   IN_LENGTH_SHORT const int dataMaxLength,
708 					   IN_FLAGS_Z( ATTR ) const int flags )
709 	{
710 	ATTRIBUTE_LIST *attributeListPtr = *listHeadPtr;
711 
712 	assert( isWritePtr( listHeadPtr, sizeof( ATTRIBUTE_LIST * ) ) );
713 	assert( isReadPtr( data, dataLength ) );
714 
715 	REQUIRES( attributeID > CRYPT_SESSINFO_FIRST && \
716 			  attributeID < CRYPT_SESSINFO_LAST );
717 	REQUIRES( dataLength > 0 && dataLength <= dataMaxLength && \
718 			  dataLength < MAX_INTLENGTH_SHORT );
719 	REQUIRES( dataMaxLength > 0 && dataMaxLength < MAX_INTLENGTH_SHORT );
720 assert( ( flags & ~( ATTR_FLAG_NONE | ATTR_FLAG_EPHEMERAL | ATTR_FLAG_ENCODEDVALUE ) ) == 0 );
721 	REQUIRES( flags >= ATTR_FLAG_NONE && flags <= ATTR_FLAG_MAX );
722 	REQUIRES( !( flags & ATTR_FLAG_MULTIVALUED ) );
723 
724 	/* Find the first attribute of this type */
725 	attributeListPtr = attributeFind( attributeListPtr, getAttrFunction,
726 									  attributeID );
727 
728 	/* If the attribute is already present, update the value */
729 	if( attributeListPtr != NULL )
730 		{
731 		REQUIRES( attributeListPtr->attributeID == attributeID );
732 		REQUIRES( ( attributeListPtr->valueLength == 0 && \
733 					!memcmp( attributeListPtr->value, \
734 							 "\x00\x00\x00\x00", 4 ) ) || \
735 				  ( attributeListPtr->valueLength > 0 ) );
736 
737 		assert( isReadPtr( data, dataLength ) );
738 
739 		REQUIRES( dataLength <= sizeofVarStruct( attributeListPtr, \
740 												 ATTRIBUTE_LIST ) - \
741 								sizeof( ATTRIBUTE_LIST ) );
742 		zeroise( attributeListPtr->value, attributeListPtr->valueLength );
743 		memcpy( attributeListPtr->value, data, dataLength );
744 		attributeListPtr->valueLength = dataLength;
745 		return( CRYPT_OK );
746 		}
747 
748 	/* The attribute isn't already present, it's a straight add */
749 	return( addInfo( listHeadPtr, attributeID, attributeID, data,
750 					 dataLength, dataMaxLength, NULL, flags ) );
751 	}
752 
753 /****************************************************************************
754 *																			*
755 *								Delete an Attribute							*
756 *																			*
757 ****************************************************************************/
758 
759 /* Delete a complete set of session attributes */
760 
761 STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
deleteSessionInfo(INOUT ATTRIBUTE_LIST ** attributeListHead,INOUT ATTRIBUTE_LIST ** attributeListCurrent,INOUT ATTRIBUTE_LIST * attributeListPtr)762 int deleteSessionInfo( INOUT ATTRIBUTE_LIST **attributeListHead,
763 					   INOUT ATTRIBUTE_LIST **attributeListCurrent,
764 					   INOUT ATTRIBUTE_LIST *attributeListPtr )
765 	{
766 	assert( isWritePtr( attributeListHead, sizeof( ATTRIBUTE_LIST * ) ) );
767 	assert( isWritePtr( attributeListCurrent, sizeof( ATTRIBUTE_LIST * ) ) );
768 	assert( isWritePtr( attributeListPtr, sizeof( ATTRIBUTE_LIST ) ) );
769 
770 	/* If we're about to delete the attribute that's pointed to by the
771 	   current-attribute pointer, advance it to the next attribute.  If
772 	   there's no next attribute, move it to the previous attribute.  This
773 	   behaviour is the most logically consistent, it means that we can do
774 	   things like deleting an entire attribute list by repeatedly deleting
775 	   a single attribute */
776 	if( *attributeListCurrent == attributeListPtr )
777 		{
778 		*attributeListCurrent = ( attributeListPtr->next != NULL ) ? \
779 								attributeListPtr->next : \
780 								attributeListPtr->prev;
781 		}
782 
783 	/* Remove the item from the list */
784 	deleteDoubleListElement( attributeListHead, attributeListPtr );
785 
786 	/* Clear all data in the list item and free the memory */
787 	endVarStruct( attributeListPtr, ATTRIBUTE_LIST );
788 	clFree( "deleteSessionInfo", attributeListPtr );
789 
790 	return( CRYPT_OK );
791 	}
792 
793 STDC_NONNULL_ARG( ( 1, 2 ) ) \
deleteSessionInfoAll(INOUT ATTRIBUTE_LIST ** attributeListHead,INOUT ATTRIBUTE_LIST ** attributeListCurrent)794 void deleteSessionInfoAll( INOUT ATTRIBUTE_LIST **attributeListHead,
795 						   INOUT ATTRIBUTE_LIST **attributeListCurrent )
796 	{
797 	ATTRIBUTE_LIST *attributeListCursor = *attributeListHead;
798 	int iterationCount;
799 
800 	assert( isWritePtr( attributeListHead, sizeof( ATTRIBUTE_LIST * ) ) );
801 	assert( isWritePtr( attributeListCurrent, sizeof( ATTRIBUTE_LIST * ) ) );
802 
803 	/* If the list was empty, return now */
804 	if( attributeListCursor == NULL )
805 		{
806 		REQUIRES_V( *attributeListCurrent == NULL );
807 		return;
808 		}
809 
810 	/* Destroy any remaining list items */
811 	for( iterationCount = 0;
812 		 attributeListCursor != NULL && \
813 			iterationCount < FAILSAFE_ITERATIONS_MAX;
814 		 iterationCount++ )
815 		{
816 		ATTRIBUTE_LIST *itemToFree = attributeListCursor;
817 
818 		attributeListCursor = attributeListCursor->next;
819 		deleteSessionInfo( attributeListHead, attributeListCurrent,
820 						   itemToFree );
821 		}
822 	ENSURES_V( iterationCount < FAILSAFE_ITERATIONS_MAX );
823 	*attributeListCurrent = NULL;
824 
825 	ENSURES_V( *attributeListHead == NULL );
826 	}
827 #endif /* USE_SESSIONS */
828