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