/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ #ifdef DEBUG static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"; #endif /* DEBUG */ /* * atav.c * * This file contains the implementation of the PKIX part-1 object * AttributeTypeAndValue. */ #ifndef NSSBASE_H #include "nssbase.h" #endif /* NSSBASE_H */ #ifndef ASN1_H #include "asn1.h" #endif /* ASN1_H */ #ifndef PKI1_H #include "pki1.h" #endif /* PKI1_H */ /* * AttributeTypeAndValue * * From draft-ietf-pkix-ipki-part1-10: * * AttributeTypeAndValue ::= SEQUENCE { * type ATTRIBUTE.&id ({SupportedAttributes}), * value ATTRIBUTE.&Type ({SupportedAttributes}{@type})} * * -- ATTRIBUTE information object class specification * -- Note: This has been greatly simplified for PKIX !! * * ATTRIBUTE ::= CLASS { * &Type, * &id OBJECT IDENTIFIER UNIQUE } * WITH SYNTAX { * WITH SYNTAX &Type ID &id } * * What this means is that the "type" of the value is determined by * the value of the oid. If we hide the structure, our accessors * can (at least in debug builds) assert value semantics beyond what * the compiler can provide. Since these things are only used in * RelativeDistinguishedNames, and since RDNs always contain a SET * of these things, we don't lose anything by hiding the structure * (and its size). */ struct NSSATAVStr { NSSBER ber; const NSSOID *oid; NSSUTF8 *value; nssStringType stringForm; }; /* * NSSATAV * * The public "methods" regarding this "object" are: * * NSSATAV_CreateFromBER -- constructor * NSSATAV_CreateFromUTF8 -- constructor * NSSATAV_Create -- constructor * * NSSATAV_Destroy * NSSATAV_GetDEREncoding * NSSATAV_GetUTF8Encoding * NSSATAV_GetType * NSSATAV_GetValue * NSSATAV_Compare * NSSATAV_Duplicate * * The non-public "methods" regarding this "object" are: * * nssATAV_CreateFromBER -- constructor * nssATAV_CreateFromUTF8 -- constructor * nssATAV_Create -- constructor * * nssATAV_Destroy * nssATAV_GetDEREncoding * nssATAV_GetUTF8Encoding * nssATAV_GetType * nssATAV_GetValue * nssATAV_Compare * nssATAV_Duplicate * * In debug builds, the following non-public call is also available: * * nssATAV_verifyPointer */ /* * NSSATAV_CreateFromBER * * This routine creates an NSSATAV by decoding a BER- or DER-encoded * ATAV. If the optional arena argument is non-null, the memory used * will be obtained from that arena; otherwise, the memory will be * obtained from the heap. This routine may return NULL upon error, * in which case it will have created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_BER * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSATAV upon success */ NSS_IMPLEMENT NSSATAV * NSSATAV_CreateFromBER ( NSSArena *arenaOpt, NSSBER *berATAV ) { nss_ClearErrorStack(); #ifdef DEBUG if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } /* * NSSBERs can be created by the user, * so no pointer-tracking can be checked. */ if( (NSSBER *)NULL == berATAV ) { nss_SetError(NSS_ERROR_INVALID_BER); return (NSSATAV *)NULL; } if( (void *)NULL == berATAV->data ) { nss_SetError(NSS_ERROR_INVALID_BER); return (NSSATAV *)NULL; } #endif /* DEBUG */ return nssATAV_CreateFromBER(arenaOpt, berATAV); } /* * NSSATAV_CreateFromUTF8 * * This routine creates an NSSATAV by decoding a UTF8 string in the * "equals" format, e.g., "c=US." If the optional arena argument is * non-null, the memory used will be obtained from that arena; * otherwise, the memory will be obtained from the heap. This routine * may return NULL upon error, in which case it will have created an * error stack. * * The error may be one of the following values: * NSS_ERROR_UNKNOWN_ATTRIBUTE * NSS_ERROR_INVALID_STRING * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSATAV upon success */ NSS_IMPLEMENT NSSATAV * NSSATAV_CreateFromUTF8 ( NSSArena *arenaOpt, NSSUTF8 *stringATAV ) { nss_ClearErrorStack(); #ifdef DEBUG if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } /* * NSSUTF8s can be created by the user, * so no pointer-tracking can be checked. */ if( (NSSUTF8 *)NULL == stringATAV ) { nss_SetError(NSS_ERROR_INVALID_UTF8); return (NSSATAV *)NULL; } #endif /* DEBUG */ return nssATAV_CreateFromUTF8(arenaOpt, stringATAV); } /* * NSSATAV_Create * * This routine creates an NSSATAV from the specified NSSOID and the * specified data. If the optional arena argument is non-null, the * memory used will be obtained from that arena; otherwise, the memory * will be obtained from the heap.If the specified data length is zero, * the data is assumed to be terminated by first zero byte; this allows * UTF8 strings to be easily specified. This routine may return NULL * upon error, in which case it will have created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ARENA * NSS_ERROR_INVALID_NSSOID * NSS_ERROR_INVALID_POINTER * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSATAV upon success */ NSS_IMPLEMENT NSSATAV * NSSATAV_Create ( NSSArena *arenaOpt, const NSSOID *oid, const void *data, PRUint32 length ) { nss_ClearErrorStack(); #ifdef DEBUG if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { return (NSSATAV *)NULL; } if( (const void *)NULL == data ) { nss_SetError(NSS_ERROR_INVALID_POINTER); return (NSSATAV *)NULL; } #endif /* DEBUG */ return nssATAV_Create(arenaOpt, oid, data, length); } /* * NSSATAV_Destroy * * This routine will destroy an ATAV object. It should eventually be * called on all ATAVs created without an arena. While it is not * necessary to call it on ATAVs created within an arena, it is not an * error to do so. This routine returns a PRStatus value; if * successful, it will return PR_SUCCESS. If unsuccessful, it will * create an error stack and return PR_FAILURE. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * * Return value: * PR_FAILURE upon error * PR_SUCCESS upon success */ NSS_IMPLEMENT PRStatus NSSATAV_Destroy ( NSSATAV *atav ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return PR_FAILURE; } #endif /* DEBUG */ return nssATAV_Destroy(atav); } /* * NSSATAV_GetDEREncoding * * This routine will DER-encode an ATAV object. If the optional arena * argument is non-null, the memory used will be obtained from that * arena; otherwise, the memory will be obtained from the heap. This * routine may return null upon error, in which case it will have * created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * The DER encoding of this NSSATAV */ NSS_IMPLEMENT NSSDER * NSSATAV_GetDEREncoding ( NSSATAV *atav, NSSArena *arenaOpt ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSDER *)NULL; } if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSDER *)NULL; } } #endif /* DEBUG */ return nssATAV_GetDEREncoding(atav, arenaOpt); } /* * NSSATAV_GetUTF8Encoding * * This routine returns a UTF8 string containing a string * representation of the ATAV in "equals" notation (e.g., "o=Acme"). * If the optional arena argument is non-null, the memory used will be * obtained from that arena; otherwise, the memory will be obtained * from the heap. This routine may return null upon error, in which * case it will have created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to a UTF8 string containing the "equals" encoding of the * ATAV */ NSS_IMPLEMENT NSSUTF8 * NSSATAV_GetUTF8Encoding ( NSSATAV *atav, NSSArena *arenaOpt ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSUTF8 *)NULL; } if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSUTF8 *)NULL; } } #endif /* DEBUG */ return nssATAV_GetUTF8Encoding(atav, arenaOpt); } /* * NSSATAV_GetType * * This routine returns the NSSOID corresponding to the attribute type * in the specified ATAV. This routine may return NSS_OID_UNKNOWN * upon error, in which case it will have created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * * Return value: * NSS_OID_UNKNOWN upon error * An element of enum NSSOIDenum upon success */ NSS_IMPLEMENT const NSSOID * NSSATAV_GetType ( NSSATAV *atav ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSOID *)NULL; } #endif /* DEBUG */ return nssATAV_GetType(atav); } /* * NSSATAV_GetValue * * This routine returns a string containing the attribute value * in the specified ATAV. If the optional arena argument is non-null, * the memory used will be obtained from that arena; otherwise, the * memory will be obtained from the heap. This routine may return * NULL upon error, in which case it will have created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSItem containing the attribute value. */ NSS_IMPLEMENT NSSUTF8 * NSSATAV_GetValue ( NSSATAV *atav, NSSArena *arenaOpt ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSUTF8 *)NULL; } if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSUTF8 *)NULL; } } #endif /* DEBUG */ return nssATAV_GetValue(atav, arenaOpt); } /* * NSSATAV_Compare * * This routine compares two ATAVs for equality. For two ATAVs to be * equal, the attribute types must be the same, and the attribute * values must have equal length and contents. The result of the * comparison will be stored at the location pointed to by the "equalp" * variable, which must point to a valid PRBool. This routine may * return PR_FAILURE upon error, in which case it will have created an * error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_INVALID_ARGUMENT * * Return value: * PR_FAILURE on error * PR_SUCCESS upon a successful comparison (equal or not) */ NSS_IMPLEMENT PRStatus NSSATAV_Compare ( NSSATAV *atav1, NSSATAV *atav2, PRBool *equalp ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav1) ) { return PR_FAILURE; } if( PR_SUCCESS != nssATAV_verifyPointer(atav2) ) { return PR_FAILURE; } if( (PRBool *)NULL == equalp ) { nss_SetError(NSS_ERROR_INVALID_ARGUMENT); return PR_FAILURE; } #endif /* DEBUG */ return nssATAV_Compare(atav1, atav2, equalp); } /* * NSSATAV_Duplicate * * This routine duplicates the specified ATAV. If the optional arena * argument is non-null, the memory required will be obtained from * that arena; otherwise, the memory will be obtained from the heap. * This routine may return NULL upon error, in which case it will have * created an error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL on error * A pointer to a new ATAV */ NSS_IMPLEMENT NSSATAV * NSSATAV_Duplicate ( NSSATAV *atav, NSSArena *arenaOpt ) { nss_ClearErrorStack(); #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSATAV *)NULL; } if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } #endif /* DEBUG */ return nssATAV_Duplicate(atav, arenaOpt); } /* * The pointer-tracking code */ #ifdef DEBUG extern const NSSError NSS_ERROR_INTERNAL_ERROR; static nssPointerTracker atav_pointer_tracker; static PRStatus atav_add_pointer ( const NSSATAV *atav ) { PRStatus rv; rv = nssPointerTracker_initialize(&atav_pointer_tracker); if( PR_SUCCESS != rv ) { return rv; } rv = nssPointerTracker_add(&atav_pointer_tracker, atav); if( PR_SUCCESS != rv ) { NSSError e = NSS_GetError(); if( NSS_ERROR_NO_MEMORY != e ) { nss_SetError(NSS_ERROR_INTERNAL_ERROR); } return rv; } return PR_SUCCESS; } static PRStatus atav_remove_pointer ( const NSSATAV *atav ) { PRStatus rv; rv = nssPointerTracker_remove(&atav_pointer_tracker, atav); if( PR_SUCCESS != rv ) { nss_SetError(NSS_ERROR_INTERNAL_ERROR); } return rv; } /* * nssATAV_verifyPointer * * This method is only present in debug builds. * * If the specified pointer is a valid pointer to an NSSATAV object, * this routine will return PR_SUCCESS. Otherwise, it will put an * error on the error stack and return PR_FAILRUE. * * The error may be one of the following values: * NSS_ERROR_INVALID_NSSATAV * NSS_ERROR_NO_MEMORY * * Return value: * PR_SUCCESS if the pointer is valid * PR_FAILURE if it isn't */ NSS_IMPLEMENT PRStatus nssATAV_verifyPointer ( NSSATAV *atav ) { PRStatus rv; rv = nssPointerTracker_initialize(&atav_pointer_tracker); if( PR_SUCCESS != rv ) { return PR_FAILURE; } rv = nssPointerTracker_verify(&atav_pointer_tracker, atav); if( PR_SUCCESS != rv ) { nss_SetError(NSS_ERROR_INVALID_ATAV); return PR_FAILURE; } return PR_SUCCESS; } #endif /* DEBUG */ typedef struct { NSSBER oid; NSSBER value; } atav_holder; static const nssASN1Template nss_atav_template[] = { { nssASN1_SEQUENCE, 0, NULL, sizeof(atav_holder) }, { nssASN1_OBJECT_ID, nsslibc_offsetof(atav_holder, oid), NULL, 0 }, { nssASN1_ANY, nsslibc_offsetof(atav_holder, value), NULL, 0 }, { 0, 0, NULL, 0 } }; /* * There are several common attributes, with well-known type aliases * and value semantics. This table lists the ones we recognize. */ struct nss_attribute_data_str { const NSSOID **oid; nssStringType stringType; PRUint32 minStringLength; PRUint32 maxStringLength; /* zero for no limit */ }; static const struct nss_attribute_data_str nss_attribute_data[] = { { &NSS_OID_X520_NAME, nssStringType_DirectoryString, 1, 32768 }, { &NSS_OID_X520_COMMON_NAME, nssStringType_DirectoryString, 1, 64 }, { &NSS_OID_X520_SURNAME, nssStringType_DirectoryString, 1, 40 }, { &NSS_OID_X520_GIVEN_NAME, nssStringType_DirectoryString, 1, 16 }, { &NSS_OID_X520_INITIALS, nssStringType_DirectoryString, 1, 5 }, { &NSS_OID_X520_GENERATION_QUALIFIER, nssStringType_DirectoryString, 1, 3 }, { &NSS_OID_X520_DN_QUALIFIER, nssStringType_PrintableString, 1, 0 }, { &NSS_OID_X520_COUNTRY_NAME, nssStringType_PrintableString, 2, 2 }, { &NSS_OID_X520_LOCALITY_NAME, nssStringType_DirectoryString, 1, 128 }, { &NSS_OID_X520_STATE_OR_PROVINCE_NAME, nssStringType_DirectoryString, 1, 128 }, { &NSS_OID_X520_ORGANIZATION_NAME, nssStringType_DirectoryString, 1, 64 }, { &NSS_OID_X520_ORGANIZATIONAL_UNIT_NAME, nssStringType_DirectoryString, 1, /* * Note, draft #11 defines both "32" and "64" for this maximum, * in two separate places. Until it's settled, "conservative * in what you send." We're always liberal in what we accept. */ 32 }, { &NSS_OID_X520_TITLE, nssStringType_DirectoryString, 1, 64 }, { &NSS_OID_RFC1274_EMAIL, nssStringType_PHGString, 1, 128 } }; PRUint32 nss_attribute_data_quantity = (sizeof(nss_attribute_data)/sizeof(nss_attribute_data[0])); static nssStringType nss_attr_underlying_string_form ( nssStringType type, void *data ) { if( nssStringType_DirectoryString == type ) { PRUint8 tag = *(PRUint8 *)data; switch( tag & nssASN1_TAGNUM_MASK ) { case 20: /* * XXX fgmr-- we have to accept Latin-1 for Teletex; (see * below) but is T61 a suitable value for "Latin-1"? */ return nssStringType_TeletexString; case 19: return nssStringType_PrintableString; case 28: return nssStringType_UniversalString; case 30: return nssStringType_BMPString; case 12: return nssStringType_UTF8String; default: return nssStringType_Unknown; } } return type; } /* * This routine decodes the attribute value, in a type-specific way. * */ static NSSUTF8 * nss_attr_to_utf8 ( NSSArena *arenaOpt, const NSSOID *oid, NSSItem *item, nssStringType *stringForm ) { NSSUTF8 *rv = (NSSUTF8 *)NULL; PRUint32 i; const struct nss_attribute_data_str *which = (struct nss_attribute_data_str *)NULL; PRUint32 len = 0; for( i = 0; i < nss_attribute_data_quantity; i++ ) { if( *(nss_attribute_data[ i ].oid) == oid ) { which = &nss_attribute_data[i]; break; } } if( (struct nss_attribute_data_str *)NULL == which ) { /* Unknown OID. Encode it as hex. */ PRUint8 *c; PRUint8 *d = (PRUint8 *)item->data; PRUint32 amt = item->size; if( item->size >= 0x7FFFFFFF ) { nss_SetError(NSS_ERROR_INVALID_STRING); return (NSSUTF8 *)NULL; } len = 1 + (item->size * 2) + 1; /* '#' + hex + '\0' */ rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len); if( (NSSUTF8 *)NULL == rv ) { return (NSSUTF8 *)NULL; } c = (PRUint8 *)rv; *c++ = '#'; /* XXX fgmr check this */ while( amt > 0 ) { static char hex[16] = "0123456789ABCDEF"; *c++ = hex[ ((*d) & 0xf0) >> 4 ]; *c++ = hex[ ((*d) & 0x0f) ]; } /* *c = '\0'; nss_ZAlloc, remember */ *stringForm = nssStringType_Unknown; /* force exact comparison */ } else { rv = nssUTF8_CreateFromBER(arenaOpt, which->stringType, (NSSBER *)item); if( (NSSUTF8 *)NULL == rv ) { return (NSSUTF8 *)NULL; } if( PR_SUCCESS != nssUTF8_Length(rv, &len) ) { nss_ZFreeIf(rv); return (NSSUTF8 *)NULL; } *stringForm = nss_attr_underlying_string_form(which->stringType, item->data); } return rv; } /* * nssATAV_CreateFromBER * * This routine creates an NSSATAV by decoding a BER- or DER-encoded * ATAV. If the optional arena argument is non-null, the memory used * will be obtained from that arena; otherwise, the memory will be * obtained from the heap. This routine may return NULL upon error, * in which case it will have set an error on the error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_BER * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSATAV upon success */ NSS_IMPLEMENT NSSATAV * nssATAV_CreateFromBER ( NSSArena *arenaOpt, const NSSBER *berATAV ) { atav_holder holder; PRStatus status; NSSATAV *rv; #ifdef NSSDEBUG if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } /* * NSSBERs can be created by the user, * so no pointer-tracking can be checked. */ if( (NSSBER *)NULL == berATAV ) { nss_SetError(NSS_ERROR_INVALID_BER); return (NSSATAV *)NULL; } if( (void *)NULL == berATAV->data ) { nss_SetError(NSS_ERROR_INVALID_BER); return (NSSATAV *)NULL; } #endif /* NSSDEBUG */ status = nssASN1_DecodeBER(arenaOpt, &holder, nss_atav_template, berATAV); if( PR_SUCCESS != status ) { return (NSSATAV *)NULL; } rv = nss_ZNEW(arenaOpt, NSSATAV); if( (NSSATAV *)NULL == rv ) { nss_ZFreeIf(holder.oid.data); nss_ZFreeIf(holder.value.data); return (NSSATAV *)NULL; } rv->oid = nssOID_CreateFromBER(&holder.oid); if( (NSSOID *)NULL == rv->oid ) { nss_ZFreeIf(rv); nss_ZFreeIf(holder.oid.data); nss_ZFreeIf(holder.value.data); return (NSSATAV *)NULL; } nss_ZFreeIf(holder.oid.data); rv->ber.data = nss_ZAlloc(arenaOpt, berATAV->size); if( (void *)NULL == rv->ber.data ) { nss_ZFreeIf(rv); nss_ZFreeIf(holder.value.data); return (NSSATAV *)NULL; } rv->ber.size = berATAV->size; (void)nsslibc_memcpy(rv->ber.data, berATAV->data, berATAV->size); rv->value = nss_attr_to_utf8(arenaOpt, rv->oid, &holder.value, &rv->stringForm); if( (NSSUTF8 *)NULL == rv->value ) { nss_ZFreeIf(rv->ber.data); nss_ZFreeIf(rv); nss_ZFreeIf(holder.value.data); return (NSSATAV *)NULL; } nss_ZFreeIf(holder.value.data); #ifdef DEBUG if( PR_SUCCESS != atav_add_pointer(rv) ) { nss_ZFreeIf(rv->ber.data); nss_ZFreeIf(rv->value); nss_ZFreeIf(rv); return (NSSATAV *)NULL; } #endif /* DEBUG */ return rv; } static PRBool nss_atav_utf8_string_is_hex ( const NSSUTF8 *s ) { /* All hex digits are ASCII, so this works */ PRUint8 *p = (PRUint8 *)s; for( ; (PRUint8)0 != *p; p++ ) { if( (('0' <= *p) && (*p <= '9')) || (('A' <= *p) && (*p <= 'F')) || (('a' <= *p) && (*p <= 'f')) ) { continue; } else { return PR_FALSE; } } return PR_TRUE; } static PRUint8 nss_atav_fromhex ( PRUint8 *d ) { PRUint8 rv; if( d[0] <= '9' ) { rv = (d[0] - '0') * 16; } else if( d[0] >= 'a' ) { rv = (d[0] - 'a' + 10) * 16; } else { rv = (d[0] - 'A' + 10); } if( d[1] <= '9' ) { rv += (d[1] - '0'); } else if( d[1] >= 'a' ) { rv += (d[1] - 'a' + 10); } else { rv += (d[1] - 'A' + 10); } return rv; } /* * nssATAV_CreateFromUTF8 * * This routine creates an NSSATAV by decoding a UTF8 string in the * "equals" format, e.g., "c=US." If the optional arena argument is * non-null, the memory used will be obtained from that arena; * otherwise, the memory will be obtained from the heap. This routine * may return NULL upon error, in which case it will have set an error * on the error stack. * * The error may be one of the following values: * NSS_ERROR_UNKNOWN_ATTRIBUTE * NSS_ERROR_INVALID_STRING * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSATAV upon success */ extern const NSSError NSS_ERROR_INTERNAL_ERROR; NSS_IMPLEMENT NSSATAV * nssATAV_CreateFromUTF8 ( NSSArena *arenaOpt, const NSSUTF8 *stringATAV ) { char *c; NSSUTF8 *type; NSSUTF8 *value; PRUint32 i; const NSSOID *oid = (NSSOID *)NULL; NSSATAV *rv; NSSItem xitem; xitem.data = (void *)NULL; for( c = (char *)stringATAV; '\0' != *c; c++ ) { if( '=' == *c ) { #ifdef PEDANTIC /* * Theoretically, one could have an '=' in an * attribute string alias. We don't, yet, though. */ if( (char *)stringATAV == c ) { nss_SetError(NSS_ERROR_INVALID_STRING); return (NSSATAV *)NULL; } else { if( '\\' == c[-1] ) { continue; } } #endif /* PEDANTIC */ break; } } if( '\0' == *c ) { nss_SetError(NSS_ERROR_INVALID_UTF8); return (NSSATAV *)NULL; } else { c++; value = (NSSUTF8 *)c; } i = ((NSSUTF8 *)c - stringATAV); type = (NSSUTF8 *)nss_ZAlloc((NSSArena *)NULL, i); if( (NSSUTF8 *)NULL == type ) { return (NSSATAV *)NULL; } (void)nsslibc_memcpy(type, stringATAV, i-1); c = (char *)stringATAV; if( (('0' <= *c) && (*c <= '9')) || ('#' == *c) ) { oid = nssOID_CreateFromUTF8(type); if( (NSSOID *)NULL == oid ) { nss_ZFreeIf(type); return (NSSATAV *)NULL; } } else { for( i = 0; i < nss_attribute_type_alias_count; i++ ) { const nssAttributeTypeAliasTable *e = &nss_attribute_type_aliases[i]; PRBool match = PR_FALSE; if( PR_SUCCESS != nssUTF8_CaseIgnoreMatch(type, e->alias, &match) ) { nss_ZFreeIf(type); return (NSSATAV *)NULL; } if( PR_TRUE == match ) { oid = *(e->oid); break; } } if( (NSSOID *)NULL == oid ) { nss_ZFreeIf(type); nss_SetError(NSS_ERROR_UNKNOWN_ATTRIBUTE); return (NSSATAV *)NULL; } } nss_ZFreeIf(type); type = (NSSUTF8 *)NULL; rv = nss_ZNEW(arenaOpt, NSSATAV); if( (NSSATAV *)NULL == rv ) { return (NSSATAV *)NULL; } rv->oid = oid; if( '#' == *value ) { /* XXX fgmr.. was it '#'? or backslash? */ PRUint32 size; PRUint32 len; PRUint8 *c; PRUint8 *d; /* It's in hex */ value++; if( PR_TRUE != nss_atav_utf8_string_is_hex(value) ) { (void)nss_ZFreeIf(rv); nss_SetError(NSS_ERROR_INVALID_STRING); return (NSSATAV *)NULL; } if( PR_SUCCESS != nssUTF8_Size(value, &size) ) { /* * Only returns an error on bad pointer (nope) or string * too long. The defined limits for known attributes are * small enough to fit in PRUint32, and when undefined we * get to apply our own practical limits. Ergo, I say the * string is invalid. */ (void)nss_ZFreeIf(rv); nss_SetError(NSS_ERROR_INVALID_STRING); return (NSSATAV *)NULL; } if( ((size-1) & 1) ) { /* odd length */ (void)nss_ZFreeIf(rv); nss_SetError(NSS_ERROR_INVALID_STRING); return (NSSATAV *)NULL; } len = (size-1)/2; rv->value = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1); if( (NSSUTF8 *)NULL == rv->value ) { (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } xitem.size = len; xitem.data = (void *)rv->value; for( c = rv->value, d = value; len--; c++, d += 2 ) { *c = nss_atav_fromhex(d); } *c = 0; } else { PRUint32 i, len; PRUint8 *s; /* * XXX fgmr-- okay, this is a little wasteful, and should * probably be abstracted out a bit. Later. */ rv->value = nssUTF8_Duplicate(value, arenaOpt); if( (NSSUTF8 *)NULL == rv->value ) { (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } if( PR_SUCCESS != nssUTF8_Size(rv->value, &len) ) { (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } s = (PRUint8 *)rv->value; for( i = 0; i < len; i++ ) { if( '\\' == s[i] ) { (void)nsslibc_memcpy(&s[i], &s[i+1], len-i-1); } } } /* Now just BER-encode the baby and we're through.. */ { const struct nss_attribute_data_str *which = (struct nss_attribute_data_str *)NULL; PRUint32 i; NSSArena *a; NSSDER *oidder; NSSItem *vitem; atav_holder ah; NSSDER *status; for( i = 0; i < nss_attribute_data_quantity; i++ ) { if( *(nss_attribute_data[ i ].oid) == rv->oid ) { which = &nss_attribute_data[i]; break; } } a = NSSArena_Create(); if( (NSSArena *)NULL == a ) { (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } oidder = nssOID_GetDEREncoding(rv->oid, a); if( (NSSDER *)NULL == oidder ) { (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } if( (struct nss_attribute_data_str *)NULL == which ) { /* * We'll just have to take the user data as an octet stream. */ if( (void *)NULL == xitem.data ) { /* * This means that an ATTR entry has been added to oids.txt, * but no corresponding entry has been added to the array * ns_attribute_data[] above. */ nss_SetError(NSS_ERROR_INTERNAL_ERROR); (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } vitem = nssASN1_EncodeDER(a, (NSSDER *)NULL, &xitem, nssASN1Template_OctetString); if( (NSSItem *)NULL == vitem ) { (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } rv->stringForm = nssStringType_Unknown; } else { PRUint32 length = 0; if( PR_SUCCESS != nssUTF8_Length(rv->value, &length) ) { (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } if( ((0 != which->minStringLength) && (length < which->minStringLength)) || ((0 != which->maxStringLength) && (length > which->maxStringLength)) ) { nss_SetError(NSS_ERROR_INVALID_STRING); (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } vitem = nssUTF8_GetDEREncoding(a, which->stringType, rv->value); if( (NSSItem *)NULL == vitem ) { (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } if( nssStringType_DirectoryString == which->stringType ) { rv->stringForm = nssStringType_UTF8String; } else { rv->stringForm = which->stringType; } } ah.oid = *oidder; ah.value = *vitem; status = nssASN1_EncodeDER(arenaOpt, &rv->ber, &ah, nss_atav_template); if( (NSSDER *)NULL == status ) { (void)NSSArena_Destroy(a); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } (void)NSSArena_Destroy(a); } return rv; } /* * nssATAV_Create * * This routine creates an NSSATAV from the specified NSSOID and the * specified data. If the optional arena argument is non-null, the * memory used will be obtained from that arena; otherwise, the memory * will be obtained from the heap.If the specified data length is zero, * the data is assumed to be terminated by first zero byte; this allows * UTF8 strings to be easily specified. This routine may return NULL * upon error, in which case it will have set an error on the error * stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ARENA * NSS_ERROR_INVALID_NSSOID * NSS_ERROR_INVALID_POINTER * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSATAV upon success */ NSS_IMPLEMENT NSSATAV * nssATAV_Create ( NSSArena *arenaOpt, const NSSOID *oid, const void *data, PRUint32 length ) { #ifdef NSSDEBUG if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } if( PR_SUCCESS != nssOID_verifyPointer(oid) ) { return (NSSATAV *)NULL; } if( (const void *)NULL == data ) { nss_SetError(NSS_ERROR_INVALID_POINTER); return (NSSATAV *)NULL; } #endif /* NSSDEBUG */ /* XXX fgmr-- oops, forgot this one */ return (NSSATAV *)NULL; } /* * nssATAV_Destroy * * This routine will destroy an ATAV object. It should eventually be * called on all ATAVs created without an arena. While it is not * necessary to call it on ATAVs created within an arena, it is not an * error to do so. This routine returns a PRStatus value; if * successful, it will return PR_SUCCESS. If unsuccessful, it will * set an error on the error stack and return PR_FAILURE. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * * Return value: * PR_FAILURE upon error * PR_SUCCESS upon success */ NSS_IMPLEMENT PRStatus nssATAV_Destroy ( NSSATAV *atav ) { #ifdef NSSDEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return PR_FAILURE; } #endif /* NSSDEBUG */ (void)nss_ZFreeIf(atav->ber.data); (void)nss_ZFreeIf(atav->value); #ifdef DEBUG if( PR_SUCCESS != atav_remove_pointer(atav) ) { return PR_FAILURE; } #endif /* DEBUG */ return PR_SUCCESS; } /* * nssATAV_GetDEREncoding * * This routine will DER-encode an ATAV object. If the optional arena * argument is non-null, the memory used will be obtained from that * arena; otherwise, the memory will be obtained from the heap. This * routine may return null upon error, in which case it will have set * an error on the error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * The DER encoding of this NSSATAV */ NSS_IMPLEMENT NSSDER * nssATAV_GetDEREncoding ( NSSATAV *atav, NSSArena *arenaOpt ) { NSSDER *rv; #ifdef NSSDEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSDER *)NULL; } #endif /* NSSDEBUG */ rv = nss_ZNEW(arenaOpt, NSSDER); if( (NSSDER *)NULL == rv ) { return (NSSDER *)NULL; } rv->data = nss_ZAlloc(arenaOpt, atav->ber.size); if( (void *)NULL == rv->data ) { (void)nss_ZFreeIf(rv); return (NSSDER *)NULL; } rv->size = atav->ber.size; if( PR_SUCCESS != nsslibc_memcpy(rv->data, atav->ber.data, rv->size) ) { (void)nss_ZFreeIf(rv->data); (void)nss_ZFreeIf(rv); return (NSSDER *)NULL; } return rv; } /* * nssATAV_GetUTF8Encoding * * This routine returns a UTF8 string containing a string * representation of the ATAV in "equals" notation (e.g., "o=Acme"). * If the optional arena argument is non-null, the memory used will be * obtained from that arena; otherwise, the memory will be obtained * from the heap. This routine may return null upon error, in which * case it will have set an error on the error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to a UTF8 string containing the "equals" encoding of the * ATAV */ NSS_IMPLEMENT NSSUTF8 * nssATAV_GetUTF8Encoding ( NSSATAV *atav, NSSArena *arenaOpt ) { NSSUTF8 *rv; PRUint32 i; const NSSUTF8 *alias = (NSSUTF8 *)NULL; NSSUTF8 *oid; NSSUTF8 *value; PRUint32 oidlen; PRUint32 valuelen; PRUint32 totallen; #ifdef NSSDEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSUTF8 *)NULL; } #endif /* NSSDEBUG */ for( i = 0; i < nss_attribute_type_alias_count; i++ ) { if( *(nss_attribute_type_aliases[i].oid) == atav->oid ) { alias = nss_attribute_type_aliases[i].alias; break; } } if( (NSSUTF8 *)NULL == alias ) { oid = nssOID_GetUTF8Encoding(atav->oid, (NSSArena *)NULL); if( (NSSUTF8 *)NULL == oid ) { return (NSSUTF8 *)NULL; } if( PR_SUCCESS != nssUTF8_Size(oid, &oidlen) ) { (void)nss_ZFreeIf(oid); return (NSSUTF8 *)NULL; } } else { if( PR_SUCCESS != nssUTF8_Size(alias, &oidlen) ) { return (NSSUTF8 *)NULL; } oid = (NSSUTF8 *)NULL; } value = nssATAV_GetValue(atav, (NSSArena *)NULL); if( (NSSUTF8 *)NULL == value ) { (void)nss_ZFreeIf(oid); return (NSSUTF8 *)NULL; } if( PR_SUCCESS != nssUTF8_Size(value, &valuelen) ) { (void)nss_ZFreeIf(value); (void)nss_ZFreeIf(oid); return (NSSUTF8 *)NULL; } totallen = oidlen + valuelen - 1 + 1; rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, totallen); if( (NSSUTF8 *)NULL == rv ) { (void)nss_ZFreeIf(value); (void)nss_ZFreeIf(oid); return (NSSUTF8 *)NULL; } if( (NSSUTF8 *)NULL == alias ) { if( (void *)NULL == nsslibc_memcpy(rv, oid, oidlen-1) ) { (void)nss_ZFreeIf(rv); (void)nss_ZFreeIf(value); (void)nss_ZFreeIf(oid); return (NSSUTF8 *)NULL; } } else { if( (void *)NULL == nsslibc_memcpy(rv, alias, oidlen-1) ) { (void)nss_ZFreeIf(rv); (void)nss_ZFreeIf(value); return (NSSUTF8 *)NULL; } } rv[ oidlen-1 ] = '='; if( (void *)NULL == nsslibc_memcpy(&rv[oidlen], value, valuelen) ) { (void)nss_ZFreeIf(rv); (void)nss_ZFreeIf(value); (void)nss_ZFreeIf(oid); return (NSSUTF8 *)NULL; } return rv; } /* * nssATAV_GetType * * This routine returns the NSSOID corresponding to the attribute type * in the specified ATAV. This routine may return NSS_OID_UNKNOWN * upon error, in which case it will have set an error on the error * stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * * Return value: * NSS_OID_UNKNOWN upon error * A valid NSSOID pointer upon success */ NSS_IMPLEMENT const NSSOID * nssATAV_GetType ( NSSATAV *atav ) { #ifdef NSSDEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSOID *)NULL; } #endif /* NSSDEBUG */ return atav->oid; } /* * nssATAV_GetValue * * This routine returns a NSSUTF8 string containing the attribute value * in the specified ATAV. If the optional arena argument is non-null, * the memory used will be obtained from that arena; otherwise, the * memory will be obtained from the heap. This routine may return * NULL upon error, in which case it will have set an error upon the * error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL upon error * A pointer to an NSSItem containing the attribute value. */ NSS_IMPLEMENT NSSUTF8 * nssATAV_GetValue ( NSSATAV *atav, NSSArena *arenaOpt ) { #ifdef NSSDEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSUTF8 *)NULL; } #endif /* NSSDEBUG */ return nssUTF8_Duplicate(atav->value, arenaOpt); } /* * nssATAV_Compare * * This routine compares two ATAVs for equality. For two ATAVs to be * equal, the attribute types must be the same, and the attribute * values must have equal length and contents. The result of the * comparison will be stored at the location pointed to by the "equalp" * variable, which must point to a valid PRBool. This routine may * return PR_FAILURE upon error, in which case it will have set an * error on the error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_INVALID_ARGUMENT * * Return value: * PR_FAILURE on error * PR_SUCCESS upon a successful comparison (equal or not) */ NSS_IMPLEMENT PRStatus nssATAV_Compare ( NSSATAV *atav1, NSSATAV *atav2, PRBool *equalp ) { nssStringType comparison; PRUint32 len1; PRUint32 len2; #ifdef DEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav1) ) { return PR_FAILURE; } if( PR_SUCCESS != nssATAV_verifyPointer(atav2) ) { return PR_FAILURE; } if( (PRBool *)NULL == equalp ) { nss_SetError(NSS_ERROR_INVALID_ARGUMENT); return PR_FAILURE; } #endif /* DEBUG */ if( atav1->oid != atav2->oid ) { *equalp = PR_FALSE; return PR_SUCCESS; } if( atav1->stringForm != atav2->stringForm ) { if( (nssStringType_PrintableString == atav1->stringForm) || (nssStringType_PrintableString == atav2->stringForm) ) { comparison = nssStringType_PrintableString; } else if( (nssStringType_PHGString == atav1->stringForm) || (nssStringType_PHGString == atav2->stringForm) ) { comparison = nssStringType_PHGString; } else { comparison = atav1->stringForm; } } else { comparison = atav1->stringForm; } switch( comparison ) { case nssStringType_DirectoryString: nss_SetError(NSS_ERROR_INTERNAL_ERROR); return PR_FAILURE; case nssStringType_TeletexString: break; case nssStringType_PrintableString: return nssUTF8_PrintableMatch(atav1->value, atav2->value, equalp); /* Case-insensitive, with whitespace reduction */ break; case nssStringType_UniversalString: break; case nssStringType_BMPString: break; case nssStringType_UTF8String: break; case nssStringType_PHGString: /* Case-insensitive (XXX fgmr, actually see draft-11 pg. 21) */ return nssUTF8_CaseIgnoreMatch(atav1->value, atav2->value, equalp); case nssStringType_Unknown: break; } if( PR_SUCCESS != nssUTF8_Size(atav1->value, &len1) ) { return PR_FAILURE; } if( PR_SUCCESS != nssUTF8_Size(atav2->value, &len2) ) { return PR_FAILURE; } if( len1 != len2 ) { *equalp = PR_FALSE; return PR_SUCCESS; } return nsslibc_compare(atav1->value, atav2->value, len1, equalp); } /* * nssATAV_Duplicate * * This routine duplicates the specified ATAV. If the optional arena * argument is non-null, the memory required will be obtained from * that arena; otherwise, the memory will be obtained from the heap. * This routine may return NULL upon error, in which case it will have * placed an error on the error stack. * * The error may be one of the following values: * NSS_ERROR_INVALID_ATAV * NSS_ERROR_NO_MEMORY * * Return value: * NULL on error * A pointer to a new ATAV */ NSS_IMPLEMENT NSSATAV * nssATAV_Duplicate ( NSSATAV *atav, NSSArena *arenaOpt ) { NSSATAV *rv; #ifdef NSSDEBUG if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) { return (NSSATAV *)NULL; } if( (NSSArena *)NULL != arenaOpt ) { if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { return (NSSATAV *)NULL; } } #endif /* NSSDEBUG */ rv = nss_ZNEW(arenaOpt, NSSATAV); if( (NSSATAV *)NULL == rv ) { return (NSSATAV *)NULL; } rv->oid = atav->oid; rv->stringForm = atav->stringForm; rv->value = nssUTF8_Duplicate(atav->value, arenaOpt); if( (NSSUTF8 *)NULL == rv->value ) { (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } rv->ber.data = nss_ZAlloc(arenaOpt, atav->ber.size); if( (void *)NULL == rv->ber.data ) { (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } rv->ber.size = atav->ber.size; if( PR_SUCCESS != nsslibc_memcpy(rv->ber.data, atav->ber.data, atav->ber.size) ) { (void)nss_ZFreeIf(rv->ber.data); (void)nss_ZFreeIf(rv->value); (void)nss_ZFreeIf(rv); return (NSSATAV *)NULL; } return rv; }