1 /****************************************************************************
2 *																			*
3 *				cryptlib Internal Attribute-list Manipulation API			*
4 *						Copyright Peter Gutmann 1992-2008					*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "crypt.h"
10 #else
11   #include "crypt.h"
12 #endif /* Compiler-specific includes */
13 
14 /* The minimum size of an attribute-list element (in this case for
15    sessions), used for error checking in debug mode.  The values are various
16    ints and pointers, and the 'previous' and 'next' pointer for the list
17    itself */
18 
19 #define MIN_ATTRLIST_SIZE	( ( 7 * sizeof( int ) ) + \
20 							  ( 2 * sizeof( void * ) ) )
21 
22 /* Movement codes for the attribute cursor */
23 
24 typedef enum {
25 	CURSOR_MOVE_NONE,		/* No movement type */
26 	CURSOR_MOVE_START,		/* Move to first attribute */
27 	CURSOR_MOVE_PREV,		/* Move to previous attribute */
28 	CURSOR_MOVE_NEXT,		/* Move to next attribute */
29 	CURSOR_MOVE_END,		/* Move to last attribute */
30 	CURSOR_MOVE_LAST		/* Last possible move type */
31 	} CURSOR_MOVE_TYPE;
32 
33 /****************************************************************************
34 *																			*
35 *							Attribute Location Routines						*
36 *																			*
37 ****************************************************************************/
38 
39 /* Find the start and end of an attribute group from an attribute within
40    the group */
41 
42 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2 ) ) \
attributeFindStart(IN_OPT const void * attributePtr,IN GETATTR_FUNCTION getAttrFunction)43 void *attributeFindStart( IN_OPT const void *attributePtr,
44 						  IN GETATTR_FUNCTION getAttrFunction )
45 	{
46 	CRYPT_ATTRIBUTE_TYPE groupID;
47 	int iterationCount;
48 
49 	assert( attributePtr == NULL || \
50 			isReadPtr( attributePtr, MIN_ATTRLIST_SIZE ) );
51 
52 	REQUIRES_N( getAttrFunction != NULL );
53 
54 	if( attributePtr == NULL )
55 		return( NULL );
56 
57 	/* Move backwards until we find the start of the attribute */
58 	if( getAttrFunction( attributePtr, &groupID, NULL, NULL,
59 						 ATTR_CURRENT ) == NULL )
60 		return( NULL );
61 	ENSURES_N( groupID != CRYPT_ATTRIBUTE_NONE );
62 	for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_MAX;
63 		 iterationCount++ )
64 		{
65 		CRYPT_ATTRIBUTE_TYPE prevGroupID;
66 		const void *prevPtr;
67 
68 		prevPtr = getAttrFunction( attributePtr, &prevGroupID, NULL, NULL,
69 								   ATTR_PREV );
70 		if( prevPtr == NULL || prevGroupID != groupID )
71 			{
72 			/* We've reached the start of the list or a different attribute
73 			   group, this is the start of the current group */
74 			break;
75 			}
76 		attributePtr = prevPtr;
77 		}
78 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
79 
80 	return( ( void * ) attributePtr );
81 	}
82 
83 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2 ) ) \
attributeFindEnd(IN_OPT const void * attributePtr,IN GETATTR_FUNCTION getAttrFunction)84 void *attributeFindEnd( IN_OPT const void *attributePtr,
85 						IN GETATTR_FUNCTION getAttrFunction )
86 	{
87 	CRYPT_ATTRIBUTE_TYPE groupID;
88 	int iterationCount;
89 
90 	assert( attributePtr == NULL || \
91 			isReadPtr( attributePtr, MIN_ATTRLIST_SIZE ) );
92 
93 	REQUIRES_N( getAttrFunction != NULL );
94 
95 	if( attributePtr == NULL )
96 		return( NULL );
97 
98 	/* Move forwards until we're just before the start of the next
99 	   attribute */
100 	if( getAttrFunction( attributePtr, &groupID, NULL, NULL,
101 						 ATTR_CURRENT ) == NULL )
102 		return( NULL );
103 	ENSURES_N( groupID != CRYPT_ATTRIBUTE_NONE );
104 	for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_MAX;
105 		 iterationCount++ )
106 		{
107 		CRYPT_ATTRIBUTE_TYPE nextGroupID;
108 		const void *nextPtr;
109 
110 		nextPtr = getAttrFunction( attributePtr, &nextGroupID, NULL, NULL,
111 								   ATTR_NEXT );
112 		if( nextPtr == NULL || nextGroupID != groupID )
113 			{
114 			/* We've reached the end of the list or a different attribute
115 			   group, this is the end of the current group */
116 			break;
117 			}
118 		attributePtr = nextPtr;
119 		}
120 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
121 
122 	return( ( void * ) attributePtr );
123 	}
124 
125 /* Find an attribute in a list of attributes */
126 
127 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2 ) ) \
attributeFind(IN_OPT const void * attributePtr,IN GETATTR_FUNCTION getAttrFunction,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID)128 void *attributeFind( IN_OPT const void *attributePtr,
129 					 IN GETATTR_FUNCTION getAttrFunction,
130 					 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeID )
131 	{
132 	CRYPT_ATTRIBUTE_TYPE currAttributeID;
133 	int iterationCount;
134 
135 	assert( attributePtr == NULL || \
136 			isReadPtr( attributePtr, MIN_ATTRLIST_SIZE ) );
137 
138 	REQUIRES_N( getAttrFunction != NULL );
139 	REQUIRES_N( isAttribute( attributeID ) || \
140 				isInternalAttribute( attributeID ) );
141 
142 	if( attributePtr == NULL )
143 		return( NULL );
144 
145 	/* Find the attribute in the list */
146 	if( getAttrFunction( attributePtr, NULL, &currAttributeID, NULL,
147 						 ATTR_CURRENT ) == NULL )
148 		return( NULL );
149 	ENSURES_N( currAttributeID != CRYPT_ATTRIBUTE_NONE );
150 	for( iterationCount = 0;
151 		 attributePtr != NULL && currAttributeID != attributeID && \
152 			iterationCount < FAILSAFE_ITERATIONS_MAX;
153 		 iterationCount++ )
154 		{
155 		attributePtr = getAttrFunction( attributePtr, NULL,
156 										&currAttributeID, NULL,
157 										ATTR_NEXT );
158 		}
159 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
160 
161 	return( ( void * ) attributePtr );
162 	}
163 
164 /* An extended form of the standard find-attribute function that searches
165    either by attribute group or by attribute + attribute-instance (the
166    case of search-by-attribute is handled through findAttribute(), and the
167    other combinations aren't valid) */
168 
169 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2 ) ) \
attributeFindEx(IN_OPT const void * attributePtr,IN GETATTR_FUNCTION getAttrFunction,IN_ENUM_OPT (CRYPT_ATTRIBUTE)const CRYPT_ATTRIBUTE_TYPE groupID,IN_ENUM_OPT (CRYPT_ATTRIBUTE)const CRYPT_ATTRIBUTE_TYPE attributeID,IN_ENUM_OPT (CRYPT_ATTRIBUTE)const CRYPT_ATTRIBUTE_TYPE instanceID)170 void *attributeFindEx( IN_OPT const void *attributePtr,
171 					   IN GETATTR_FUNCTION getAttrFunction,
172 					   IN_ENUM_OPT( CRYPT_ATTRIBUTE ) \
173 							const CRYPT_ATTRIBUTE_TYPE groupID,
174 					   IN_ENUM_OPT( CRYPT_ATTRIBUTE ) \
175 							const CRYPT_ATTRIBUTE_TYPE attributeID,
176 					   IN_ENUM_OPT( CRYPT_ATTRIBUTE ) \
177 							const CRYPT_ATTRIBUTE_TYPE instanceID )
178 	{
179 	CRYPT_ATTRIBUTE_TYPE currAttributeID, currInstanceID;
180 	int iterationCount;
181 
182 	assert( attributePtr == NULL || \
183 			isReadPtr( attributePtr, MIN_ATTRLIST_SIZE ) );
184 
185 	REQUIRES_N( getAttrFunction != NULL );
186 	REQUIRES_N( groupID == CRYPT_ATTRIBUTE_NONE || \
187 				isAttribute( groupID ) || \
188 				isInternalAttribute( groupID ) );
189 	REQUIRES_N( attributeID == CRYPT_ATTRIBUTE_NONE || \
190 				isAttribute( attributeID ) || \
191 				isInternalAttribute( attributeID ) );
192 	REQUIRES_N( instanceID == CRYPT_ATTRIBUTE_NONE || \
193 				isAttribute( instanceID ) || \
194 				isInternalAttribute( instanceID ) );
195 	REQUIRES_N( ( groupID != CRYPT_ATTRIBUTE_NONE && \
196 				  attributeID == CRYPT_ATTRIBUTE_NONE && \
197 				  instanceID == CRYPT_ATTRIBUTE_NONE ) || \
198 				( groupID == CRYPT_ATTRIBUTE_NONE && \
199 				  attributeID != CRYPT_ATTRIBUTE_NONE && \
200 				  instanceID != CRYPT_ATTRIBUTE_NONE ) );
201 
202 	if( attributePtr == NULL )
203 		return( NULL );
204 
205 	/* Find the attribute group if required */
206 	if( groupID != CRYPT_ATTRIBUTE_NONE )
207 		{
208 		CRYPT_ATTRIBUTE_TYPE currGroupID;
209 
210 		if( getAttrFunction( attributePtr, &currGroupID, NULL, NULL,
211 							 ATTR_CURRENT ) == NULL )
212 			return( NULL );
213 		ENSURES_N( currGroupID != CRYPT_ATTRIBUTE_NONE );
214 		for( iterationCount = 0;
215 			 attributePtr != NULL && currGroupID != groupID && \
216 				iterationCount < FAILSAFE_ITERATIONS_MAX;
217 			 iterationCount++ )
218 			{
219 			attributePtr = getAttrFunction( attributePtr, &currGroupID,
220 											NULL, NULL, ATTR_NEXT );
221 			}
222 		ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
223 
224 		return( ( void * ) attributePtr );
225 		}
226 
227 	/* Find the attribute */
228 	if( getAttrFunction( attributePtr, NULL, &currAttributeID, NULL,
229 						 ATTR_CURRENT ) == NULL )
230 		return( NULL );
231 	ENSURES_N( currAttributeID != CRYPT_ATTRIBUTE_NONE );
232 	for( iterationCount = 0;
233 		 attributePtr != NULL && currAttributeID != attributeID && \
234 			iterationCount < FAILSAFE_ITERATIONS_MAX;
235 		 iterationCount++ )
236 		{
237 		attributePtr = getAttrFunction( attributePtr, NULL,
238 										&currAttributeID, NULL,
239 										ATTR_NEXT );
240 		}
241 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
242 	if( attributePtr == NULL )
243 		return( NULL );
244 
245 	/* Find the attribute instance */
246 	if( getAttrFunction( attributePtr, NULL, &currAttributeID,
247 						 &currInstanceID, ATTR_CURRENT ) == NULL )
248 		return( NULL );
249 	ENSURES_N( currAttributeID != CRYPT_ATTRIBUTE_NONE );
250 	for( iterationCount = 0;
251 		 attributePtr != NULL && currAttributeID == attributeID && \
252 			iterationCount < FAILSAFE_ITERATIONS_MAX;
253 		 iterationCount++ )
254 		{
255 		if( currInstanceID == instanceID )
256 			return( ( void * ) attributePtr );
257 		attributePtr = getAttrFunction( attributePtr, NULL,
258 										&currAttributeID, &currInstanceID,
259 										ATTR_NEXT );
260 		}
261 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
262 
263 	return( NULL );
264 	}
265 
266 /* Find the next instance of an attribute in an attribute group.  This is
267    used to step through multiple instances of an attribute, for example in
268    a cert extension containing a SEQUENCE OF <attribute> */
269 
270 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2 ) ) \
attributeFindNextInstance(IN_OPT const void * attributePtr,IN GETATTR_FUNCTION getAttrFunction)271 void *attributeFindNextInstance( IN_OPT const void *attributePtr,
272 								 IN GETATTR_FUNCTION getAttrFunction )
273 	{
274 	CRYPT_ATTRIBUTE_TYPE groupID, attributeID;
275 	CRYPT_ATTRIBUTE_TYPE currGroupID, currAttributeID;
276 	int iterationCount;
277 
278 	assert( attributePtr == NULL || \
279 			isReadPtr( attributePtr, MIN_ATTRLIST_SIZE ) );
280 
281 	REQUIRES_N( getAttrFunction != NULL );
282 
283 	if( attributePtr == NULL )
284 		return( NULL );
285 
286 	/* Skip the current field */
287 	if( getAttrFunction( attributePtr, &groupID, &attributeID, NULL,
288 						 ATTR_CURRENT ) == NULL )
289 		return( NULL );
290 	ENSURES_N( groupID != CRYPT_ATTRIBUTE_NONE && \
291 			   attributeID != CRYPT_ATTRIBUTE_NONE );
292 	attributePtr = getAttrFunction( attributePtr, &currGroupID,
293 									&currAttributeID, NULL, ATTR_NEXT );
294 	if( attributePtr == NULL )
295 		{
296 		/* No next attribute, we're done */
297 		return( NULL );
298 		}
299 	ENSURES_N( currGroupID != CRYPT_ATTRIBUTE_NONE );
300 
301 	/* Step through the remaining attributes in the group looking for
302 	   another occurrence of the current attribute */
303 	for( iterationCount = 0; \
304 		 attributePtr != NULL && currGroupID == groupID && \
305 			iterationCount < FAILSAFE_ITERATIONS_MAX;
306 		 iterationCount++ )
307 		{
308 		if( currAttributeID == attributeID )
309 			return( ( void * ) attributePtr );
310 		attributePtr = getAttrFunction( attributePtr, &currGroupID,
311 										&currAttributeID, NULL,
312 										ATTR_NEXT );
313 		}
314 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
315 
316 	/* We couldn't find another instance of the attribute in this group */
317 	return( NULL );
318 	}
319 
320 /****************************************************************************
321 *																			*
322 *						Attribute Cursor Movement Routines					*
323 *																			*
324 ****************************************************************************/
325 
326 /* Moving the cursor by attribute group is a bit more complex than just
327    stepping forwards or backwards along the attribute list.  First we have
328    to find the start or end of the current group.  Then we move to the start
329    of the previous (via ATTR_PREV and attributeFindStart()), or start of the
330    next (via ATTR_NEXT) group beyond that.  This has the effect of moving us
331    from anywhere in the current group to the start of the preceding or
332    following group.  Finally, we repeat this as required */
333 
334 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1, 2 ) ) \
moveCursorByGroup(const void * currentCursor,IN GETATTR_FUNCTION getAttrFunction,IN_ENUM (CURSOR_MOVE)const CURSOR_MOVE_TYPE cursorMoveType,IN_INT int count,const BOOLEAN absMove)335 static const void *moveCursorByGroup( const void *currentCursor,
336 									  IN GETATTR_FUNCTION getAttrFunction,
337 									  IN_ENUM( CURSOR_MOVE ) \
338 										const CURSOR_MOVE_TYPE cursorMoveType,
339 									  IN_INT int count,
340 									  const BOOLEAN absMove )
341 	{
342 	const void *newCursor = currentCursor, *lastCursor = NULL;
343 	int iterationCount;
344 
345 	assert( isReadPtr( currentCursor, MIN_ATTRLIST_SIZE ) );
346 
347 	REQUIRES_N( getAttrFunction != NULL );
348 	REQUIRES_N( cursorMoveType > CURSOR_MOVE_NONE && \
349 				cursorMoveType < CURSOR_MOVE_LAST );
350 	REQUIRES_N( count > 0 && count <= MAX_INTLENGTH );
351 
352 	for( iterationCount = 0; \
353 		 count-- > 0 && newCursor != NULL && \
354 			iterationCount < FAILSAFE_ITERATIONS_MAX;
355 		 iterationCount++ )
356 		{
357 		lastCursor = newCursor;
358 		if( cursorMoveType == CURSOR_MOVE_START || \
359 			cursorMoveType == CURSOR_MOVE_PREV )
360 			{
361 			/* Move from the start of the current group to the start of the
362 			   preceding group */
363 			newCursor = attributeFindStart( newCursor, getAttrFunction );
364 			if( newCursor != NULL )
365 				newCursor = getAttrFunction( newCursor, NULL, NULL, NULL,
366 											 ATTR_PREV );
367 			if( newCursor != NULL )
368 				newCursor = attributeFindStart( newCursor, getAttrFunction );
369 			}
370 		else
371 			{
372 			REQUIRES_N( cursorMoveType == CURSOR_MOVE_NEXT || \
373 						cursorMoveType == CURSOR_MOVE_END );
374 
375 			/* Move from the end of the current group to the start of the
376 			   next group */
377 			newCursor = attributeFindEnd( newCursor, getAttrFunction );
378 			if( newCursor != NULL )
379 				newCursor = getAttrFunction( newCursor, NULL, NULL, NULL,
380 											 ATTR_NEXT );
381 			}
382 		}
383 	ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
384 	ENSURES_N( lastCursor != NULL );	/* We went through the loop at least once */
385 
386 	/* If the new cursor is NULL, we've reached the start or end of the
387 	   attribute list */
388 	if( newCursor == NULL )
389 		{
390 		/* If it's an absolute move we've reached our destination, otherwise
391 		   there's nowhere left to move to.  We move to the start of the
392 		   first or last attribute that we got to before we ran out of
393 		   attributes to make sure that we don't fall off the start/end of
394 		   the list */
395 		return( absMove ? \
396 				attributeFindStart( lastCursor, getAttrFunction ) : NULL );
397 		}
398 
399 	/* We've found what we were looking for */
400 	return( newCursor );
401 	}
402 
403 /* Moving by attribute or attribute instance is rather simpler than moving by
404    group.  For attributes we move backwards or forwards until we either run
405    out of attributes or the next attribute belongs to a different group.  For
406    attribute instances we move similarly, except that we stop when we reach
407    an attribute whose group type, attribute type, and instance type don't
408    match the current one.  We have to explicitly keep track of whether the
409    cursor was successfully moved rather than checking that its value has
410    changed because some object types implement composite attributes that
411    maintain an attribute-internal virtual cursor, which can return the same
412    attribute pointer multiple times if the move is internal to the
413    (composite) attribute */
414 
415 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1, 2 ) ) \
moveCursorByAttribute(const void * currentCursor,GETATTR_FUNCTION getAttrFunction,IN_ENUM (CURSOR_MOVE)const CURSOR_MOVE_TYPE cursorMoveType,IN_INT int count,const BOOLEAN absMove)416 static const void *moveCursorByAttribute( const void *currentCursor,
417 										  GETATTR_FUNCTION getAttrFunction,
418 										  IN_ENUM( CURSOR_MOVE ) \
419 											const CURSOR_MOVE_TYPE cursorMoveType,
420 										  IN_INT int count,
421 										  const BOOLEAN absMove )
422 	{
423 	CRYPT_ATTRIBUTE_TYPE groupID;
424 	BOOLEAN cursorMoved = FALSE;
425 	const void *newCursor = currentCursor;
426 	int iterationCount;
427 
428 	assert( isReadPtr( currentCursor, MIN_ATTRLIST_SIZE ) );
429 
430 	REQUIRES_N( getAttrFunction != NULL );
431 	REQUIRES_N( cursorMoveType > CURSOR_MOVE_NONE && \
432 				cursorMoveType < CURSOR_MOVE_LAST );
433 	REQUIRES_N( count > 0 && count <= MAX_INTLENGTH );
434 
435 	if( getAttrFunction( currentCursor, &groupID, NULL, NULL,
436 						 ATTR_CURRENT ) == NULL )
437 		return( NULL );
438 	ENSURES_N( groupID != CRYPT_ATTRIBUTE_NONE );
439 	if( cursorMoveType == CURSOR_MOVE_START || \
440 		cursorMoveType == CURSOR_MOVE_PREV )
441 		{
442 		CRYPT_ATTRIBUTE_TYPE prevGroupID;
443 		const void *prevCursor;
444 
445 		prevCursor = getAttrFunction( newCursor, &prevGroupID, NULL,
446 									  NULL, ATTR_PREV );
447 		for( iterationCount = 0; \
448 			 count-- > 0 && prevCursor != NULL && prevGroupID == groupID && \
449 				iterationCount < FAILSAFE_ITERATIONS_MAX;
450 			 iterationCount++ )
451 			{
452 			newCursor = prevCursor;
453 			prevCursor = getAttrFunction( newCursor, &prevGroupID, NULL,
454 										  NULL, ATTR_PREV );
455 			cursorMoved = TRUE;
456 			}
457 		ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
458 		}
459 	else
460 		{
461 		CRYPT_ATTRIBUTE_TYPE nextGroupID;
462 		const void *nextCursor;
463 
464 		REQUIRES_N( cursorMoveType == CURSOR_MOVE_NEXT || \
465 					cursorMoveType == CURSOR_MOVE_END );
466 
467 		nextCursor = getAttrFunction( newCursor, &nextGroupID, NULL,
468 									  NULL, ATTR_NEXT );
469 		for( iterationCount = 0; \
470 			 count-- > 0 && nextCursor != NULL && nextGroupID == groupID && \
471 				iterationCount < FAILSAFE_ITERATIONS_MAX;
472 			 iterationCount++ )
473 			{
474 			newCursor = nextCursor;
475 			nextCursor = getAttrFunction( newCursor, &nextGroupID, NULL,
476 										  NULL, ATTR_NEXT );
477 			cursorMoved = TRUE;
478 			}
479 		ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
480 		}
481 
482 	if( !absMove && !cursorMoved )
483 		return( NULL );
484 	return( newCursor );
485 	}
486 
487 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1, 2 ) ) \
moveCursorByInstance(const void * currentCursor,GETATTR_FUNCTION getAttrFunction,IN_ENUM (CURSOR_MOVE)const CURSOR_MOVE_TYPE cursorMoveType,IN_INT int count,const BOOLEAN absMove)488 static const void *moveCursorByInstance( const void *currentCursor,
489 										 GETATTR_FUNCTION getAttrFunction,
490 										 IN_ENUM( CURSOR_MOVE ) \
491 											const CURSOR_MOVE_TYPE cursorMoveType,
492 										 IN_INT int count,
493 										 const BOOLEAN absMove )
494 	{
495 	CRYPT_ATTRIBUTE_TYPE groupID, attributeID, instanceID;
496 	BOOLEAN cursorMoved = FALSE;
497 	const void *newCursor = currentCursor;
498 	int iterationCount;
499 
500 	assert( isReadPtr( currentCursor, MIN_ATTRLIST_SIZE ) );
501 
502 	REQUIRES_N( getAttrFunction != NULL );
503 	REQUIRES_N( cursorMoveType > CURSOR_MOVE_NONE && \
504 				cursorMoveType < CURSOR_MOVE_LAST );
505 	REQUIRES_N( count > 0 && count <= MAX_INTLENGTH );
506 
507 	if( getAttrFunction( currentCursor, &groupID, &attributeID,
508 						 &instanceID, ATTR_CURRENT ) == NULL )
509 		return( NULL );
510 	ENSURES_N( groupID != CRYPT_ATTRIBUTE_NONE && \
511 			   attributeID != CRYPT_ATTRIBUTE_NONE );
512 	if( cursorMoveType == CURSOR_MOVE_START || \
513 		cursorMoveType == CURSOR_MOVE_PREV )
514 		{
515 		CRYPT_ATTRIBUTE_TYPE prevGroupID, prevAttrID, prevInstID;
516 		const void *prevCursor;
517 
518 		prevCursor = getAttrFunction( newCursor, &prevGroupID,
519 									  &prevAttrID, &prevInstID,
520 									  ATTR_PREV );
521 		for( iterationCount = 0; \
522 			 count-- > 0 && prevCursor != NULL && prevGroupID == groupID && \
523 				prevAttrID == attributeID && prevInstID == instanceID && \
524 				iterationCount < FAILSAFE_ITERATIONS_MAX;
525 			 iterationCount++ )
526 			{
527 			newCursor = prevCursor;
528 			prevCursor = getAttrFunction( newCursor, &prevGroupID,
529 										  &prevAttrID, &prevInstID,
530 										  ATTR_PREV );
531 			cursorMoved = TRUE;
532 			}
533 		ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
534 		}
535 	else
536 		{
537 		CRYPT_ATTRIBUTE_TYPE nextGroupID, nextAttrID, nextInstID;
538 		const void *nextCursor;
539 
540 		REQUIRES_N( cursorMoveType == CURSOR_MOVE_NEXT || \
541 					cursorMoveType == CURSOR_MOVE_END );
542 
543 		nextCursor = getAttrFunction( newCursor, &nextGroupID,
544 									  &nextAttrID, &nextInstID,
545 									  ATTR_NEXT );
546 		for( iterationCount = 0; \
547 			 count-- > 0 && nextCursor != NULL && nextGroupID == groupID && \
548 				nextAttrID == attributeID && nextInstID == instanceID && \
549 				iterationCount < FAILSAFE_ITERATIONS_MAX;
550 			 iterationCount++ )
551 			{
552 			newCursor = nextCursor;
553 			nextCursor = getAttrFunction( newCursor, &nextGroupID,
554 										  &nextAttrID, &nextInstID,
555 										  ATTR_NEXT );
556 			cursorMoved = TRUE;
557 			}
558 		ENSURES_N( iterationCount < FAILSAFE_ITERATIONS_MAX );
559 		}
560 
561 	if( !absMove && !cursorMoved )
562 		return( NULL );
563 	return( newCursor );
564 	}
565 
566 /* Move the attribute cursor relative to the current cursor position */
567 
568 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 2 ) )\
attributeMoveCursor(IN_OPT const void * currentCursor,IN GETATTR_FUNCTION getAttrFunction,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeMoveType,IN_RANGE (CRYPT_CURSOR_LAST,CRYPT_CURSOR_FIRST)const int cursorMoveType)569 const void *attributeMoveCursor( IN_OPT const void *currentCursor,
570 								 IN GETATTR_FUNCTION getAttrFunction,
571 								 IN_ATTRIBUTE \
572 									const CRYPT_ATTRIBUTE_TYPE attributeMoveType,
573 								 IN_RANGE( CRYPT_CURSOR_LAST, \
574 										   CRYPT_CURSOR_FIRST ) /* Values are -ve */
575 									const int cursorMoveType )
576 	{
577 	typedef struct {
578 		const int moveCode;
579 		const CURSOR_MOVE_TYPE cursorMoveType;
580 		} MOVECODE_MAP_INFO;
581 	static const MOVECODE_MAP_INFO moveCodeMap[] = {
582 		{ CRYPT_CURSOR_FIRST, CURSOR_MOVE_START },
583 		{ CRYPT_CURSOR_PREVIOUS, CURSOR_MOVE_PREV },
584 		{ CRYPT_CURSOR_NEXT, CURSOR_MOVE_NEXT },
585 		{ CRYPT_CURSOR_LAST, CURSOR_MOVE_END },
586 		{ 0, CURSOR_MOVE_NONE }, { 0, CURSOR_MOVE_NONE }
587 		};
588 	const BOOLEAN absMove = ( cursorMoveType == CRYPT_CURSOR_FIRST || \
589 							  cursorMoveType == CRYPT_CURSOR_LAST ) ? \
590 							TRUE : FALSE;
591 	CURSOR_MOVE_TYPE moveType;
592 	int count, i;
593 
594 	assert( currentCursor == NULL || \
595 			isReadPtr( currentCursor, MIN_ATTRLIST_SIZE ) );
596 
597 	REQUIRES_N( getAttrFunction != NULL );
598 	REQUIRES_N( attributeMoveType == CRYPT_ATTRIBUTE_CURRENT_GROUP || \
599 				attributeMoveType == CRYPT_ATTRIBUTE_CURRENT || \
600 				attributeMoveType == CRYPT_ATTRIBUTE_CURRENT_INSTANCE );
601 	REQUIRES_N( cursorMoveType >= CRYPT_CURSOR_LAST && \
602 				cursorMoveType <= CRYPT_CURSOR_FIRST );	/* Values are -ve */
603 
604 	/* Positioning in null attribute lists is always unsuccessful */
605 	if( currentCursor == NULL )
606 		return( NULL );
607 
608 	/* Convert the move type into a more logical cursor move code.  We can't
609 	   use mapValue() for this because the move-type values overlap with the
610 	   end-of-table marker value expected by mapValue() */
611 	for( i = 0;
612 		 moveCodeMap[ i ].moveCode != cursorMoveType && \
613 			moveCodeMap[ i ].moveCode != 0 && \
614 			i < FAILSAFE_ARRAYSIZE( moveCodeMap, MOVECODE_MAP_INFO );
615 		 i++ );
616 	ENSURES_N( i < FAILSAFE_ARRAYSIZE( moveCodeMap, MOVECODE_MAP_INFO ) );
617 	ENSURES_N( moveCodeMap[ i ].moveCode != 0 );
618 	moveType = moveCodeMap[ i ].cursorMoveType;
619 
620 	/* Set the amount that we want to move by based on the position code.
621 	   This means that we can handle the movement in a simple while loop
622 	   instead of having to special-case it for moves by one item */
623 	count = absMove ? MAX_INTLENGTH : 1;
624 
625 	/* Perform the appropriate attribute move type */
626 	switch( attributeMoveType )
627 		{
628 		case CRYPT_ATTRIBUTE_CURRENT_GROUP:
629 			return( moveCursorByGroup( currentCursor, getAttrFunction,
630 									   moveType, count, absMove ) );
631 
632 		case CRYPT_ATTRIBUTE_CURRENT:
633 			return( moveCursorByAttribute( currentCursor, getAttrFunction,
634 										   moveType, count, absMove ) );
635 
636 		case CRYPT_ATTRIBUTE_CURRENT_INSTANCE:
637 			return( moveCursorByInstance( currentCursor, getAttrFunction,
638 										  moveType, count, absMove ) );
639 		}
640 
641 	/* Everything else is an error */
642 	retIntError_Null();
643 	}
644