1 /****************************************************************************
2 *																			*
3 *						Certificate DN Read/Write Routines					*
4 *						Copyright Peter Gutmann 1996-2013					*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "cert.h"
10   #include "dn.h"
11 #else
12   #include "cert/cert.h"
13   #include "cert/dn.h"
14 #endif /* Compiler-specific includes */
15 
16 #ifdef USE_CERTIFICATES
17 
18 /****************************************************************************
19 *																			*
20 *									Read a DN								*
21 *																			*
22 ****************************************************************************/
23 
24 /* Parse an AVA.  This determines the AVA type and leaves the stream pointer
25    at the start of the data value */
26 
27 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
readAVABitstring(INOUT STREAM * stream,OUT_LENGTH_SHORT_Z int * length,OUT_TAG_ENCODED_Z int * stringTag)28 static int readAVABitstring( INOUT STREAM *stream,
29 							 OUT_LENGTH_SHORT_Z int *length,
30 							 OUT_TAG_ENCODED_Z int *stringTag )
31 	{
32 	long streamPos;
33 	int bitStringLength, innerTag, innerLength DUMMY_INIT, status;
34 
35 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
36 	assert( isWritePtr( length, sizeof( int ) ) );
37 	assert( isWritePtr( stringTag, sizeof( int ) ) );
38 
39 	/* Bitstrings are used for uniqueIdentifiers, however these usually
40 	   encapsulate something else:
41 
42 		BIT STRING {
43 			IA5String 'xxxxx'
44 			}
45 
46 	   so we try and dig one level deeper to find the encapsulated string if
47 	   there is one.  This gets a bit complicated because we have to
48 	   speculatively try and decode the inner content and if that fails
49 	   assume that it's raw bitstring data.  First we read the bitstring
50 	   wrapper and remember where the bitstring data starts */
51 	status = readBitStringHole( stream, &bitStringLength, 2, DEFAULT_TAG );
52 	if( cryptStatusError( status ) )
53 		return( status );
54 	streamPos = stell( stream );
55 
56 	/* Then we try and read any inner content */
57 	status = innerTag = peekTag( stream );
58 	if( !cryptStatusError( status ) )
59 		status = readGenericHole( stream, &innerLength, 1, innerTag );
60 	if( !cryptStatusError( status ) && \
61 		bitStringLength == sizeofObject( innerLength ) )
62 		{
63 		/* There was inner content present, treat it as the actual type and
64 		   value of the bitstring.  This assumes that the inner content is
65 		   a string data type, which always seems to be the case (in any
66 		   event it's not certain what we should be returning to the user if
67 		   we find, for example, a SEQUENCE with further encapsulated
68 		   content at this point) */
69 		*stringTag = innerTag;
70 		*length = innerLength;
71 
72 		return( CRYPT_OK );
73 		}
74 
75 	/* We couldn't identify any (obvious) inner content, it must be raw
76 	   bitstring data.  Unfortunately we have no idea what format this is
77 	   in, it could in fact really be raw binary data but never actually
78 	   seems to be this, it's usually ASCII text so we mark it as such and
79 	   let the string-read routines sort it out */
80 	sClearError( stream );
81 	sseek( stream, streamPos );
82 	*stringTag = BER_STRING_IA5;
83 	*length = bitStringLength;
84 
85 	return( CRYPT_OK );
86 	}
87 
88 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \
readAVA(INOUT STREAM * stream,OUT_INT_Z int * type,OUT_LENGTH_SHORT_Z int * length,OUT_TAG_ENCODED_Z int * stringTag)89 static int readAVA( INOUT STREAM *stream,
90 					OUT_INT_Z int *type,
91 					OUT_LENGTH_SHORT_Z int *length,
92 					OUT_TAG_ENCODED_Z int *stringTag )
93 	{
94 	const DN_COMPONENT_INFO *dnComponentInfo;
95 	BYTE oid[ MAX_OID_SIZE + 8 ];
96 	int oidLength, tag, status;
97 
98 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
99 	assert( isWritePtr( type, sizeof( int ) ) );
100 	assert( isWritePtr( length, sizeof( int ) ) );
101 	assert( isWritePtr( stringTag, sizeof( int ) ) );
102 
103 	/* Clear return values */
104 	*type = 0;
105 	*length = 0;
106 	*stringTag = 0;
107 
108 	/* Read the start of the AVA and determine the type from the
109 	   AttributeType field.  If we find something that we don't recognise we
110 	   indicate it as a non-component type that can be read or written but
111 	   not directly accessed by the user (although it can still be accessed
112 	   using the cursor functions) */
113 	readSequence( stream, NULL );
114 	status = readEncodedOID( stream, oid, MAX_OID_SIZE, &oidLength,
115 							 BER_OBJECT_IDENTIFIER );
116 	if( cryptStatusError( status ) )
117 		return( status );
118 	dnComponentInfo = findDNInfoByOID( oid, oidLength );
119 	if( dnComponentInfo == NULL )
120 		{
121 		/* If we don't recognise the component type at all, skip it */
122 		status = readUniversal( stream );
123 		return( cryptStatusError( status ) ? status : OK_SPECIAL );
124 		}
125 	*type = dnComponentInfo->type;
126 
127 	/* We've reached the data value, make sure that it's in order.  When we
128 	   read the wrapper around the string type with readGenericHole() we have
129 	   to allow a minimum length of zero instead of one because of broken
130 	   AVAs with zero-length strings */
131 	status = tag = peekTag( stream );
132 	if( cryptStatusError( status ) )
133 		return( status );
134 	if( tag == BER_BITSTRING )
135 		return( readAVABitstring( stream, length, stringTag ) );
136 	*stringTag = tag;
137 	return( readGenericHoleZ( stream, length, 0, tag ) );
138 	}
139 
140 /* Read an RDN/DN component */
141 
142 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
readRDNcomponent(INOUT STREAM * stream,INOUT DN_COMPONENT ** dnComponentListPtrPtr,IN_LENGTH_SHORT const int rdnDataLeft)143 static int readRDNcomponent( INOUT STREAM *stream,
144 							 INOUT DN_COMPONENT **dnComponentListPtrPtr,
145 							 IN_LENGTH_SHORT const int rdnDataLeft )
146 	{
147 	CRYPT_ERRTYPE_TYPE dummy;
148 	BYTE stringBuffer[ MAX_ATTRIBUTE_SIZE + 8 ];
149 	void *value;
150 	const int rdnStart = stell( stream );
151 	int type, valueLength, valueStringType, stringTag;
152 	int flags = DN_FLAG_NOCHECK, status;
153 
154 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
155 	assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) );
156 
157 	REQUIRES( rdnDataLeft > 0 && rdnDataLeft < MAX_INTLENGTH_SHORT );
158 	REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT );
159 
160 	/* Read the type information for this AVA */
161 	status = readAVA( stream, &type, &valueLength, &stringTag );
162 	if( cryptStatusError( status ) )
163 		{
164 		/* If this is an unrecognised AVA, don't try and process it (the
165 		   content will already have been skipped in readAVA()) */
166 		if( status == OK_SPECIAL )
167 			return( CRYPT_OK );
168 
169 		return( status );
170 		}
171 
172 	/* Make sure that the string is a valid type for a DirectoryString.  We
173 	   don't allow Universal strings since no-one in their right mind uses
174 	   those.
175 
176 	   Alongside DirectoryString values we also allow IA5Strings, from the
177 	   practice of stuffing email addresses into DNs */
178 	if( stringTag != BER_STRING_PRINTABLE && stringTag != BER_STRING_T61 && \
179 		stringTag != BER_STRING_BMP && stringTag != BER_STRING_UTF8 && \
180 		stringTag != BER_STRING_IA5 )
181 		return( CRYPT_ERROR_BADDATA );
182 
183 	/* Skip broken AVAs with zero-length strings */
184 	if( valueLength <= 0 )
185 		return( CRYPT_OK );
186 
187 	/* Record the string contents, avoiding moving it into a buffer */
188 	status = sMemGetDataBlock( stream, &value, valueLength );
189 	if( cryptStatusOK( status ) )
190 		status = sSkip( stream, valueLength, MAX_INTLENGTH_SHORT );
191 	if( cryptStatusError( status ) )
192 		return( status );
193 	ANALYSER_HINT( value != NULL );
194 
195 	/* If there's room for another AVA, mark this one as being continued.  The
196 	   +10 value is the minimum length for an AVA: SEQUENCE { OID, value }
197 	   (2-bytes SEQUENCE + 5 bytes OID + 2 bytes (tag + length) + 1 byte min-
198 	   length data).  We don't do a simple =/!= check to get around incorrectly
199 	   encoded lengths */
200 	if( rdnDataLeft >= ( stell( stream ) - rdnStart ) + 10 )
201 		flags |= DN_FLAG_CONTINUED;
202 
203 	/* Convert the string into the local character set */
204 	status = copyFromAsn1String( stringBuffer, MAX_ATTRIBUTE_SIZE,
205 								 &valueLength, &valueStringType, value,
206 								 valueLength, stringTag );
207 	if( cryptStatusError( status ) )
208 		return( status );
209 
210 	/* Add the DN component to the DN.  If we hit a non-memory related error
211 	   we turn it into a generic CRYPT_ERROR_BADDATA error since the other
212 	   codes are somewhat too specific for this case, something like
213 	   CRYPT_ERROR_INITED or an arg error isn't too useful for the caller */
214 	status = insertDNstring( dnComponentListPtrPtr, type, stringBuffer,
215 							 valueLength, valueStringType, flags, &dummy );
216 	return( ( cryptStatusError( status ) && status != CRYPT_ERROR_MEMORY ) ? \
217 			CRYPT_ERROR_BADDATA : status );
218 	}
219 
220 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
readDNComponent(INOUT STREAM * stream,INOUT DN_COMPONENT ** dnComponentListPtrPtr)221 static int readDNComponent( INOUT STREAM *stream,
222 							INOUT DN_COMPONENT **dnComponentListPtrPtr )
223 	{
224 	int rdnLength, iterationCount, status;
225 
226 	/* Read the start of the RDN */
227 	status = readSet( stream, &rdnLength );
228 	if( cryptStatusError( status ) )
229 		return( status );
230 
231 	/* Read each RDN component */
232 	for( iterationCount = 0;
233 		 rdnLength > 0 && iterationCount < FAILSAFE_ITERATIONS_MED;
234 		 iterationCount++ )
235 		{
236 		const int rdnStart = stell( stream );
237 
238 		REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT );
239 
240 		status = readRDNcomponent( stream, dnComponentListPtrPtr,
241 								   rdnLength );
242 		if( cryptStatusError( status ) )
243 			return( status );
244 
245 		rdnLength -= stell( stream ) - rdnStart;
246 		}
247 	if( rdnLength < 0 || iterationCount >= FAILSAFE_ITERATIONS_MED )
248 		return( CRYPT_ERROR_BADDATA );
249 
250 	return( CRYPT_OK );
251 	}
252 
253 /* Read a DN */
254 
255 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
readDN(INOUT STREAM * stream,OUT_PTR_COND DN_PTR ** dnComponentListPtrPtr)256 int readDN( INOUT STREAM *stream,
257 			OUT_PTR_COND DN_PTR **dnComponentListPtrPtr )
258 	{
259 	DN_COMPONENT *dnComponentListPtr = NULL;
260 	int length, iterationCount, status;
261 
262 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
263 	assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) );
264 
265 	/* Clear return value */
266 	*dnComponentListPtrPtr = NULL;
267 
268 	/* Read the encoded DN into the local copy of the DN (in other words
269 	   into the dnComponentListPtr, not the externally-visible
270 	   dnComponentListPtrPtr) */
271 	status = readSequenceZ( stream, &length );
272 	if( cryptStatusError( status ) )
273 		return( status );
274 	if( length <= 0 )
275 		{
276 		/* Some buggy certificates include zero-length DNs, which we skip */
277 		return( CRYPT_OK );
278 		}
279 	for( iterationCount = 0;
280 		 length > 0 && iterationCount < FAILSAFE_ITERATIONS_MED;
281 		 iterationCount++ )
282 		{
283 		const int startPos = stell( stream );
284 
285 		REQUIRES( startPos > 0 && startPos < MAX_INTLENGTH_SHORT );
286 
287 		status = readDNComponent( stream, &dnComponentListPtr );
288 		if( cryptStatusError( status ) )
289 			break;
290 		length -= stell( stream ) - startPos;
291 		}
292 	if( cryptStatusError( status ) || \
293 		length < 0 || iterationCount >= FAILSAFE_ITERATIONS_MED )
294 		{
295 		/* Delete the local copy of the DN read so far if necessary */
296 		if( dnComponentListPtr != NULL )
297 			deleteDN( ( DN_PTR ** ) &dnComponentListPtr );
298 		return( cryptStatusError( status ) ? status : CRYPT_ERROR_BADDATA );
299 		}
300 
301 	/* Copy the local copy of the DN back to the caller */
302 	*dnComponentListPtrPtr = dnComponentListPtr;
303 	return( CRYPT_OK );
304 	}
305 
306 /****************************************************************************
307 *																			*
308 *									Write a DN								*
309 *																			*
310 ****************************************************************************/
311 
312 /* Perform the pre-encoding processing for a DN.  Note that sizeofDN() takes
313    a slightly anomalous non-const parameter because the pre-encoding process
314    required to determine the DN's size modifies portions of the DN component
315    values related to the encoding process */
316 
317 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
preEncodeDN(INOUT DN_COMPONENT * dnComponentPtr,OUT_LENGTH_SHORT_Z int * length)318 static int preEncodeDN( INOUT DN_COMPONENT *dnComponentPtr,
319 						OUT_LENGTH_SHORT_Z int *length )
320 	{
321 	int size = 0, iterationCount;
322 
323 	assert( isWritePtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) );
324 	assert( isWritePtr( length, sizeof( int ) ) );
325 
326 	/* Clear return value */
327 	*length = 0;
328 
329 	assert( isReadPtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) );
330 
331 	ENSURES( dnComponentPtr->prev == NULL );
332 
333 	/* Walk down the DN pre-encoding each AVA */
334 	for( iterationCount = 0;
335 		 dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
336 		 dnComponentPtr = dnComponentPtr->next, iterationCount++ )
337 		{
338 		DN_COMPONENT *rdnStartPtr = dnComponentPtr;
339 		int innerIterationCount;
340 
341 		/* Calculate the size of every AVA in this RDN */
342 		for( innerIterationCount = 0;
343 			 dnComponentPtr != NULL && \
344 				innerIterationCount < FAILSAFE_ITERATIONS_MED;
345 			 dnComponentPtr = dnComponentPtr->next, innerIterationCount++ )
346 			{
347 			const DN_COMPONENT_INFO *dnComponentInfo = dnComponentPtr->typeInfo;
348 			int dnStringLength, status;
349 
350 			status = getAsn1StringInfo( dnComponentPtr->value,
351 										dnComponentPtr->valueLength,
352 										&dnComponentPtr->valueStringType,
353 										&dnComponentPtr->asn1EncodedStringType,
354 										&dnStringLength, TRUE );
355 			if( cryptStatusError( status ) )
356 				return( status );
357 			dnComponentPtr->encodedAVAdataSize = ( int ) \
358 										sizeofOID( dnComponentInfo->oid ) + \
359 										sizeofObject( dnStringLength );
360 			dnComponentPtr->encodedRDNdataSize = 0;
361 			rdnStartPtr->encodedRDNdataSize += ( int ) \
362 						sizeofObject( dnComponentPtr->encodedAVAdataSize );
363 			if( !( dnComponentPtr->flags & DN_FLAG_CONTINUED ) )
364 				break;
365 			}
366 		ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
367 
368 		/* Calculate the overall size of the RDN */
369 		size += ( int ) sizeofObject( rdnStartPtr->encodedRDNdataSize );
370 
371 		/* If the inner loop terminated because it reached the end of the DN
372 		   then we need to explicitly exit the outer loop as well before it
373 		   tries to follow the 'next' link in the dnComponentPtr */
374 		if( dnComponentPtr == NULL )
375 			break;
376 		}
377 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
378 	*length = size;
379 
380 	return( CRYPT_OK );
381 	}
382 
383 CHECK_RETVAL_LENGTH \
sizeofDN(INOUT_OPT DN_PTR * dnComponentList)384 int sizeofDN( INOUT_OPT DN_PTR *dnComponentList )
385 	{
386 	int length, status;
387 
388 	assert( dnComponentList == NULL || \
389 			isWritePtr( dnComponentList, sizeof( DN_COMPONENT ) ) );
390 
391 	/* Null DNs produce a zero-length SEQUENCE */
392 	if( dnComponentList == NULL )
393 		return( sizeofObject( 0 ) );
394 
395 	status = preEncodeDN( dnComponentList, &length );
396 	if( cryptStatusError( status ) )
397 		return( status );
398 	return( sizeofObject( length ) );
399 	}
400 
401 /* Write a DN */
402 
403 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeDN(INOUT STREAM * stream,IN_OPT const DN_PTR * dnComponentList,IN_TAG const int tag)404 int writeDN( INOUT STREAM *stream,
405 			 IN_OPT const DN_PTR *dnComponentList,
406 			 IN_TAG const int tag )
407 	{
408 	DN_COMPONENT *dnComponentPtr;
409 	int size, iterationCount, status;
410 
411 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
412 	assert( dnComponentList == NULL || \
413 			isReadPtr( dnComponentList, sizeof( DN_COMPONENT ) ) );
414 
415 	REQUIRES_S( tag == DEFAULT_TAG || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
416 
417 	/* Special case for empty DNs */
418 	if( dnComponentList == NULL )
419 		return( writeConstructed( stream, 0, tag ) );
420 
421 	status = preEncodeDN( ( DN_COMPONENT * ) dnComponentList, &size );
422 	if( cryptStatusError( status ) )
423 		return( status );
424 
425 	/* Write the DN */
426 	writeConstructed( stream, size, tag );
427 	for( dnComponentPtr = ( DN_COMPONENT * ) dnComponentList, \
428 			iterationCount = 0;
429 		 dnComponentPtr != NULL && \
430 			iterationCount < FAILSAFE_ITERATIONS_MED;
431 		 dnComponentPtr = dnComponentPtr->next, iterationCount++ )
432 		{
433 		const DN_COMPONENT_INFO *dnComponentInfo = dnComponentPtr->typeInfo;
434 		BYTE dnString[ MAX_ATTRIBUTE_SIZE + 8 ];
435 		int dnStringLength;
436 
437 		/* Write the RDN wrapper */
438 		if( dnComponentPtr->encodedRDNdataSize > 0 )
439 			{
440 			/* If it's the start of an RDN, write the RDN header */
441 			writeSet( stream, dnComponentPtr->encodedRDNdataSize );
442 			}
443 		writeSequence( stream, dnComponentPtr->encodedAVAdataSize );
444 		status = swrite( stream, dnComponentInfo->oid,
445 						 sizeofOID( dnComponentInfo->oid ) );
446 		if( cryptStatusError( status ) )
447 			return( status );
448 
449 		/* Convert the string to an ASN.1-compatible format and write it
450 		   out */
451 		status = copyToAsn1String( dnString, MAX_ATTRIBUTE_SIZE,
452 								   &dnStringLength, dnComponentPtr->value,
453 								   dnComponentPtr->valueLength,
454 								   dnComponentPtr->valueStringType );
455 		if( cryptStatusError( status ) )
456 			return( status );
457 		if( dnComponentPtr->asn1EncodedStringType == BER_STRING_IA5 && \
458 			!dnComponentInfo->ia5OK )
459 			{
460 			/* If an IA5String isn't allowed in this instance, use a
461 			   T61String instead */
462 			dnComponentPtr->asn1EncodedStringType = BER_STRING_T61;
463 			}
464 		status = writeCharacterString( stream, dnString, dnStringLength,
465 									   dnComponentPtr->asn1EncodedStringType );
466 		if( cryptStatusError( status ) )
467 			return( status );
468 		}
469 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
470 
471 	return( CRYPT_OK );
472 	}
473 
474 /****************************************************************************
475 *																			*
476 *								DN String Routines							*
477 *																			*
478 ****************************************************************************/
479 
480 /* The ability to specify free-form DNs means that users can create
481    arbitrarily garbled and broken DNs (the creation of weird nonstandard DNs
482    is pretty much the main reason why the DN-string capability exists).
483    This includes DNs that can't be easily handled through normal cryptlib
484    facilities, for example ones where the CN component consists of illegal
485    characters or is in a form that isn't usable as a search key for
486    functions like cryptGetPublicKey().  Because of these problems this
487    functionality is disabled by default, if users want to use this oddball-
488    DN facility it's up to them to make sure that the resulting DN
489    information works with whatever environment they're intending to use it
490    in */
491 
492 #ifdef USE_CERT_DNSTRING
493 
494 #if defined( _MSC_VER ) || defined( __GNUC__ )
495   #pragma message( "  Building with string-form DNs enabled." )
496 #endif /* Warn about special features enabled */
497 
498 /* Check whether a string can be represented as a textual DN string */
499 
isTextString(IN_BUFFER (stringLength)const BYTE * string,IN_LENGTH_ATTRIBUTE const int stringLength)500 static BOOLEAN isTextString( IN_BUFFER( stringLength ) const BYTE *string,
501 							 IN_LENGTH_ATTRIBUTE const int stringLength )
502 	{
503 	int i;
504 
505 	assert( isReadPtr( string, stringLength ) );
506 
507 	REQUIRES( stringLength > 0 && stringLength <= MAX_ATTRIBUTE_SIZE );
508 
509 	/* Make sure that there are no control characters in the string.  We
510 	   allow high-bit-set characters in order to support latin-1 strings,
511 	   see also the comment at the start of this section about the general
512 	   caveat-emptor philosophy for this interface */
513 	for( i = 0; i < stringLength; i++ )
514 		{
515 		if( ( string[ i ] & 0x7F ) < ' ' )
516 			return( FALSE );
517 		}
518 
519 	return( TRUE );
520 	}
521 
522 /* Read a DN in string form */
523 
524 typedef struct {
525 	BUFFER_FIXED( labelLen ) \
526 	const BYTE *label;
527 	BUFFER_FIXED( textLen ) \
528 	const BYTE *text;
529 	int labelLen, textLen;	/* DN component label and value */
530 	BOOLEAN isContinued;	/* Whether there are further AVAs in this RDN */
531 	} DN_STRING_INFO;
532 
533 #define MAX_DNSTRING_COMPONENTS 32
534 
535 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
OUT_ARRAY(MAX_DNSTRING_COMPONENTS)536 static BOOLEAN parseDNString( OUT_ARRAY( MAX_DNSTRING_COMPONENTS ) \
537 									DN_STRING_INFO *dnStringInfo,
538 							  OUT_RANGE( 0, MAX_DNSTRING_COMPONENTS ) \
539 									int *dnStringInfoIndex,
540 							  IN_BUFFER( stringLength ) const BYTE *string,
541 							  IN_LENGTH_ATTRIBUTE const int stringLength )
542 	{
543 	int stringPos, stringInfoIndex;
544 
545 	assert( isWritePtr( dnStringInfo, sizeof( DN_STRING_INFO ) * \
546 									  MAX_DNSTRING_COMPONENTS ) );
547 	assert( isReadPtr( string, stringLength ) );
548 
549 	REQUIRES( stringLength > 0 && stringLength <= MAX_ATTRIBUTE_SIZE );
550 
551 	/* Clear return values */
552 	memset( dnStringInfo, 0,
553 			sizeof( DN_STRING_INFO ) * MAX_DNSTRING_COMPONENTS );
554 	*dnStringInfoIndex = 0;
555 
556 	/* Make sure that the string is decodable as a text string */
557 	if( !isTextString( string, stringLength ) )
558 		return( FALSE );
559 
560 	/* Verify that a DN string is of the form:
561 
562 		dnString ::= assignment '\0' | assignment ',' assignment
563 		assignment ::= label '=' text */
564 	for( stringPos = 0, stringInfoIndex = 0;
565 		 stringPos < stringLength && \
566 			stringInfoIndex < MAX_DNSTRING_COMPONENTS;
567 		 stringInfoIndex++ )
568 		{
569 		DN_STRING_INFO *dnStringInfoPtr = &dnStringInfo[ stringInfoIndex ];
570 		int i;
571 
572 		/* Check for label '=' ... */
573 		for( i = stringPos; i < stringLength; i++ )
574 			{
575 			const int ch = byteToInt( string[ i ] );
576 
577 			if( ch == '\\' || ch == '+' || ch == ',' )
578 				{
579 				/* The label component can't have special characters in it */
580 				return( FALSE );
581 				}
582 			if( ch == '=' )
583 				break;
584 			}
585 		if( i <= stringPos || i >= stringLength - 1 )	/* -1 for '=' */
586 			{
587 			/* The label component is empty */
588 			return( FALSE );
589 			}
590 		ENSURES( rangeCheckZ( stringPos, i - stringPos, stringLength ) );
591 		dnStringInfoPtr->label = string + stringPos;
592 		dnStringInfoPtr->labelLen = i - stringPos;
593 		stringPos = i + 1;		/* Skip text + '=' */
594 
595 		/* Check for ... text { EOT | ',' ... | '+' ... }.  Note that we
596 		   make the loop variable increment part of the loop code rather
597 		   than having it in the for() statement to avoid a series of messy
598 		   fencepost-error adjustments in the checks that follow */
599 		for( i = stringPos; i < stringLength && \
600 							i < MAX_ATTRIBUTE_SIZE; i++ )
601 			{
602 			const int ch = byteToInt( string[ i ] );
603 
604 			/* Check for invalid data */
605 			if( ch == '=' )
606 				{
607 				/* Spurious '=' */
608 				return( FALSE );
609 				}
610 			if( ch == '\\' )
611 				{
612 				if( i >= stringLength - 1 )
613 					{
614 					/* Missing escaped character */
615 					return( FALSE );
616 					}
617 
618 				/* It's an escaped character that isn't subject to the usual
619 				   checks, skip it and continue */
620 				i++;
621 				continue;
622 				}
623 
624 			/* If this isn't a continuation symbol, continue */
625 			if( ch != ',' && ch != '+' )
626 				continue;
627 
628 			/* We've reached a continuation symbol, make sure that there's
629 			   room for at least another 'x=y' after this point */
630 			if( i >= stringLength - 3 )
631 				return( FALSE );
632 
633 			break;
634 			}
635 		ENSURES( i < MAX_ATTRIBUTE_SIZE );
636 		ENSURES( rangeCheck( stringPos, i - stringPos, stringLength ) );
637 		dnStringInfoPtr->text = string + stringPos;
638 		dnStringInfoPtr->textLen = i - stringPos;
639 		if( string[ i ] == ',' || string[ i ] == '+' )
640 			{
641 			/* Skip the final ',' or '+' and remember whether this is a
642 			   continued RDN */
643 			if( string[ i ] == '+' )
644 				dnStringInfoPtr->isContinued = TRUE;
645 			i++;
646 			}
647 		stringPos = i;			/* Skip text + optional ','/'+' */
648 
649 		/* Strip leading and trailing whitespace on the label and text */
650 		dnStringInfoPtr->labelLen = \
651 				strStripWhitespace( ( const char ** ) &dnStringInfoPtr->label,
652 									dnStringInfoPtr->label,
653 									dnStringInfoPtr->labelLen );
654 		dnStringInfoPtr->textLen = \
655 				strStripWhitespace( ( const char ** ) &dnStringInfoPtr->text,
656 									dnStringInfoPtr->text,
657 									dnStringInfoPtr->textLen );
658 		if( dnStringInfoPtr->labelLen < 1 || \
659 			dnStringInfoPtr->labelLen > MAX_ATTRIBUTE_SIZE || \
660 			dnStringInfoPtr->textLen < 1 || \
661 			dnStringInfoPtr->textLen > MAX_ATTRIBUTE_SIZE )
662 			return( FALSE );
663 		}
664 	if( stringInfoIndex <= 0 || stringInfoIndex >= MAX_DNSTRING_COMPONENTS )
665 		return( FALSE );
666 	*dnStringInfoIndex = stringInfoIndex;
667 
668 	return( TRUE );
669 	}
670 
671 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
readDNstring(INOUT_PTR DN_PTR ** dnComponentListPtrPtr,IN_BUFFER (stringLength)const BYTE * string,IN_LENGTH_ATTRIBUTE const int stringLength)672 int readDNstring( INOUT_PTR DN_PTR **dnComponentListPtrPtr,
673 				  IN_BUFFER( stringLength ) const BYTE *string,
674 				  IN_LENGTH_ATTRIBUTE const int stringLength )
675 	{
676 	DN_STRING_INFO dnStringInfo[ MAX_DNSTRING_COMPONENTS + 8 ];
677 	DN_COMPONENT *dnComponentPtr = NULL;
678 	int stringInfoIndex, iterationCount;
679 
680 	assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) );
681 	assert( isReadPtr( string, stringLength ) );
682 
683 	REQUIRES( stringLength > 0 && stringLength <= MAX_ATTRIBUTE_SIZE );
684 
685 	/* Clear return value */
686 	*dnComponentListPtrPtr = NULL;
687 
688 	/* We have to perform the text string to DN translation in two stages
689 	   thanks to the backwards encoding required by RFC 1779.  First we
690 	   parse it forwards to separate out the RDN components, then we move
691 	   through the parsed information backwards adding it to the RDN (with
692 	   special handling for multi-AVA RDNs as for writeDNstring()).  Overall
693 	   this isn't so bad because it means that we can perform a general
694 	   firewall check to make sure that the DN string is well-formed and
695 	   then leave the encoding as a separate pass */
696 	if( !parseDNString( dnStringInfo, &stringInfoIndex, string,
697 						stringLength ) )
698 		return( CRYPT_ARGERROR_STR1 );
699 
700 	/* parseDNString() returns the number of entries parsed, since we're
701 	   using zero-based indexing we have to decrement the value returned to
702 	   provide the actual index into the dnStringInfo[] array */
703 	stringInfoIndex--;
704 
705 	for( iterationCount = 0;
706 		 stringInfoIndex >= 0 && iterationCount < FAILSAFE_ITERATIONS_MED;
707 		 stringInfoIndex--, iterationCount++ )
708 		{
709 		const DN_STRING_INFO *dnStringInfoPtr;
710 		BOOLEAN isContinued;
711 		int innerIterationCount;
712 
713 		/* Find the start of the RDN */
714 		while( stringInfoIndex > 0 && \
715 			   dnStringInfo[ stringInfoIndex - 1 ].isContinued )
716 			stringInfoIndex--;
717 		dnStringInfoPtr = &dnStringInfo[ stringInfoIndex ];
718 
719 		for( isContinued = TRUE, innerIterationCount = 0;
720 			 isContinued && innerIterationCount < FAILSAFE_ITERATIONS_MED;
721 			 innerIterationCount++ )
722 			{
723 			CRYPT_ERRTYPE_TYPE dummy;
724 			const DN_COMPONENT_INFO *dnComponentInfo;
725 			BYTE textBuffer[ MAX_ATTRIBUTE_SIZE + 8 ];
726 			CRYPT_ATTRIBUTE_TYPE type;
727 			int i, textIndex = 0, valueStringType, dummy1, dummy2, status;
728 
729 			/* Look up the DN component information */
730 			dnComponentInfo = findDNInfoByLabel( dnStringInfoPtr->label,
731 												 dnStringInfoPtr->labelLen );
732 			if( dnComponentInfo == NULL )
733 				{
734 				if( dnComponentPtr != NULL )
735 					deleteDN( ( DN_PTR ** ) &dnComponentPtr );
736 				return( CRYPT_ARGERROR_STR1 );
737 				}
738 			type = dnComponentInfo->type;
739 
740 			/* Convert the text to canonical form, removing any escapes for
741 			   special characters */
742 			for( i = 0; i < dnStringInfoPtr->textLen && \
743 						i < MAX_ATTRIBUTE_SIZE; i++ )
744 				{
745 				int ch = byteToInt( dnStringInfoPtr->text[ i ] );
746 
747 				if( ch == '\\' )
748 					{
749 					/* Skip '\\' */
750 					i++;
751 					if( i >= dnStringInfoPtr->textLen )
752 						{
753 						if( dnComponentPtr != NULL )
754 							deleteDN( ( DN_PTR ** ) &dnComponentPtr );
755 						return( CRYPT_ARGERROR_STR1 );
756 						}
757 					ch = byteToInt( dnStringInfoPtr->text[ i ] );
758 					}
759 				textBuffer[ textIndex++ ] = intToByte( ch );
760 				}
761 			ENSURES( i < MAX_ATTRIBUTE_SIZE );
762 			ENSURES( textIndex > 0 && textIndex < MAX_INTLENGTH_SHORT );
763 
764 			/* The value is coming from an external source, make sure that
765 			   it's representable as a certificate string type.  All that
766 			   we care about here is the validity so we ignore the returned
767 			   encoding information */
768 			status = getAsn1StringInfo( textBuffer, textIndex,
769 										&valueStringType, &dummy1, &dummy2,
770 										FALSE );
771 			if( cryptStatusError( status ) )
772 				{
773 				if( dnComponentPtr != NULL )
774 					deleteDN( ( DN_PTR ** ) &dnComponentPtr );
775 				return( CRYPT_ARGERROR_STR1 );
776 				}
777 
778 			/* Add the AVA to the DN */
779 			if( type == CRYPT_CERTINFO_COUNTRYNAME )
780 				{
781 				/* If it's a country code force it to uppercase as per ISO 3166 */
782 				if( textIndex != 2 )
783 					{
784 					if( dnComponentPtr != NULL )
785 						deleteDN( ( DN_PTR ** ) &dnComponentPtr );
786 					return( CRYPT_ARGERROR_STR1 );
787 					}
788 				textBuffer[ 0 ] = intToByte( toUpper( textBuffer[ 0 ] ) );
789 				textBuffer[ 1 ] = intToByte( toUpper( textBuffer[ 1 ] ) );
790 				}
791 			status = insertDNstring( &dnComponentPtr, type,
792 									 textBuffer, textIndex, valueStringType,
793 									 ( dnStringInfoPtr->isContinued ) ? \
794 										DN_FLAG_CONTINUED | DN_FLAG_NOCHECK :
795 										DN_FLAG_NOCHECK, &dummy );
796 			if( cryptStatusError( status ) )
797 				{
798 				if( dnComponentPtr != NULL )
799 					deleteDN( ( DN_PTR ** ) &dnComponentPtr );
800 				return( status );
801 				}
802 
803 			/* Move on to the next AVA */
804 			isContinued = dnStringInfoPtr->isContinued;
805 			dnStringInfoPtr++;
806 			}
807 		ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
808 		}
809 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
810 
811 	/* We're done, lock the DN against further updates */
812 	*dnComponentListPtrPtr = dnComponentPtr;
813 	for( iterationCount = 0;
814 		 dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_LARGE;
815 		 dnComponentPtr = dnComponentPtr->next, iterationCount++ )
816 		dnComponentPtr->flags |= DN_FLAG_LOCKED;
817 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_LARGE );
818 
819 	return( CRYPT_OK );
820 	}
821 
822 /* Write a DN in string form */
823 
824 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writeAVAString(INOUT STREAM * stream,const DN_COMPONENT * dnComponentPtr)825 static int writeAVAString( INOUT STREAM *stream,
826 						   const DN_COMPONENT *dnComponentPtr )
827 	{
828 	const DN_COMPONENT_INFO *componentInfoPtr = dnComponentPtr->typeInfo;
829 	int i, status;
830 
831 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
832 	assert( isReadPtr( dnComponentPtr, sizeof( DN_COMPONENT ) ) );
833 
834 	/* Print the current AVA */
835 	swrite( stream, componentInfoPtr->name, componentInfoPtr->nameLen );
836 	status = sputc( stream, '=' );
837 	if( cryptStatusError( status ) )
838 		return( status );
839 	for( i = 0; i < dnComponentPtr->valueLength; i++ )
840 		{
841 		const int ch = byteToInt( ( ( BYTE * ) dnComponentPtr->value )[ i ] );
842 
843 		if( ch == ',' || ch == '=' || ch == '+' || ch == ';' || \
844 			ch == '\\' || ch == '"' )
845 			sputc( stream, '\\' );
846 		status = sputc( stream, ch );
847 		if( cryptStatusError( status ) )
848 			return( status );
849 		}
850 
851 	return( CRYPT_OK );
852 	}
853 
854 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeDNstring(INOUT STREAM * stream,IN_OPT const DN_PTR * dnComponentList)855 int writeDNstring( INOUT STREAM *stream,
856 				   IN_OPT const DN_PTR *dnComponentList )
857 	{
858 	const DN_COMPONENT *dnComponentPtr = dnComponentList;
859 	int iterationCount, status;
860 
861 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
862 	assert( dnComponentList == NULL || \
863 			isReadPtr( dnComponentList, sizeof( DN_COMPONENT ) ) );
864 
865 	/* If it's an empty DN there's nothing to write */
866 	if( dnComponentPtr == NULL )
867 		return( CRYPT_OK );
868 
869 	/* Find the end of the DN string.  We have to print the RDNs backwards
870 	   because of ISODE's JANET memorial backwards encoding.  This also
871 	   provides a convenient location to check that the DN data is actually
872 	   representable as a text string.
873 
874 	   Some string types can't be represented as text strings which means
875 	   that we can't send them directly to the output.  The details of what
876 	   to do here are a bit complex, in theory we could send them out as
877 	   UTF-8 but few environments are going to expect this as a returned
878 	   type, particularly when the existing expectation is for oddball
879 	   characters in text strings to be latin-1.  The least ugly solution
880 	   seems to be to just return an indicator that this string can't be
881 	   represented */
882 	for( iterationCount = 0;
883 		 dnComponentPtr->next != NULL && \
884 			iterationCount < FAILSAFE_ITERATIONS_MED;
885 		 dnComponentPtr = dnComponentPtr->next, iterationCount++ )
886 		{
887 		/* Make sure that the DN component is representable as a text
888 		   string.  Exactly what we should return if this check fails is a
889 		   bit uncertain since there's no error code that it really
890 		   corresponds to, CRYPT_ERROR_NOTAVAIL appears to be the least
891 		   inappropriate one to use.
892 
893 		   An alternative is to return a special-case string like "(DN
894 		   can't be represented in string form)" but this then looks (from
895 		   the return status) as if it was actually representable, requiring
896 		   special-case checks for valid-but-not-valid returned data, so the
897 		   error status is probably the best option */
898 		if( !isTextString( dnComponentPtr->value,
899 						   dnComponentPtr->valueLength ) )
900 			return( CRYPT_ERROR_NOTAVAIL );
901 		}
902 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
903 
904 	for( iterationCount = 0;
905 		 dnComponentPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
906 		 iterationCount++ )
907 		{
908 		const DN_COMPONENT *dnComponentCursor;
909 		int innerIterationCount;
910 
911 		/* Find the start of the RDN */
912 		for( innerIterationCount = 0;
913 			 dnComponentPtr->prev != NULL && \
914 				( dnComponentPtr->prev->flags & DN_FLAG_CONTINUED ) && \
915 				innerIterationCount < FAILSAFE_ITERATIONS_MED;
916 			 dnComponentPtr = dnComponentPtr->prev, innerIterationCount++ );
917 		ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
918 		dnComponentCursor = dnComponentPtr;
919 		dnComponentPtr = dnComponentPtr->prev;
920 
921 		/* Print the current RDN */
922 		for( status = CRYPT_OK, innerIterationCount = 0;
923 			 cryptStatusOK( status ) && \
924 				innerIterationCount < FAILSAFE_ITERATIONS_MED;
925 			 innerIterationCount++ )
926 			{
927 			status = writeAVAString( stream, dnComponentCursor );
928 			if( cryptStatusError( status ) )
929 				return( status );
930 
931 			/* If this is the last AVA in the RDN, we're done */
932 			if( !( dnComponentCursor->flags & DN_FLAG_CONTINUED ) )
933 				break;
934 
935 			/* There are more AVAs in this RDN, print a continuation
936 			   indicator and move on to the next AVA */
937 			status = swrite( stream, " + ", 3 );
938 			if( cryptStatusError( status ) )
939 				return( status );
940 			dnComponentCursor = dnComponentCursor->next;
941 			ENSURES( dnComponentCursor != NULL );
942 			}
943 		ENSURES( innerIterationCount < FAILSAFE_ITERATIONS_MED );
944 
945 		/* If there are more components to come print an RDN separator */
946 		if( dnComponentPtr != NULL )
947 			{
948 			status = swrite( stream, ", ", 2 );
949 			if( cryptStatusError( status ) )
950 				return( status );
951 			}
952 		}
953 	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
954 
955 	return( CRYPT_OK );
956 	}
957 #endif /* USE_CERT_DNSTRING */
958 #endif /* USE_CERTIFICATES */
959