diff options
Diffstat (limited to 'security/nss/lib/pki1/atav.c')
-rw-r--r-- | security/nss/lib/pki1/atav.c | 1803 |
1 files changed, 1803 insertions, 0 deletions
diff --git a/security/nss/lib/pki1/atav.c b/security/nss/lib/pki1/atav.c new file mode 100644 index 000000000..299ddfc0d --- /dev/null +++ b/security/nss/lib/pki1/atav.c @@ -0,0 +1,1803 @@ +/* + * 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; +} |