Blame nss/lib/certdb/alg1485.c

Packit 40b132
/* alg1485.c - implementation of RFCs 1485, 1779 and 2253.
Packit 40b132
 *
Packit 40b132
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit 40b132
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit 40b132
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit 40b132
Packit 40b132
#include "prprf.h"
Packit 40b132
#include "cert.h"
Packit 40b132
#include "certi.h"
Packit 40b132
#include "xconst.h"
Packit 40b132
#include "genname.h"
Packit 40b132
#include "secitem.h"
Packit 40b132
#include "secerr.h"
Packit 40b132
Packit 40b132
typedef struct NameToKindStr {
Packit 40b132
    const char * name;
Packit 40b132
    unsigned int maxLen; /* max bytes in UTF8 encoded string value */
Packit 40b132
    SECOidTag    kind;
Packit 40b132
    int		 valueType;
Packit 40b132
} NameToKind;
Packit 40b132
Packit 40b132
/* local type for directory string--could be printable_string or utf8 */
Packit 40b132
#define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER
Packit 40b132
Packit 40b132
/* Add new entries to this table, and maybe to function ParseRFC1485AVA */
Packit 40b132
static const NameToKind name2kinds[] = {
Packit 40b132
/* IANA registered type names
Packit 40b132
 * (See: http://www.iana.org/assignments/ldap-parameters) 
Packit 40b132
 */
Packit 40b132
/* RFC 3280, 4630 MUST SUPPORT */
Packit 40b132
    { "CN",            640, SEC_OID_AVA_COMMON_NAME,    SEC_ASN1_DS},
Packit 40b132
    { "ST",            128, SEC_OID_AVA_STATE_OR_PROVINCE,
Packit 40b132
							SEC_ASN1_DS},
Packit 40b132
    { "O",             128, SEC_OID_AVA_ORGANIZATION_NAME,
Packit 40b132
							SEC_ASN1_DS},
Packit 40b132
    { "OU",            128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
Packit 40b132
                                                        SEC_ASN1_DS},
Packit 40b132
    { "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING},
Packit 40b132
    { "C",               2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING},
Packit 40b132
    { "serialNumber",   64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING},
Packit 40b132
Packit 40b132
/* RFC 3280, 4630 SHOULD SUPPORT */
Packit 40b132
    { "L",             128, SEC_OID_AVA_LOCALITY,       SEC_ASN1_DS},
Packit 40b132
    { "title",          64, SEC_OID_AVA_TITLE,          SEC_ASN1_DS},
Packit 40b132
    { "SN",             64, SEC_OID_AVA_SURNAME,        SEC_ASN1_DS},
Packit 40b132
    { "givenName",      64, SEC_OID_AVA_GIVEN_NAME,     SEC_ASN1_DS},
Packit 40b132
    { "initials",       64, SEC_OID_AVA_INITIALS,       SEC_ASN1_DS},
Packit 40b132
    { "generationQualifier",
Packit 40b132
                        64, SEC_OID_AVA_GENERATION_QUALIFIER,
Packit 40b132
                                                        SEC_ASN1_DS},
Packit 40b132
/* RFC 3280, 4630 MAY SUPPORT */
Packit 40b132
    { "DC",            128, SEC_OID_AVA_DC,             SEC_ASN1_IA5_STRING},
Packit 40b132
    { "MAIL",          256, SEC_OID_RFC1274_MAIL,       SEC_ASN1_IA5_STRING},
Packit 40b132
    { "UID",           256, SEC_OID_RFC1274_UID,        SEC_ASN1_DS},
Packit 40b132
Packit 40b132
/* ------------------ "strict" boundary ---------------------------------
Packit 40b132
 * In strict mode, cert_NameToAscii does not encode any of the attributes
Packit 40b132
 * below this line. The first SECOidTag below this line must be used to
Packit 40b132
 * conditionally define the "endKind" in function AppendAVA() below.
Packit 40b132
 * Most new attribute names should be added below this line.
Packit 40b132
 * Maybe this line should be up higher?  Say, after the 3280 MUSTs and 
Packit 40b132
 * before the 3280 SHOULDs?
Packit 40b132
 */
Packit 40b132
Packit 40b132
/* values from draft-ietf-ldapbis-user-schema-05 (not in RFC 3280) */
Packit 40b132
    { "postalAddress", 128, SEC_OID_AVA_POSTAL_ADDRESS, SEC_ASN1_DS},
Packit 40b132
    { "postalCode",     40, SEC_OID_AVA_POSTAL_CODE,    SEC_ASN1_DS},
Packit 40b132
    { "postOfficeBox",  40, SEC_OID_AVA_POST_OFFICE_BOX,SEC_ASN1_DS},
Packit 40b132
    { "houseIdentifier",64, SEC_OID_AVA_HOUSE_IDENTIFIER,SEC_ASN1_DS},
Packit 40b132
/* end of IANA registered type names */
Packit 40b132
Packit 40b132
/* legacy keywords */
Packit 40b132
    { "E",             128, SEC_OID_PKCS9_EMAIL_ADDRESS,SEC_ASN1_IA5_STRING},
Packit 40b132
    { "STREET",        128, SEC_OID_AVA_STREET_ADDRESS, SEC_ASN1_DS},
Packit 40b132
    { "pseudonym",      64, SEC_OID_AVA_PSEUDONYM,      SEC_ASN1_DS},
Packit 40b132
Packit 40b132
/* values defined by the CAB Forum for EV */
Packit 40b132
    { "incorporationLocality", 128, SEC_OID_EV_INCORPORATION_LOCALITY,
Packit 40b132
                                                        SEC_ASN1_DS},
Packit 40b132
    { "incorporationState",    128, SEC_OID_EV_INCORPORATION_STATE,
Packit 40b132
                                                        SEC_ASN1_DS},
Packit 40b132
    { "incorporationCountry",    2, SEC_OID_EV_INCORPORATION_COUNTRY,
Packit 40b132
                                                    SEC_ASN1_PRINTABLE_STRING},
Packit 40b132
    { "businessCategory",       64, SEC_OID_BUSINESS_CATEGORY, SEC_ASN1_DS},
Packit 40b132
Packit 40b132
/* values defined in X.520 */
Packit 40b132
    { "name",           64, SEC_OID_AVA_NAME,           SEC_ASN1_DS},
Packit 40b132
Packit 40b132
    { 0,               256, SEC_OID_UNKNOWN,            0},
Packit 40b132
};
Packit 40b132
Packit 40b132
/* Table facilitates conversion of ASCII hex to binary. */
Packit 40b132
static const PRInt16 x2b[256] = {
Packit 40b132
/* #0x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #1x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #2x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #3x */  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #4x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #5x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #6x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #7x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #8x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #9x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #ax */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #bx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #cx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #dx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #ex */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
Packit 40b132
/* #fx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
Packit 40b132
};
Packit 40b132
Packit 40b132
#define IS_HEX(c) (x2b[(PRUint8)(c)] >= 0)
Packit 40b132
Packit 40b132
#define C_DOUBLE_QUOTE '\042'
Packit 40b132
Packit 40b132
#define C_BACKSLASH '\134'
Packit 40b132
Packit 40b132
#define C_EQUAL '='
Packit 40b132
Packit 40b132
#define OPTIONAL_SPACE(c) \
Packit 40b132
    (((c) == ' ') || ((c) == '\r') || ((c) == '\n'))
Packit 40b132
Packit 40b132
#define SPECIAL_CHAR(c)						\
Packit 40b132
    (((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) ||	\
Packit 40b132
     ((c) == '\r') || ((c) == '\n') || ((c) == '+') ||		\
Packit 40b132
     ((c) == '<') || ((c) == '>') || ((c) == '#') ||		\
Packit 40b132
     ((c) == ';') || ((c) == C_BACKSLASH))
Packit 40b132
Packit 40b132
Packit 40b132
#define IS_PRINTABLE(c)						\
Packit 40b132
    ((((c) >= 'a') && ((c) <= 'z')) ||				\
Packit 40b132
     (((c) >= 'A') && ((c) <= 'Z')) ||				\
Packit 40b132
     (((c) >= '0') && ((c) <= '9')) ||				\
Packit 40b132
     ((c) == ' ') ||						\
Packit 40b132
     ((c) == '\'') ||						\
Packit 40b132
     ((c) == '\050') ||				/* ( */		\
Packit 40b132
     ((c) == '\051') ||				/* ) */		\
Packit 40b132
     (((c) >= '+') && ((c) <= '/')) ||		/* + , - . / */	\
Packit 40b132
     ((c) == ':') ||						\
Packit 40b132
     ((c) == '=') ||						\
Packit 40b132
     ((c) == '?'))
Packit 40b132
Packit 40b132
/* RFC 2253 says we must escape ",+\"\\<>;=" EXCEPT inside a quoted string.
Packit 40b132
 * Inside a quoted string, we only need to escape " and \
Packit 40b132
 * We choose to quote strings containing any of those special characters,
Packit 40b132
 * so we only need to escape " and \
Packit 40b132
 */
Packit 40b132
#define NEEDS_ESCAPE(c) \
Packit 40b132
    (c == C_DOUBLE_QUOTE || c == C_BACKSLASH)
Packit 40b132
Packit 40b132
#define NEEDS_HEX_ESCAPE(c) \
Packit 40b132
    ((PRUint8)c < 0x20 || c == 0x7f)
Packit 40b132
Packit 40b132
int
Packit 40b132
cert_AVAOidTagToMaxLen(SECOidTag tag)
Packit 40b132
{
Packit 40b132
    const NameToKind *n2k = name2kinds;
Packit 40b132
Packit 40b132
    while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) {
Packit 40b132
	++n2k;
Packit 40b132
    }
Packit 40b132
    return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1;
Packit 40b132
}
Packit 40b132
Packit 40b132
static PRBool
Packit 40b132
IsPrintable(unsigned char *data, unsigned len)
Packit 40b132
{
Packit 40b132
    unsigned char ch, *end;
Packit 40b132
Packit 40b132
    end = data + len;
Packit 40b132
    while (data < end) {
Packit 40b132
	ch = *data++;
Packit 40b132
	if (!IS_PRINTABLE(ch)) {
Packit 40b132
	    return PR_FALSE;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return PR_TRUE;
Packit 40b132
}
Packit 40b132
Packit 40b132
static void
Packit 40b132
skipSpace(const char **pbp, const char *endptr)
Packit 40b132
{
Packit 40b132
    const char *bp = *pbp;
Packit 40b132
    while (bp < endptr && OPTIONAL_SPACE(*bp)) {
Packit 40b132
	bp++;
Packit 40b132
    }
Packit 40b132
    *pbp = bp;
Packit 40b132
}
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
scanTag(const char **pbp, const char *endptr, char *tagBuf, int tagBufSize)
Packit 40b132
{
Packit 40b132
    const char *bp;
Packit 40b132
    char *tagBufp;
Packit 40b132
    int taglen;
Packit 40b132
Packit 40b132
    PORT_Assert(tagBufSize > 0);
Packit 40b132
    
Packit 40b132
    /* skip optional leading space */
Packit 40b132
    skipSpace(pbp, endptr);
Packit 40b132
    if (*pbp == endptr) {
Packit 40b132
	/* nothing left */
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    /* fill tagBuf */
Packit 40b132
    taglen = 0;
Packit 40b132
    bp = *pbp;
Packit 40b132
    tagBufp = tagBuf;
Packit 40b132
    while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
Packit 40b132
	if (++taglen >= tagBufSize) {
Packit 40b132
	    *pbp = bp;
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
	*tagBufp++ = *bp++;
Packit 40b132
    }
Packit 40b132
    /* null-terminate tagBuf -- guaranteed at least one space left */
Packit 40b132
    *tagBufp++ = 0;
Packit 40b132
    *pbp = bp;
Packit 40b132
    
Packit 40b132
    /* skip trailing spaces till we hit something - should be an equal sign */
Packit 40b132
    skipSpace(pbp, endptr);
Packit 40b132
    if (*pbp == endptr) {
Packit 40b132
	/* nothing left */
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    if (**pbp != C_EQUAL) {
Packit 40b132
	/* should be an equal sign */
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    /* skip over the equal sign */
Packit 40b132
    (*pbp)++;
Packit 40b132
    
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Returns the number of bytes in the value. 0 means failure. */
Packit 40b132
static int
Packit 40b132
scanVal(const char **pbp, const char *endptr, char *valBuf, int valBufSize)  
Packit 40b132
{
Packit 40b132
    const char *bp;
Packit 40b132
    char *valBufp;
Packit 40b132
    int vallen = 0;
Packit 40b132
    PRBool isQuoted;
Packit 40b132
    
Packit 40b132
    PORT_Assert(valBufSize > 0);
Packit 40b132
    
Packit 40b132
    /* skip optional leading space */
Packit 40b132
    skipSpace(pbp, endptr);
Packit 40b132
    if(*pbp == endptr) {
Packit 40b132
	/* nothing left */
Packit 40b132
	return 0;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    bp = *pbp;
Packit 40b132
    
Packit 40b132
    /* quoted? */
Packit 40b132
    if (*bp == C_DOUBLE_QUOTE) {
Packit 40b132
	isQuoted = PR_TRUE;
Packit 40b132
	/* skip over it */
Packit 40b132
	bp++;
Packit 40b132
    } else {
Packit 40b132
	isQuoted = PR_FALSE;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    valBufp = valBuf;
Packit 40b132
    while (bp < endptr) {
Packit 40b132
	char c = *bp;
Packit 40b132
	if (c == C_BACKSLASH) {
Packit 40b132
	    /* escape character */
Packit 40b132
	    bp++;
Packit 40b132
	    if (bp >= endptr) {
Packit 40b132
		/* escape charater must appear with paired char */
Packit 40b132
		*pbp = bp;
Packit 40b132
		return 0;
Packit 40b132
	    }
Packit 40b132
	    c = *bp;
Packit 40b132
	    if (IS_HEX(c) && (endptr - bp) >= 2 && IS_HEX(bp[1])) {
Packit 40b132
		bp++;
Packit 40b132
		c = (char)((x2b[(PRUint8)c] << 4) | x2b[(PRUint8)*bp]); 
Packit 40b132
	    }
Packit 40b132
	} else if (c == '#' && bp == *pbp) {
Packit 40b132
	    /* ignore leading #, quotation not required for it. */
Packit 40b132
	} else if (!isQuoted && SPECIAL_CHAR(c)) {
Packit 40b132
	    /* unescaped special and not within quoted value */
Packit 40b132
	    break;
Packit 40b132
	} else if (c == C_DOUBLE_QUOTE) {
Packit 40b132
	    /* reached unescaped double quote */
Packit 40b132
	    break;
Packit 40b132
	}
Packit 40b132
	/* append character */
Packit 40b132
        vallen++;
Packit 40b132
	if (vallen >= valBufSize) {
Packit 40b132
	    *pbp = bp;
Packit 40b132
	    return 0;
Packit 40b132
	}
Packit 40b132
	*valBufp++ = c;
Packit 40b132
	bp++;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    /* strip trailing spaces from unquoted values */
Packit 40b132
    if (!isQuoted) {
Packit 40b132
	while (valBufp > valBuf) {
Packit 40b132
	    char c = valBufp[-1];
Packit 40b132
	    if (! OPTIONAL_SPACE(c))
Packit 40b132
	        break;
Packit 40b132
	    --valBufp;
Packit 40b132
	}
Packit 40b132
	vallen = valBufp - valBuf;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    if (isQuoted) {
Packit 40b132
	/* insist that we stopped on a double quote */
Packit 40b132
	if (*bp != C_DOUBLE_QUOTE) {
Packit 40b132
	    *pbp = bp;
Packit 40b132
	    return 0;
Packit 40b132
	}
Packit 40b132
	/* skip over the quote and skip optional space */
Packit 40b132
	bp++;
Packit 40b132
	skipSpace(&bp, endptr);
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    *pbp = bp;
Packit 40b132
    
Packit 40b132
    /* null-terminate valBuf -- guaranteed at least one space left */
Packit 40b132
    *valBufp = 0;
Packit 40b132
    
Packit 40b132
    return vallen;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Caller must set error code upon failure */
Packit 40b132
static SECStatus
Packit 40b132
hexToBin(PLArenaPool *pool, SECItem * destItem, const char * src, int len)
Packit 40b132
{
Packit 40b132
    PRUint8 * dest;
Packit 40b132
Packit 40b132
    destItem->data = NULL; 
Packit 40b132
    if (len <= 0 || (len & 1)) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    len >>= 1;
Packit 40b132
    if (!SECITEM_AllocItem(pool, destItem, len))
Packit 40b132
	goto loser;
Packit 40b132
    dest = destItem->data;
Packit 40b132
    for (; len > 0; len--, src += 2) {
Packit 40b132
	PRInt16 bin = (x2b[(PRUint8)src[0]] << 4) | x2b[(PRUint8)src[1]]; 
Packit 40b132
	if (bin < 0)
Packit 40b132
	    goto loser;
Packit 40b132
	*dest++ = (PRUint8)bin;
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
loser:
Packit 40b132
    if (!pool)
Packit 40b132
    	SECITEM_FreeItem(destItem, PR_FALSE);
Packit 40b132
    return SECFailure;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Parses one AVA, starting at *pbp.  Stops at endptr.
Packit 40b132
 * Advances *pbp past parsed AVA and trailing separator (if present).
Packit 40b132
 * On any error, returns NULL and *pbp is undefined.
Packit 40b132
 * On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was 
Packit 40b132
 * the last character parsed.  *pbp is either equal to endptr or 
Packit 40b132
 * points to first character after separator.
Packit 40b132
 */
Packit 40b132
static CERTAVA *
Packit 40b132
ParseRFC1485AVA(PLArenaPool *arena, const char **pbp, const char *endptr)
Packit 40b132
{
Packit 40b132
    CERTAVA *a;
Packit 40b132
    const NameToKind *n2k;
Packit 40b132
    const char *bp;
Packit 40b132
    int       vt = -1;
Packit 40b132
    int       valLen;
Packit 40b132
    SECOidTag kind  = SEC_OID_UNKNOWN;
Packit 40b132
    SECStatus rv    = SECFailure;
Packit 40b132
    SECItem   derOid = { 0, NULL, 0 };
Packit 40b132
    SECItem   derVal = { 0, NULL, 0};
Packit 40b132
    char      sep   = 0;
Packit 40b132
Packit 40b132
    char tagBuf[32];
Packit 40b132
    char valBuf[1024];
Packit 40b132
Packit 40b132
    PORT_Assert(arena);
Packit 40b132
    if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) ||
Packit 40b132
	!(valLen    = scanVal(pbp, endptr, valBuf, sizeof valBuf))) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    bp = *pbp;
Packit 40b132
    if (bp < endptr) {
Packit 40b132
	sep = *bp++; /* skip over separator */
Packit 40b132
    }
Packit 40b132
    *pbp = bp;
Packit 40b132
    /* if we haven't finished, insist that we've stopped on a separator */
Packit 40b132
    if (sep && sep != ',' && sep != ';' && sep != '+') {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* is this a dotted decimal OID attribute type ? */
Packit 40b132
    if (!PL_strncasecmp("oid.", tagBuf, 4)) {
Packit 40b132
        rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf));
Packit 40b132
    } else {
Packit 40b132
	for (n2k = name2kinds; n2k->name; n2k++) {
Packit 40b132
	    SECOidData *oidrec;
Packit 40b132
	    if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) {
Packit 40b132
		kind = n2k->kind;
Packit 40b132
		vt   = n2k->valueType;
Packit 40b132
		oidrec = SECOID_FindOIDByTag(kind);
Packit 40b132
		if (oidrec == NULL)
Packit 40b132
		    goto loser;
Packit 40b132
		derOid = oidrec->oid;
Packit 40b132
		break;
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    if (kind == SEC_OID_UNKNOWN && rv != SECSuccess) 
Packit 40b132
	goto loser;
Packit 40b132
Packit 40b132
    /* Is this a hex encoding of a DER attribute value ? */
Packit 40b132
    if ('#' == valBuf[0]) {
Packit 40b132
    	/* convert attribute value from hex to binary */
Packit 40b132
	rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1);
Packit 40b132
	if (rv)
Packit 40b132
	    goto loser;
Packit 40b132
	a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal);
Packit 40b132
    } else {
Packit 40b132
	if (kind == SEC_OID_UNKNOWN)
Packit 40b132
	    goto loser;
Packit 40b132
	if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2)
Packit 40b132
	    goto loser;
Packit 40b132
	if (vt == SEC_ASN1_PRINTABLE_STRING &&
Packit 40b132
	    !IsPrintable((unsigned char*) valBuf, valLen)) 
Packit 40b132
	    goto loser;
Packit 40b132
	if (vt == SEC_ASN1_DS) {
Packit 40b132
	    /* RFC 4630: choose PrintableString or UTF8String */
Packit 40b132
	    if (IsPrintable((unsigned char*) valBuf, valLen))
Packit 40b132
		vt = SEC_ASN1_PRINTABLE_STRING;
Packit 40b132
	    else 
Packit 40b132
		vt = SEC_ASN1_UTF8_STRING;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	derVal.data = (unsigned char*) valBuf;
Packit 40b132
	derVal.len  = valLen;
Packit 40b132
	a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal);
Packit 40b132
    }
Packit 40b132
    return a;
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    /* matched no kind -- invalid tag */
Packit 40b132
    PORT_SetError(SEC_ERROR_INVALID_AVA);
Packit 40b132
    return 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
static CERTName *
Packit 40b132
ParseRFC1485Name(const char *buf, int len)
Packit 40b132
{
Packit 40b132
    SECStatus rv;
Packit 40b132
    CERTName *name;
Packit 40b132
    const char *bp, *e;
Packit 40b132
    CERTAVA *ava;
Packit 40b132
    CERTRDN *rdn = NULL;
Packit 40b132
Packit 40b132
    name = CERT_CreateName(NULL);
Packit 40b132
    if (name == NULL) {
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    e = buf + len;
Packit 40b132
    bp = buf;
Packit 40b132
    while (bp < e) {
Packit 40b132
	ava = ParseRFC1485AVA(name->arena, &bp, e);
Packit 40b132
	if (ava == 0) 
Packit 40b132
	    goto loser;
Packit 40b132
	if (!rdn) {
Packit 40b132
	    rdn = CERT_CreateRDN(name->arena, ava, (CERTAVA *)0);
Packit 40b132
	    if (rdn == 0) 
Packit 40b132
		goto loser;
Packit 40b132
	    rv = CERT_AddRDN(name, rdn);
Packit 40b132
	} else {
Packit 40b132
	    rv = CERT_AddAVA(name->arena, rdn, ava);
Packit 40b132
	}
Packit 40b132
	if (rv) 
Packit 40b132
	    goto loser;
Packit 40b132
	if (bp[-1] != '+')
Packit 40b132
	    rdn = NULL; /* done with this RDN */
Packit 40b132
	skipSpace(&bp, e);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (name->rdns[0] == 0) {
Packit 40b132
	/* empty name -- illegal */
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Reverse order of RDNS to comply with RFC */
Packit 40b132
    {
Packit 40b132
	CERTRDN **firstRdn;
Packit 40b132
	CERTRDN **lastRdn;
Packit 40b132
	CERTRDN *tmp;
Packit 40b132
	
Packit 40b132
	/* get first one */
Packit 40b132
	firstRdn = name->rdns;
Packit 40b132
	
Packit 40b132
	/* find last one */
Packit 40b132
	lastRdn = name->rdns;
Packit 40b132
	while (*lastRdn) lastRdn++;
Packit 40b132
	lastRdn--;
Packit 40b132
	
Packit 40b132
	/* reverse list */
Packit 40b132
	for ( ; firstRdn < lastRdn; firstRdn++, lastRdn--) {
Packit 40b132
	    tmp = *firstRdn;
Packit 40b132
	    *firstRdn = *lastRdn;
Packit 40b132
	    *lastRdn = tmp;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    /* return result */
Packit 40b132
    return name;
Packit 40b132
    
Packit 40b132
  loser:
Packit 40b132
    CERT_DestroyName(name);
Packit 40b132
    return NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
CERTName *
Packit 40b132
CERT_AsciiToName(const char *string)
Packit 40b132
{
Packit 40b132
    CERTName *name;
Packit 40b132
    name = ParseRFC1485Name(string, PORT_Strlen(string));
Packit 40b132
    return name;
Packit 40b132
}
Packit 40b132
Packit 40b132
/************************************************************************/
Packit 40b132
Packit 40b132
typedef struct stringBufStr {
Packit 40b132
    char *buffer;
Packit 40b132
    unsigned offset;
Packit 40b132
    unsigned size;
Packit 40b132
} stringBuf;
Packit 40b132
Packit 40b132
#define DEFAULT_BUFFER_SIZE 200
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
AppendStr(stringBuf *bufp, char *str)
Packit 40b132
{
Packit 40b132
    char *buf;
Packit 40b132
    unsigned bufLen, bufSize, len;
Packit 40b132
    int size = 0;
Packit 40b132
Packit 40b132
    /* Figure out how much to grow buf by (add in the '\0') */
Packit 40b132
    buf = bufp->buffer;
Packit 40b132
    bufLen = bufp->offset;
Packit 40b132
    len = PORT_Strlen(str);
Packit 40b132
    bufSize = bufLen + len;
Packit 40b132
    if (!buf) {
Packit 40b132
	bufSize++;
Packit 40b132
	size = PR_MAX(DEFAULT_BUFFER_SIZE,bufSize*2);
Packit 40b132
	buf = (char *) PORT_Alloc(size);
Packit 40b132
	bufp->size = size;
Packit 40b132
    } else if (bufp->size < bufSize) {
Packit 40b132
	size = bufSize*2;
Packit 40b132
	buf =(char *) PORT_Realloc(buf,size);
Packit 40b132
	bufp->size = size;
Packit 40b132
    }
Packit 40b132
    if (!buf) {
Packit 40b132
	PORT_SetError(SEC_ERROR_NO_MEMORY);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    bufp->buffer = buf;
Packit 40b132
    bufp->offset = bufSize;
Packit 40b132
Packit 40b132
    /* Concatenate str onto buf */
Packit 40b132
    buf = buf + bufLen;
Packit 40b132
    if (bufLen) buf--;			/* stomp on old '\0' */
Packit 40b132
    PORT_Memcpy(buf, str, len+1);		/* put in new null */
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
typedef enum {
Packit 40b132
    minimalEscape = 0,		/* only hex escapes, and " and \ */
Packit 40b132
    minimalEscapeAndQuote,	/* as above, plus quoting        */
Packit 40b132
    fullEscape                  /* no quoting, full escaping     */
Packit 40b132
} EQMode;
Packit 40b132
Packit 40b132
/* Some characters must be escaped as a hex string, e.g. c -> \nn .
Packit 40b132
 * Others must be escaped by preceding with a '\', e.g. c -> \c , but
Packit 40b132
 * there are certain "special characters" that may be handled by either
Packit 40b132
 * escaping them, or by enclosing the entire attribute value in quotes.
Packit 40b132
 * A NULL value for pEQMode implies selecting minimalEscape mode.
Packit 40b132
 * Some callers will do quoting when needed, others will not.
Packit 40b132
 * If a caller selects minimalEscapeAndQuote, and the string does not
Packit 40b132
 * need quoting, then this function changes it to minimalEscape.
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
cert_RFC1485_GetRequiredLen(const char *src, int srclen, EQMode *pEQMode)
Packit 40b132
{
Packit 40b132
    int i, reqLen=0;
Packit 40b132
    EQMode mode = pEQMode ? *pEQMode : minimalEscape;
Packit 40b132
    PRBool needsQuoting = PR_FALSE;
Packit 40b132
    char lastC = 0;
Packit 40b132
Packit 40b132
    /* need to make an initial pass to determine if quoting is needed */
Packit 40b132
    for (i = 0; i < srclen; i++) {
Packit 40b132
	char c = src[i];
Packit 40b132
	reqLen++;
Packit 40b132
	if (NEEDS_HEX_ESCAPE(c)) {      /* c -> \xx  */
Packit 40b132
	    reqLen += 2;
Packit 40b132
	} else if (NEEDS_ESCAPE(c)) {   /* c -> \c   */
Packit 40b132
	    reqLen++;
Packit 40b132
	} else if (SPECIAL_CHAR(c)) {
Packit 40b132
	    if (mode == minimalEscapeAndQuote) /* quoting is allowed */
Packit 40b132
		needsQuoting = PR_TRUE; /* entirety will need quoting */
Packit 40b132
	    else if (mode == fullEscape)
Packit 40b132
	    	reqLen++;               /* MAY escape this character */
Packit 40b132
	} else if (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)) {
Packit 40b132
	    if (mode == minimalEscapeAndQuote) /* quoting is allowed */
Packit 40b132
		needsQuoting = PR_TRUE; /* entirety will need quoting */
Packit 40b132
	}
Packit 40b132
	lastC = c;
Packit 40b132
    }
Packit 40b132
    /* if it begins or ends in optional space it needs quoting */
Packit 40b132
    if (!needsQuoting && srclen > 0 && mode == minimalEscapeAndQuote && 
Packit 40b132
	(OPTIONAL_SPACE(src[srclen-1]) || OPTIONAL_SPACE(src[0]))) {
Packit 40b132
	needsQuoting = PR_TRUE;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (needsQuoting) 
Packit 40b132
    	reqLen += 2;
Packit 40b132
    if (pEQMode && mode == minimalEscapeAndQuote && !needsQuoting)
Packit 40b132
    	*pEQMode = minimalEscape;
Packit 40b132
    return reqLen;
Packit 40b132
}
Packit 40b132
Packit 40b132
static const char hexChars[16] = { "0123456789abcdef" };
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
escapeAndQuote(char *dst, int dstlen, char *src, int srclen, EQMode *pEQMode)
Packit 40b132
{
Packit 40b132
    int i, reqLen=0;
Packit 40b132
    EQMode mode = pEQMode ? *pEQMode : minimalEscape;
Packit 40b132
Packit 40b132
    /* space for terminal null */
Packit 40b132
    reqLen = cert_RFC1485_GetRequiredLen(src, srclen, &mode) + 1;
Packit 40b132
    if (reqLen > dstlen) {
Packit 40b132
	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (mode == minimalEscapeAndQuote)
Packit 40b132
        *dst++ = C_DOUBLE_QUOTE;
Packit 40b132
    for (i = 0; i < srclen; i++) {
Packit 40b132
	char c = src[i];
Packit 40b132
	if (NEEDS_HEX_ESCAPE(c)) {
Packit 40b132
	    *dst++ = C_BACKSLASH;
Packit 40b132
	    *dst++ = hexChars[ (c >> 4) & 0x0f ];
Packit 40b132
	    *dst++ = hexChars[  c       & 0x0f ];
Packit 40b132
	} else {
Packit 40b132
	    if (NEEDS_ESCAPE(c) || (SPECIAL_CHAR(c) && mode == fullEscape)) {
Packit 40b132
		*dst++ = C_BACKSLASH;
Packit 40b132
	    }
Packit 40b132
	    *dst++ = c;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    if (mode == minimalEscapeAndQuote)
Packit 40b132
    	*dst++ = C_DOUBLE_QUOTE;
Packit 40b132
    *dst++ = 0;
Packit 40b132
    if (pEQMode)
Packit 40b132
    	*pEQMode = mode;
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
SECStatus
Packit 40b132
CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src, int srclen)
Packit 40b132
{
Packit 40b132
    EQMode mode = minimalEscapeAndQuote;
Packit 40b132
    return escapeAndQuote(dst, dstlen, src, srclen, &mode);
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/* convert an OID to dotted-decimal representation */
Packit 40b132
/* Returns a string that must be freed with PR_smprintf_free(), */
Packit 40b132
char *
Packit 40b132
CERT_GetOidString(const SECItem *oid)
Packit 40b132
{
Packit 40b132
    PRUint8 *stop;   /* points to first byte after OID string */
Packit 40b132
    PRUint8 *first;  /* byte of an OID component integer      */
Packit 40b132
    PRUint8 *last;   /* byte of an OID component integer      */
Packit 40b132
    char *rvString   = NULL;
Packit 40b132
    char *prefix     = NULL;
Packit 40b132
Packit 40b132
#define MAX_OID_LEN 1024 /* bytes */
Packit 40b132
Packit 40b132
    if (oid->len > MAX_OID_LEN) {
Packit 40b132
    	PORT_SetError(SEC_ERROR_INPUT_LEN);
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* first will point to the next sequence of bytes to decode */
Packit 40b132
    first = (PRUint8 *)oid->data;
Packit 40b132
    /* stop points to one past the legitimate data */
Packit 40b132
    stop = &first[ oid->len ];
Packit 40b132
Packit 40b132
    /*
Packit 40b132
     * Check for our pseudo-encoded single-digit OIDs
Packit 40b132
     */
Packit 40b132
    if ((*first == 0x80) && (2 == oid->len)) {
Packit 40b132
	/* Funky encoding.  The second byte is the number */
Packit 40b132
	rvString = PR_smprintf("%lu", (PRUint32)first[1]);
Packit 40b132
	if (!rvString) {
Packit 40b132
	    PORT_SetError(SEC_ERROR_NO_MEMORY);
Packit 40b132
	}
Packit 40b132
	return rvString;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    for (; first < stop; first = last + 1) {
Packit 40b132
    	unsigned int bytesBeforeLast;
Packit 40b132
    
Packit 40b132
	for (last = first; last < stop; last++) {
Packit 40b132
	    if (0 == (*last & 0x80)) {
Packit 40b132
		break;
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
	bytesBeforeLast = (unsigned int)(last - first);
Packit 40b132
	if (bytesBeforeLast <= 3U) {        /* 0-28 bit number */
Packit 40b132
	    PRUint32 n = 0;
Packit 40b132
	    PRUint32 c;
Packit 40b132
Packit 40b132
#define CGET(i, m) \
Packit 40b132
		c  = last[-i] & m; \
Packit 40b132
		n |= c << (7 * i)
Packit 40b132
Packit 40b132
#define CASE(i, m) \
Packit 40b132
	    case i:                      \
Packit 40b132
		CGET(i, m);              \
Packit 40b132
		if (!n) goto unsupported \
Packit 40b132
		/* fall-through */
Packit 40b132
Packit 40b132
	    switch (bytesBeforeLast) {
Packit 40b132
	    CASE(3, 0x7f);
Packit 40b132
	    CASE(2, 0x7f);
Packit 40b132
	    CASE(1, 0x7f);
Packit 40b132
	    case 0: n |= last[0] & 0x7f;
Packit 40b132
		break;
Packit 40b132
	    }
Packit 40b132
	    if (last[0] & 0x80)
Packit 40b132
	    	goto unsupported;
Packit 40b132
      
Packit 40b132
	    if (!rvString) {
Packit 40b132
		/* This is the first number.. decompose it */
Packit 40b132
		PRUint32 one = PR_MIN(n/40, 2); /* never > 2 */
Packit 40b132
		PRUint32 two = n - (one * 40);
Packit 40b132
        
Packit 40b132
		rvString = PR_smprintf("OID.%lu.%lu", one, two);
Packit 40b132
	    } else {
Packit 40b132
		prefix = rvString;
Packit 40b132
		rvString = PR_smprintf("%s.%lu", prefix, n);
Packit 40b132
	    }
Packit 40b132
	} else if (bytesBeforeLast <= 9U) { /* 29-64 bit number */
Packit 40b132
	    PRUint64 n = 0;
Packit 40b132
	    PRUint64 c;
Packit 40b132
Packit 40b132
	    switch (bytesBeforeLast) {
Packit 40b132
	    CASE(9, 0x01);
Packit 40b132
	    CASE(8, 0x7f);
Packit 40b132
	    CASE(7, 0x7f);
Packit 40b132
	    CASE(6, 0x7f);
Packit 40b132
	    CASE(5, 0x7f);
Packit 40b132
	    CASE(4, 0x7f);
Packit 40b132
	    CGET(3, 0x7f);
Packit 40b132
	    CGET(2, 0x7f);
Packit 40b132
	    CGET(1, 0x7f);
Packit 40b132
	    CGET(0, 0x7f);
Packit 40b132
		break;
Packit 40b132
	    }
Packit 40b132
	    if (last[0] & 0x80)
Packit 40b132
	    	goto unsupported;
Packit 40b132
      
Packit 40b132
	    if (!rvString) {
Packit 40b132
		/* This is the first number.. decompose it */
Packit 40b132
		PRUint64 one = PR_MIN(n/40, 2); /* never > 2 */
Packit 40b132
		PRUint64 two = n - (one * 40);
Packit 40b132
        
Packit 40b132
		rvString = PR_smprintf("OID.%llu.%llu", one, two);
Packit 40b132
	    } else {
Packit 40b132
		prefix = rvString;
Packit 40b132
		rvString = PR_smprintf("%s.%llu", prefix, n);
Packit 40b132
	    }
Packit 40b132
	} else {
Packit 40b132
	    /* More than a 64-bit number, or not minimal encoding. */
Packit 40b132
unsupported:
Packit 40b132
	    if (!rvString)
Packit 40b132
		rvString = PR_smprintf("OID.UNSUPPORTED");
Packit 40b132
	    else {
Packit 40b132
		prefix = rvString;
Packit 40b132
		rvString = PR_smprintf("%s.UNSUPPORTED", prefix);
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
Packit 40b132
	if (prefix) {
Packit 40b132
	    PR_smprintf_free(prefix);
Packit 40b132
	    prefix = NULL;
Packit 40b132
	}
Packit 40b132
	if (!rvString) {
Packit 40b132
	    PORT_SetError(SEC_ERROR_NO_MEMORY);
Packit 40b132
	    break;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return rvString;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* convert DER-encoded hex to a string */
Packit 40b132
static SECItem *
Packit 40b132
get_hex_string(SECItem *data)
Packit 40b132
{
Packit 40b132
    SECItem *rv;
Packit 40b132
    unsigned int i, j;
Packit 40b132
    static const char hex[] = { "0123456789ABCDEF" };
Packit 40b132
Packit 40b132
    /* '#' + 2 chars per octet + terminator */
Packit 40b132
    rv = SECITEM_AllocItem(NULL, NULL, data->len*2 + 2);
Packit 40b132
    if (!rv) {
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
    rv->data[0] = '#';
Packit 40b132
    rv->len = 1 + 2 * data->len;
Packit 40b132
    for (i=0; i<data->len; i++) {
Packit 40b132
	j = data->data[i];
Packit 40b132
	rv->data[2*i+1] = hex[j >> 4];
Packit 40b132
	rv->data[2*i+2] = hex[j & 15];
Packit 40b132
    }
Packit 40b132
    rv->data[rv->len] = 0;
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* For compliance with RFC 2253, RFC 3280 and RFC 4630, we choose to 
Packit 40b132
 * use the NAME=STRING form, rather than the OID.N.N=#hexXXXX form, 
Packit 40b132
 * when both of these conditions are met:
Packit 40b132
 *  1) The attribute name OID (kind) has a known name string that is 
Packit 40b132
 *     defined in one of those RFCs, or in RFCs that they cite, AND
Packit 40b132
 *  2) The attribute's value encoding is RFC compliant for the kind
Packit 40b132
 *     (e.g., the value's encoding tag is correct for the kind, and
Packit 40b132
 *     the value's length is in the range allowed for the kind, and
Packit 40b132
 *     the value's contents are appropriate for the encoding tag).
Packit 40b132
 *  Otherwise, we use the OID.N.N=#hexXXXX form.
Packit 40b132
 *
Packit 40b132
 *  If the caller prefers maximum human readability to RFC compliance,
Packit 40b132
 *  then 
Packit 40b132
 *  - We print the kind in NAME= string form if we know the name
Packit 40b132
 *    string for the attribute type OID, regardless of whether the 
Packit 40b132
 *    value is correctly encoded or not. else we use the OID.N.N= form.
Packit 40b132
 *  - We use the non-hex STRING form for the attribute value if the
Packit 40b132
 *    value can be represented in such a form.  Otherwise, we use 
Packit 40b132
 *    the hex string form.
Packit 40b132
 *  This implies that, for maximum human readability, in addition to 
Packit 40b132
 *  the two forms allowed by the RFC, we allow two other forms of output:
Packit 40b132
 *  - the OID.N.N=STRING form, and 
Packit 40b132
 *  - the NAME=#hexXXXX form
Packit 40b132
 *  When the caller prefers maximum human readability, we do not allow
Packit 40b132
 *  the value of any attribute to exceed the length allowed by the RFC.
Packit 40b132
 *  If the attribute value exceeds the allowed length, we truncate it to 
Packit 40b132
 *  the allowed length and append "...".
Packit 40b132
 *  Also in this case, we arbitrarily impose a limit on the length of the 
Packit 40b132
 *  entire AVA encoding, regardless of the form, of 384 bytes per AVA.
Packit 40b132
 *  This limit includes the trailing NULL character.  If the encoded 
Packit 40b132
 *  AVA length exceeds that limit, this function reports failure to encode
Packit 40b132
 *  the AVA.
Packit 40b132
 *
Packit 40b132
 *  An ASCII representation of an AVA is said to be "invertible" if 
Packit 40b132
 *  conversion back to DER reproduces the original DER encoding exactly.
Packit 40b132
 *  The RFC 2253 rules do not ensure that all ASCII AVAs derived according
Packit 40b132
 *  to its rules are invertible. That is because the RFCs allow some 
Packit 40b132
 *  attribute values to be encoded in any of a number of encodings,
Packit 40b132
 *  and the encoding type information is lost in the non-hex STRING form.
Packit 40b132
 *  This is particularly true of attributes of type DirectoryString.
Packit 40b132
 *  The encoding type information is always preserved in the hex string 
Packit 40b132
 *  form, because the hex includes the entire DER encoding of the value.
Packit 40b132
 *
Packit 40b132
 *  So, when the caller perfers maximum invertibility, we apply the 
Packit 40b132
 *  RFC compliance rules stated above, and add a third required 
Packit 40b132
 *  condition on the use of the NAME=STRING form.  
Packit 40b132
 *   3) The attribute's kind is not is allowed to be encoded in any of 
Packit 40b132
 *      several different encodings, such as DirectoryStrings.
Packit 40b132
 *
Packit 40b132
 * The chief difference between CERT_N2A_STRICT and CERT_N2A_INVERTIBLE
Packit 40b132
 * is that the latter forces DirectoryStrings to be hex encoded.
Packit 40b132
 *
Packit 40b132
 * As a simplification, we assume the value is correctly encoded for 
Packit 40b132
 * its encoding type.  That is, we do not test that all the characters
Packit 40b132
 * in a string encoded type are allowed by that type.  We assume it.
Packit 40b132
 */
Packit 40b132
static SECStatus
Packit 40b132
AppendAVA(stringBuf *bufp, CERTAVA *ava, CertStrictnessLevel strict)
Packit 40b132
{
Packit 40b132
#define TMPBUF_LEN 2048
Packit 40b132
    const NameToKind *pn2k   = name2kinds;
Packit 40b132
    SECItem     *avaValue    = NULL;
Packit 40b132
    char        *unknownTag  = NULL;
Packit 40b132
    char        *encodedAVA  = NULL;
Packit 40b132
    PRBool       useHex      = PR_FALSE;  /* use =#hexXXXX form */
Packit 40b132
    PRBool       truncateName  = PR_FALSE;
Packit 40b132
    PRBool       truncateValue = PR_FALSE;
Packit 40b132
    SECOidTag    endKind;
Packit 40b132
    SECStatus    rv;
Packit 40b132
    unsigned int len;
Packit 40b132
    unsigned int nameLen, valueLen;
Packit 40b132
    unsigned int maxName, maxValue;
Packit 40b132
    EQMode       mode        = minimalEscapeAndQuote;
Packit 40b132
    NameToKind   n2k         = { NULL, 32767, SEC_OID_UNKNOWN, SEC_ASN1_DS };
Packit 40b132
    char         tmpBuf[TMPBUF_LEN];
Packit 40b132
Packit 40b132
#define tagName  n2k.name    /* non-NULL means use NAME= form */
Packit 40b132
#define maxBytes n2k.maxLen
Packit 40b132
#define tag      n2k.kind
Packit 40b132
#define vt       n2k.valueType
Packit 40b132
Packit 40b132
    /* READABLE mode recognizes more names from the name2kinds table
Packit 40b132
     * than do STRICT or INVERTIBLE modes.  This assignment chooses the
Packit 40b132
     * point in the table where the attribute type name scanning stops.
Packit 40b132
     */
Packit 40b132
    endKind = (strict == CERT_N2A_READABLE) ? SEC_OID_UNKNOWN
Packit 40b132
                                            : SEC_OID_AVA_POSTAL_ADDRESS;
Packit 40b132
    tag = CERT_GetAVATag(ava);
Packit 40b132
    while (pn2k->kind != tag && pn2k->kind != endKind) {
Packit 40b132
        ++pn2k;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (pn2k->kind != endKind ) {
Packit 40b132
        n2k = *pn2k;
Packit 40b132
    } else if (strict != CERT_N2A_READABLE) {
Packit 40b132
        useHex = PR_TRUE;
Packit 40b132
    }
Packit 40b132
    /* For invertable form, force Directory Strings to use hex form. */
Packit 40b132
    if (strict == CERT_N2A_INVERTIBLE && vt == SEC_ASN1_DS) {
Packit 40b132
	tagName = NULL;      /* must use OID.N form */
Packit 40b132
	useHex = PR_TRUE;    /* must use hex string */
Packit 40b132
    }
Packit 40b132
    if (!useHex) {
Packit 40b132
	avaValue = CERT_DecodeAVAValue(&ava->value);
Packit 40b132
	if (!avaValue) {
Packit 40b132
	    useHex = PR_TRUE;
Packit 40b132
	    if (strict != CERT_N2A_READABLE) {
Packit 40b132
		tagName = NULL;  /* must use OID.N form */
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    if (!tagName) {
Packit 40b132
	/* handle unknown attribute types per RFC 2253 */
Packit 40b132
	tagName = unknownTag = CERT_GetOidString(&ava->type);
Packit 40b132
	if (!tagName) {
Packit 40b132
	    if (avaValue)
Packit 40b132
		SECITEM_FreeItem(avaValue, PR_TRUE);
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    if (useHex) {
Packit 40b132
	avaValue = get_hex_string(&ava->value);
Packit 40b132
	if (!avaValue) {
Packit 40b132
	    if (unknownTag) 
Packit 40b132
	    	PR_smprintf_free(unknownTag);
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    nameLen  = strlen(tagName);
Packit 40b132
    valueLen = (useHex ? avaValue->len : 
Packit 40b132
		cert_RFC1485_GetRequiredLen((char *)avaValue->data, avaValue->len, 
Packit 40b132
					    &mode);;
Packit 40b132
    len = nameLen + valueLen + 2; /* Add 2 for '=' and trailing NUL */
Packit 40b132
Packit 40b132
    maxName  = nameLen;
Packit 40b132
    maxValue = valueLen;
Packit 40b132
    if (len <= sizeof(tmpBuf)) {
Packit 40b132
    	encodedAVA = tmpBuf;
Packit 40b132
    } else if (strict != CERT_N2A_READABLE) {
Packit 40b132
	encodedAVA = PORT_Alloc(len);
Packit 40b132
	if (!encodedAVA) {
Packit 40b132
	    SECITEM_FreeItem(avaValue, PR_TRUE);
Packit 40b132
	    if (unknownTag) 
Packit 40b132
		PR_smprintf_free(unknownTag);
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
    } else {
Packit 40b132
	/* Must make output fit in tmpbuf */
Packit 40b132
	unsigned int fair = (sizeof tmpBuf)/2 - 1; /* for = and \0 */
Packit 40b132
Packit 40b132
	if (nameLen < fair) {
Packit 40b132
	    /* just truncate the value */
Packit 40b132
	    maxValue = (sizeof tmpBuf) - (nameLen + 6); /* for "=...\0",
Packit 40b132
                                                           and possibly '"' */
Packit 40b132
	} else if (valueLen < fair) {
Packit 40b132
	    /* just truncate the name */
Packit 40b132
	    maxName  = (sizeof tmpBuf) - (valueLen + 5); /* for "=...\0" */
Packit 40b132
	} else {
Packit 40b132
	    /* truncate both */
Packit 40b132
	    maxName = maxValue = fair - 3;  /* for "..." */
Packit 40b132
	}
Packit 40b132
	if (nameLen > maxName) {
Packit 40b132
	    PORT_Assert(unknownTag && unknownTag == tagName);
Packit 40b132
	    truncateName = PR_TRUE;
Packit 40b132
	    nameLen = maxName;
Packit 40b132
	}
Packit 40b132
    	encodedAVA = tmpBuf;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    memcpy(encodedAVA, tagName, nameLen);
Packit 40b132
    if (truncateName) {
Packit 40b132
	/* If tag name is too long, we know it is an OID form that was 
Packit 40b132
	 * allocated from the heap, so we can modify it in place 
Packit 40b132
	 */
Packit 40b132
	encodedAVA[nameLen-1] = '.';
Packit 40b132
	encodedAVA[nameLen-2] = '.';
Packit 40b132
	encodedAVA[nameLen-3] = '.';
Packit 40b132
    }
Packit 40b132
    encodedAVA[nameLen++] = '=';
Packit 40b132
    if (unknownTag) 
Packit 40b132
    	PR_smprintf_free(unknownTag);
Packit 40b132
Packit 40b132
    if (strict == CERT_N2A_READABLE && maxValue > maxBytes)
Packit 40b132
	maxValue = maxBytes;
Packit 40b132
    if (valueLen > maxValue) {
Packit 40b132
    	valueLen = maxValue;
Packit 40b132
	truncateValue = PR_TRUE;
Packit 40b132
    }
Packit 40b132
    /* escape and quote as necessary - don't quote hex strings */
Packit 40b132
    if (useHex) {
Packit 40b132
	char * end = encodedAVA + nameLen + valueLen;
Packit 40b132
	memcpy(encodedAVA + nameLen, (char *)avaValue->data, valueLen);
Packit 40b132
	end[0] = '\0';
Packit 40b132
	if (truncateValue) {
Packit 40b132
	    end[-1] = '.';
Packit 40b132
	    end[-2] = '.';
Packit 40b132
	    end[-3] = '.';
Packit 40b132
	}
Packit 40b132
	rv = SECSuccess;
Packit 40b132
    } else if (!truncateValue) {
Packit 40b132
	rv = escapeAndQuote(encodedAVA + nameLen, len - nameLen, 
Packit 40b132
			    (char *)avaValue->data, avaValue->len, &mode);
Packit 40b132
    } else {
Packit 40b132
	/* must truncate the escaped and quoted value */
Packit 40b132
	char bigTmpBuf[TMPBUF_LEN * 3 + 3];
Packit 40b132
	PORT_Assert(valueLen < sizeof tmpBuf);
Packit 40b132
	rv = escapeAndQuote(bigTmpBuf, sizeof bigTmpBuf,
Packit 40b132
			    (char *)avaValue->data,
Packit 40b132
			    PR_MIN(avaValue->len, valueLen), &mode);
Packit 40b132
Packit 40b132
	bigTmpBuf[valueLen--] = '\0'; /* hard stop here */
Packit 40b132
	/* See if we're in the middle of a multi-byte UTF8 character */
Packit 40b132
	while (((bigTmpBuf[valueLen] & 0xc0) == 0x80) && valueLen > 0) {
Packit 40b132
	    bigTmpBuf[valueLen--] = '\0';
Packit 40b132
	}
Packit 40b132
	/* add ellipsis to signify truncation. */
Packit 40b132
	bigTmpBuf[++valueLen] = '.';
Packit 40b132
	bigTmpBuf[++valueLen] = '.';
Packit 40b132
	bigTmpBuf[++valueLen] = '.';
Packit 40b132
	if (bigTmpBuf[0] == '"')
Packit 40b132
	    bigTmpBuf[++valueLen] = '"';
Packit 40b132
	bigTmpBuf[++valueLen] = '\0';
Packit 40b132
	PORT_Assert(nameLen + valueLen <= (sizeof tmpBuf) - 1);
Packit 40b132
	memcpy(encodedAVA + nameLen, bigTmpBuf, valueLen+1);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    SECITEM_FreeItem(avaValue, PR_TRUE);
Packit 40b132
    if (rv == SECSuccess)
Packit 40b132
	rv = AppendStr(bufp, encodedAVA);
Packit 40b132
    if (encodedAVA != tmpBuf)
Packit 40b132
    	PORT_Free(encodedAVA);
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
#undef tagName
Packit 40b132
#undef maxBytes
Packit 40b132
#undef tag
Packit 40b132
#undef vt
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_NameToAsciiInvertible(CERTName *name, CertStrictnessLevel strict)
Packit 40b132
{
Packit 40b132
    CERTRDN** rdns;
Packit 40b132
    CERTRDN** lastRdn;
Packit 40b132
    CERTRDN** rdn;
Packit 40b132
    PRBool first = PR_TRUE;
Packit 40b132
    stringBuf strBuf = { NULL, 0, 0 };
Packit 40b132
    
Packit 40b132
    rdns = name->rdns;
Packit 40b132
    if (rdns == NULL) {
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    /* find last RDN */
Packit 40b132
    lastRdn = rdns;
Packit 40b132
    while (*lastRdn) lastRdn++;
Packit 40b132
    lastRdn--;
Packit 40b132
    
Packit 40b132
    /*
Packit 40b132
     * Loop over name contents in _reverse_ RDN order appending to string
Packit 40b132
     */
Packit 40b132
    for (rdn = lastRdn; rdn >= rdns; rdn--) {
Packit 40b132
	CERTAVA** avas = (*rdn)->avas;
Packit 40b132
	CERTAVA* ava;
Packit 40b132
	PRBool newRDN = PR_TRUE;
Packit 40b132
Packit 40b132
	/* 
Packit 40b132
	 * XXX Do we need to traverse the AVAs in reverse order, too?
Packit 40b132
	 */
Packit 40b132
	while (avas && (ava = *avas++) != NULL) {
Packit 40b132
	    SECStatus rv;
Packit 40b132
	    /* Put in comma or plus separator */
Packit 40b132
	    if (!first) {
Packit 40b132
		/* Use of spaces is deprecated in RFC 2253. */
Packit 40b132
		rv = AppendStr(&strBuf, newRDN ? "," : "+");
Packit 40b132
		if (rv) goto loser;
Packit 40b132
	    } else {
Packit 40b132
		first = PR_FALSE;
Packit 40b132
	    }
Packit 40b132
	    
Packit 40b132
	    /* Add in tag type plus value into strBuf */
Packit 40b132
	    rv = AppendAVA(&strBuf, ava, strict);
Packit 40b132
	    if (rv) goto loser;
Packit 40b132
	    newRDN = PR_FALSE;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return strBuf.buffer;
Packit 40b132
loser:
Packit 40b132
    if (strBuf.buffer) {
Packit 40b132
	PORT_Free(strBuf.buffer);
Packit 40b132
    }
Packit 40b132
    return NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_NameToAscii(CERTName *name)
Packit 40b132
{
Packit 40b132
    return CERT_NameToAsciiInvertible(name, CERT_N2A_READABLE);
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * Return the string representation of a DER encoded distinguished name
Packit 40b132
 * "dername" - The DER encoded name to convert
Packit 40b132
 */
Packit 40b132
char *
Packit 40b132
CERT_DerNameToAscii(SECItem *dername)
Packit 40b132
{
Packit 40b132
    int rv;
Packit 40b132
    PLArenaPool *arena = NULL;
Packit 40b132
    CERTName name;
Packit 40b132
    char *retstr = NULL;
Packit 40b132
    
Packit 40b132
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
Packit 40b132
    
Packit 40b132
    if ( arena == NULL) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername);
Packit 40b132
    
Packit 40b132
    if ( rv != SECSuccess ) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    retstr = CERT_NameToAscii(&name);
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if ( arena != NULL ) {
Packit 40b132
	PORT_FreeArena(arena, PR_FALSE);
Packit 40b132
    }
Packit 40b132
    
Packit 40b132
    return(retstr);
Packit 40b132
}
Packit 40b132
Packit 40b132
static char *
Packit 40b132
avaToString(PLArenaPool *arena, CERTAVA *ava)
Packit 40b132
{
Packit 40b132
    char *    buf       = NULL;
Packit 40b132
    SECItem*  avaValue;
Packit 40b132
    int       valueLen;
Packit 40b132
Packit 40b132
    avaValue = CERT_DecodeAVAValue(&ava->value);
Packit 40b132
    if(!avaValue) {
Packit 40b132
	return buf;
Packit 40b132
    }
Packit 40b132
    valueLen = cert_RFC1485_GetRequiredLen((char *)avaValue->data,
Packit 40b132
                                           avaValue->len, NULL) + 1;
Packit 40b132
    if (arena) {
Packit 40b132
	buf = (char *)PORT_ArenaZAlloc(arena, valueLen);
Packit 40b132
    } else {
Packit 40b132
	buf = (char *)PORT_ZAlloc(valueLen);
Packit 40b132
    }
Packit 40b132
    if (buf) {
Packit 40b132
	SECStatus rv = escapeAndQuote(buf, valueLen, (char *)avaValue->data, 
Packit 40b132
	                              avaValue->len, NULL);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    if (!arena)
Packit 40b132
		PORT_Free(buf);
Packit 40b132
	    buf = NULL;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    SECITEM_FreeItem(avaValue, PR_TRUE);
Packit 40b132
    return buf;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* RDNs are sorted from most general to most specific.
Packit 40b132
 * This code returns the FIRST one found, the most general one found.
Packit 40b132
 */
Packit 40b132
static char *
Packit 40b132
CERT_GetNameElement(PLArenaPool *arena, const CERTName *name, int wantedTag)
Packit 40b132
{
Packit 40b132
    CERTRDN** rdns = name->rdns;
Packit 40b132
    CERTRDN*  rdn;
Packit 40b132
    CERTAVA*  ava  = NULL;
Packit 40b132
Packit 40b132
    while (rdns && (rdn = *rdns++) != 0) {
Packit 40b132
	CERTAVA** avas = rdn->avas;
Packit 40b132
	while (avas && (ava = *avas++) != 0) {
Packit 40b132
	    int tag = CERT_GetAVATag(ava);
Packit 40b132
	    if ( tag == wantedTag ) {
Packit 40b132
		avas = NULL;
Packit 40b132
		rdns = NULL; /* break out of all loops */
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return ava ? avaToString(arena, ava) : NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* RDNs are sorted from most general to most specific.
Packit 40b132
 * This code returns the LAST one found, the most specific one found.
Packit 40b132
 * This is particularly appropriate for Common Name.  See RFC 2818.
Packit 40b132
 */
Packit 40b132
static char *
Packit 40b132
CERT_GetLastNameElement(PLArenaPool *arena, const CERTName *name, int wantedTag)
Packit 40b132
{
Packit 40b132
    CERTRDN** rdns    = name->rdns;
Packit 40b132
    CERTRDN*  rdn;
Packit 40b132
    CERTAVA*  lastAva = NULL;
Packit 40b132
    
Packit 40b132
    while (rdns && (rdn = *rdns++) != 0) {
Packit 40b132
	CERTAVA** avas = rdn->avas;
Packit 40b132
	CERTAVA*  ava;
Packit 40b132
	while (avas && (ava = *avas++) != 0) {
Packit 40b132
	    int tag = CERT_GetAVATag(ava);
Packit 40b132
	    if ( tag == wantedTag ) {
Packit 40b132
		lastAva = ava;
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return lastAva ? avaToString(arena, lastAva) : NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetCertificateEmailAddress(CERTCertificate *cert)
Packit 40b132
{
Packit 40b132
    char *rawEmailAddr = NULL;
Packit 40b132
    SECItem subAltName;
Packit 40b132
    SECStatus rv;
Packit 40b132
    CERTGeneralName *nameList = NULL;
Packit 40b132
    CERTGeneralName *current;
Packit 40b132
    PLArenaPool *arena = NULL;
Packit 40b132
    int i;
Packit 40b132
    
Packit 40b132
    subAltName.data = NULL;
Packit 40b132
Packit 40b132
    rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject),
Packit 40b132
						 SEC_OID_PKCS9_EMAIL_ADDRESS);
Packit 40b132
    if ( rawEmailAddr == NULL ) {
Packit 40b132
	rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), 
Packit 40b132
							SEC_OID_RFC1274_MAIL);
Packit 40b132
    }
Packit 40b132
    if ( rawEmailAddr == NULL) {
Packit 40b132
Packit 40b132
	rv = CERT_FindCertExtension(cert,  SEC_OID_X509_SUBJECT_ALT_NAME, 
Packit 40b132
								&subAltName);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    goto finish;
Packit 40b132
	}
Packit 40b132
	arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
Packit 40b132
	if (!arena) {
Packit 40b132
	    goto finish;
Packit 40b132
	}
Packit 40b132
	nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
Packit 40b132
	if (!nameList ) {
Packit 40b132
	    goto finish;
Packit 40b132
	}
Packit 40b132
	if (nameList != NULL) {
Packit 40b132
	    do {
Packit 40b132
		if (current->type == certDirectoryName) {
Packit 40b132
		    rawEmailAddr = CERT_GetNameElement(cert->arena,
Packit 40b132
			&(current->name.directoryName), 
Packit 40b132
					       SEC_OID_PKCS9_EMAIL_ADDRESS);
Packit 40b132
		    if ( rawEmailAddr == NULL ) {
Packit 40b132
			rawEmailAddr = CERT_GetNameElement(cert->arena,
Packit 40b132
			  &(current->name.directoryName), SEC_OID_RFC1274_MAIL);
Packit 40b132
		    }
Packit 40b132
		} else if (current->type == certRFC822Name) {
Packit 40b132
		    rawEmailAddr = (char*)PORT_ArenaZAlloc(cert->arena,
Packit 40b132
						current->name.other.len + 1);
Packit 40b132
		    if (!rawEmailAddr) {
Packit 40b132
			goto finish;
Packit 40b132
		    }
Packit 40b132
		    PORT_Memcpy(rawEmailAddr, current->name.other.data, 
Packit 40b132
				current->name.other.len);
Packit 40b132
		    rawEmailAddr[current->name.other.len] = '\0';
Packit 40b132
		}
Packit 40b132
		if (rawEmailAddr) {
Packit 40b132
		    break;
Packit 40b132
		}
Packit 40b132
		current = CERT_GetNextGeneralName(current);
Packit 40b132
	    } while (current != nameList);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    if (rawEmailAddr) {
Packit 40b132
	for (i = 0; i <= (int) PORT_Strlen(rawEmailAddr); i++) {
Packit 40b132
	    rawEmailAddr[i] = tolower(rawEmailAddr[i]);
Packit 40b132
	}
Packit 40b132
    } 
Packit 40b132
Packit 40b132
finish:
Packit 40b132
Packit 40b132
    /* Don't free nameList, it's part of the arena. */
Packit 40b132
Packit 40b132
    if (arena) {
Packit 40b132
	PORT_FreeArena(arena, PR_FALSE);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if ( subAltName.data ) {
Packit 40b132
	SECITEM_FreeItem(&subAltName, PR_FALSE);
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return(rawEmailAddr);
Packit 40b132
}
Packit 40b132
Packit 40b132
static char *
Packit 40b132
appendStringToBuf(char *dest, char *src, PRUint32 *pRemaining)
Packit 40b132
{
Packit 40b132
    PRUint32 len;
Packit 40b132
    if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) {
Packit 40b132
	PRUint32 i;
Packit 40b132
	for (i = 0; i < len; ++i)
Packit 40b132
	    dest[i] = tolower(src[i]);
Packit 40b132
	dest[len] = 0;
Packit 40b132
	dest        += len + 1;
Packit 40b132
	*pRemaining -= len + 1;
Packit 40b132
    }
Packit 40b132
    return dest;
Packit 40b132
}
Packit 40b132
Packit 40b132
#undef NEEDS_HEX_ESCAPE
Packit 40b132
#define NEEDS_HEX_ESCAPE(c) (c < 0x20)
Packit 40b132
Packit 40b132
static char *
Packit 40b132
appendItemToBuf(char *dest, SECItem *src, PRUint32 *pRemaining)
Packit 40b132
{
Packit 40b132
    if (dest && src && src->data && src->len && src->data[0]) {
Packit 40b132
	PRUint32 len = src->len;
Packit 40b132
	PRUint32 i;
Packit 40b132
	PRUint32 reqLen = len + 1;
Packit 40b132
	/* are there any embedded control characters ? */
Packit 40b132
	for (i = 0; i < len; i++) {
Packit 40b132
	    if (NEEDS_HEX_ESCAPE(src->data[i]))
Packit 40b132
	    	reqLen += 2;   
Packit 40b132
	}
Packit 40b132
	if (*pRemaining > reqLen) {
Packit 40b132
	    for (i = 0; i < len; ++i) {
Packit 40b132
		PRUint8 c = src->data[i];
Packit 40b132
		if (NEEDS_HEX_ESCAPE(c)) {
Packit 40b132
		    *dest++ = C_BACKSLASH;
Packit 40b132
		    *dest++ = hexChars[ (c >> 4) & 0x0f ];
Packit 40b132
		    *dest++ = hexChars[  c       & 0x0f ];
Packit 40b132
		} else {
Packit 40b132
		    *dest++ = tolower(c);
Packit 40b132
	    	}
Packit 40b132
	    }
Packit 40b132
	    *dest++ = '\0';
Packit 40b132
	    *pRemaining -= reqLen;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return dest;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Returns a pointer to an environment-like string, a series of 
Packit 40b132
** null-terminated strings, terminated by a zero-length string.
Packit 40b132
** This function is intended to be internal to NSS.
Packit 40b132
*/
Packit 40b132
char *
Packit 40b132
cert_GetCertificateEmailAddresses(CERTCertificate *cert)
Packit 40b132
{
Packit 40b132
    char *           rawEmailAddr = NULL;
Packit 40b132
    char *           addrBuf      = NULL;
Packit 40b132
    char *           pBuf         = NULL;
Packit 40b132
    PLArenaPool *    tmpArena     = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
Packit 40b132
    PRUint32         maxLen       = 0;
Packit 40b132
    PRInt32          finalLen     = 0;
Packit 40b132
    SECStatus        rv;
Packit 40b132
    SECItem          subAltName;
Packit 40b132
    
Packit 40b132
    if (!tmpArena) 
Packit 40b132
    	return addrBuf;
Packit 40b132
Packit 40b132
    subAltName.data = NULL;
Packit 40b132
    maxLen = cert->derCert.len;
Packit 40b132
    PORT_Assert(maxLen);
Packit 40b132
    if (!maxLen) 
Packit 40b132
	maxLen = 2000;  /* a guess, should never happen */
Packit 40b132
Packit 40b132
    pBuf = addrBuf = (char *)PORT_ArenaZAlloc(tmpArena, maxLen + 1);
Packit 40b132
    if (!addrBuf) 
Packit 40b132
    	goto loser;
Packit 40b132
Packit 40b132
    rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject,
Packit 40b132
				       SEC_OID_PKCS9_EMAIL_ADDRESS);
Packit 40b132
    pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
Packit 40b132
Packit 40b132
    rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject, 
Packit 40b132
				       SEC_OID_RFC1274_MAIL);
Packit 40b132
    pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
Packit 40b132
Packit 40b132
    rv = CERT_FindCertExtension(cert,  SEC_OID_X509_SUBJECT_ALT_NAME, 
Packit 40b132
				&subAltName);
Packit 40b132
    if (rv == SECSuccess && subAltName.data) {
Packit 40b132
	CERTGeneralName *nameList     = NULL;
Packit 40b132
Packit 40b132
	if (!!(nameList = CERT_DecodeAltNameExtension(tmpArena, &subAltName))) {
Packit 40b132
	    CERTGeneralName *current = nameList;
Packit 40b132
	    do {
Packit 40b132
		if (current->type == certDirectoryName) {
Packit 40b132
		    rawEmailAddr = CERT_GetNameElement(tmpArena,
Packit 40b132
			                       &current->name.directoryName, 
Packit 40b132
					       SEC_OID_PKCS9_EMAIL_ADDRESS);
Packit 40b132
		    pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
Packit 40b132
Packit 40b132
		    rawEmailAddr = CERT_GetNameElement(tmpArena,
Packit 40b132
					      &current->name.directoryName, 
Packit 40b132
					      SEC_OID_RFC1274_MAIL);
Packit 40b132
		    pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
Packit 40b132
		} else if (current->type == certRFC822Name) {
Packit 40b132
		    pBuf = appendItemToBuf(pBuf, &current->name.other, &maxLen);
Packit 40b132
		}
Packit 40b132
		current = CERT_GetNextGeneralName(current);
Packit 40b132
	    } while (current != nameList);
Packit 40b132
	}
Packit 40b132
	SECITEM_FreeItem(&subAltName, PR_FALSE);
Packit 40b132
	/* Don't free nameList, it's part of the tmpArena. */
Packit 40b132
    }
Packit 40b132
    /* now copy superstring to cert's arena */
Packit 40b132
    finalLen = (pBuf - addrBuf) + 1;
Packit 40b132
    pBuf = NULL;
Packit 40b132
    if (finalLen > 1) {
Packit 40b132
	pBuf = PORT_ArenaAlloc(cert->arena, finalLen);
Packit 40b132
	if (pBuf) {
Packit 40b132
	    PORT_Memcpy(pBuf, addrBuf, finalLen);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
loser:
Packit 40b132
    if (tmpArena)
Packit 40b132
	PORT_FreeArena(tmpArena, PR_FALSE);
Packit 40b132
Packit 40b132
    return pBuf;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* returns pointer to storage in cert's arena.  Storage remains valid
Packit 40b132
** as long as cert's reference count doesn't go to zero.
Packit 40b132
** Caller should strdup or otherwise copy.
Packit 40b132
*/
Packit 40b132
const char *	/* const so caller won't muck with it. */
Packit 40b132
CERT_GetFirstEmailAddress(CERTCertificate * cert)
Packit 40b132
{
Packit 40b132
    if (cert && cert->emailAddr && cert->emailAddr[0])
Packit 40b132
    	return (const char *)cert->emailAddr;
Packit 40b132
    return NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* returns pointer to storage in cert's arena.  Storage remains valid
Packit 40b132
** as long as cert's reference count doesn't go to zero.
Packit 40b132
** Caller should strdup or otherwise copy.
Packit 40b132
*/
Packit 40b132
const char *	/* const so caller won't muck with it. */
Packit 40b132
CERT_GetNextEmailAddress(CERTCertificate * cert, const char * prev)
Packit 40b132
{
Packit 40b132
    if (cert && prev && prev[0]) {
Packit 40b132
    	PRUint32 len = PL_strlen(prev);
Packit 40b132
	prev += len + 1;
Packit 40b132
	if (prev && prev[0])
Packit 40b132
	    return prev;
Packit 40b132
    }
Packit 40b132
    return NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* This is seriously bogus, now that certs store their email addresses in
Packit 40b132
** subject Alternative Name extensions. 
Packit 40b132
** Returns a string allocated by PORT_StrDup, which the caller must free.
Packit 40b132
*/
Packit 40b132
char *
Packit 40b132
CERT_GetCertEmailAddress(const CERTName *name)
Packit 40b132
{
Packit 40b132
    char *rawEmailAddr;
Packit 40b132
    char *emailAddr;
Packit 40b132
Packit 40b132
    
Packit 40b132
    rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS);
Packit 40b132
    if ( rawEmailAddr == NULL ) {
Packit 40b132
	rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL);
Packit 40b132
    }
Packit 40b132
    emailAddr = CERT_FixupEmailAddr(rawEmailAddr);
Packit 40b132
    if ( rawEmailAddr ) {
Packit 40b132
	PORT_Free(rawEmailAddr);
Packit 40b132
    }
Packit 40b132
    return(emailAddr);
Packit 40b132
}
Packit 40b132
Packit 40b132
/* The return value must be freed with PORT_Free. */
Packit 40b132
char *
Packit 40b132
CERT_GetCommonName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetCountryName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetLocalityName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetStateName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetOrgName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetDomainComponentName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetOrgUnitName(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetDnQualifier(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER));
Packit 40b132
}
Packit 40b132
Packit 40b132
char *
Packit 40b132
CERT_GetCertUid(const CERTName *name)
Packit 40b132
{
Packit 40b132
    return(CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID));
Packit 40b132
}
Packit 40b132