1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/certdb/alg1485.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1560 @@ 1.4 +/* alg1485.c - implementation of RFCs 1485, 1779 and 2253. 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "prprf.h" 1.11 +#include "cert.h" 1.12 +#include "certi.h" 1.13 +#include "xconst.h" 1.14 +#include "genname.h" 1.15 +#include "secitem.h" 1.16 +#include "secerr.h" 1.17 + 1.18 +typedef struct NameToKindStr { 1.19 + const char * name; 1.20 + unsigned int maxLen; /* max bytes in UTF8 encoded string value */ 1.21 + SECOidTag kind; 1.22 + int valueType; 1.23 +} NameToKind; 1.24 + 1.25 +/* local type for directory string--could be printable_string or utf8 */ 1.26 +#define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER 1.27 + 1.28 +/* Add new entries to this table, and maybe to function ParseRFC1485AVA */ 1.29 +static const NameToKind name2kinds[] = { 1.30 +/* IANA registered type names 1.31 + * (See: http://www.iana.org/assignments/ldap-parameters) 1.32 + */ 1.33 +/* RFC 3280, 4630 MUST SUPPORT */ 1.34 + { "CN", 640, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS}, 1.35 + { "ST", 128, SEC_OID_AVA_STATE_OR_PROVINCE, 1.36 + SEC_ASN1_DS}, 1.37 + { "O", 128, SEC_OID_AVA_ORGANIZATION_NAME, 1.38 + SEC_ASN1_DS}, 1.39 + { "OU", 128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, 1.40 + SEC_ASN1_DS}, 1.41 + { "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING}, 1.42 + { "C", 2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING}, 1.43 + { "serialNumber", 64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING}, 1.44 + 1.45 +/* RFC 3280, 4630 SHOULD SUPPORT */ 1.46 + { "L", 128, SEC_OID_AVA_LOCALITY, SEC_ASN1_DS}, 1.47 + { "title", 64, SEC_OID_AVA_TITLE, SEC_ASN1_DS}, 1.48 + { "SN", 64, SEC_OID_AVA_SURNAME, SEC_ASN1_DS}, 1.49 + { "givenName", 64, SEC_OID_AVA_GIVEN_NAME, SEC_ASN1_DS}, 1.50 + { "initials", 64, SEC_OID_AVA_INITIALS, SEC_ASN1_DS}, 1.51 + { "generationQualifier", 1.52 + 64, SEC_OID_AVA_GENERATION_QUALIFIER, 1.53 + SEC_ASN1_DS}, 1.54 +/* RFC 3280, 4630 MAY SUPPORT */ 1.55 + { "DC", 128, SEC_OID_AVA_DC, SEC_ASN1_IA5_STRING}, 1.56 + { "MAIL", 256, SEC_OID_RFC1274_MAIL, SEC_ASN1_IA5_STRING}, 1.57 + { "UID", 256, SEC_OID_RFC1274_UID, SEC_ASN1_DS}, 1.58 + 1.59 +/* ------------------ "strict" boundary --------------------------------- 1.60 + * In strict mode, cert_NameToAscii does not encode any of the attributes 1.61 + * below this line. The first SECOidTag below this line must be used to 1.62 + * conditionally define the "endKind" in function AppendAVA() below. 1.63 + * Most new attribute names should be added below this line. 1.64 + * Maybe this line should be up higher? Say, after the 3280 MUSTs and 1.65 + * before the 3280 SHOULDs? 1.66 + */ 1.67 + 1.68 +/* values from draft-ietf-ldapbis-user-schema-05 (not in RFC 3280) */ 1.69 + { "postalAddress", 128, SEC_OID_AVA_POSTAL_ADDRESS, SEC_ASN1_DS}, 1.70 + { "postalCode", 40, SEC_OID_AVA_POSTAL_CODE, SEC_ASN1_DS}, 1.71 + { "postOfficeBox", 40, SEC_OID_AVA_POST_OFFICE_BOX,SEC_ASN1_DS}, 1.72 + { "houseIdentifier",64, SEC_OID_AVA_HOUSE_IDENTIFIER,SEC_ASN1_DS}, 1.73 +/* end of IANA registered type names */ 1.74 + 1.75 +/* legacy keywords */ 1.76 + { "E", 128, SEC_OID_PKCS9_EMAIL_ADDRESS,SEC_ASN1_IA5_STRING}, 1.77 + { "STREET", 128, SEC_OID_AVA_STREET_ADDRESS, SEC_ASN1_DS}, 1.78 + { "pseudonym", 64, SEC_OID_AVA_PSEUDONYM, SEC_ASN1_DS}, 1.79 + 1.80 +/* values defined by the CAB Forum for EV */ 1.81 + { "incorporationLocality", 128, SEC_OID_EV_INCORPORATION_LOCALITY, 1.82 + SEC_ASN1_DS}, 1.83 + { "incorporationState", 128, SEC_OID_EV_INCORPORATION_STATE, 1.84 + SEC_ASN1_DS}, 1.85 + { "incorporationCountry", 2, SEC_OID_EV_INCORPORATION_COUNTRY, 1.86 + SEC_ASN1_PRINTABLE_STRING}, 1.87 + { "businessCategory", 64, SEC_OID_BUSINESS_CATEGORY, SEC_ASN1_DS}, 1.88 + 1.89 +/* values defined in X.520 */ 1.90 + { "name", 64, SEC_OID_AVA_NAME, SEC_ASN1_DS}, 1.91 + 1.92 + { 0, 256, SEC_OID_UNKNOWN, 0}, 1.93 +}; 1.94 + 1.95 +/* Table facilitates conversion of ASCII hex to binary. */ 1.96 +static const PRInt16 x2b[256] = { 1.97 +/* #0x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.98 +/* #1x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.99 +/* #2x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.100 +/* #3x */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 1.101 +/* #4x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.102 +/* #5x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.103 +/* #6x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.104 +/* #7x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.105 +/* #8x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.106 +/* #9x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.107 +/* #ax */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.108 +/* #bx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.109 +/* #cx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.110 +/* #dx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.111 +/* #ex */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1.112 +/* #fx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 1.113 +}; 1.114 + 1.115 +#define IS_HEX(c) (x2b[(PRUint8)(c)] >= 0) 1.116 + 1.117 +#define C_DOUBLE_QUOTE '\042' 1.118 + 1.119 +#define C_BACKSLASH '\134' 1.120 + 1.121 +#define C_EQUAL '=' 1.122 + 1.123 +#define OPTIONAL_SPACE(c) \ 1.124 + (((c) == ' ') || ((c) == '\r') || ((c) == '\n')) 1.125 + 1.126 +#define SPECIAL_CHAR(c) \ 1.127 + (((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) || \ 1.128 + ((c) == '\r') || ((c) == '\n') || ((c) == '+') || \ 1.129 + ((c) == '<') || ((c) == '>') || ((c) == '#') || \ 1.130 + ((c) == ';') || ((c) == C_BACKSLASH)) 1.131 + 1.132 + 1.133 +#define IS_PRINTABLE(c) \ 1.134 + ((((c) >= 'a') && ((c) <= 'z')) || \ 1.135 + (((c) >= 'A') && ((c) <= 'Z')) || \ 1.136 + (((c) >= '0') && ((c) <= '9')) || \ 1.137 + ((c) == ' ') || \ 1.138 + ((c) == '\'') || \ 1.139 + ((c) == '\050') || /* ( */ \ 1.140 + ((c) == '\051') || /* ) */ \ 1.141 + (((c) >= '+') && ((c) <= '/')) || /* + , - . / */ \ 1.142 + ((c) == ':') || \ 1.143 + ((c) == '=') || \ 1.144 + ((c) == '?')) 1.145 + 1.146 +/* RFC 2253 says we must escape ",+\"\\<>;=" EXCEPT inside a quoted string. 1.147 + * Inside a quoted string, we only need to escape " and \ 1.148 + * We choose to quote strings containing any of those special characters, 1.149 + * so we only need to escape " and \ 1.150 + */ 1.151 +#define NEEDS_ESCAPE(c) \ 1.152 + (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) 1.153 + 1.154 +#define NEEDS_HEX_ESCAPE(c) \ 1.155 + ((PRUint8)c < 0x20 || c == 0x7f) 1.156 + 1.157 +int 1.158 +cert_AVAOidTagToMaxLen(SECOidTag tag) 1.159 +{ 1.160 + const NameToKind *n2k = name2kinds; 1.161 + 1.162 + while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) { 1.163 + ++n2k; 1.164 + } 1.165 + return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1; 1.166 +} 1.167 + 1.168 +static PRBool 1.169 +IsPrintable(unsigned char *data, unsigned len) 1.170 +{ 1.171 + unsigned char ch, *end; 1.172 + 1.173 + end = data + len; 1.174 + while (data < end) { 1.175 + ch = *data++; 1.176 + if (!IS_PRINTABLE(ch)) { 1.177 + return PR_FALSE; 1.178 + } 1.179 + } 1.180 + return PR_TRUE; 1.181 +} 1.182 + 1.183 +static void 1.184 +skipSpace(const char **pbp, const char *endptr) 1.185 +{ 1.186 + const char *bp = *pbp; 1.187 + while (bp < endptr && OPTIONAL_SPACE(*bp)) { 1.188 + bp++; 1.189 + } 1.190 + *pbp = bp; 1.191 +} 1.192 + 1.193 +static SECStatus 1.194 +scanTag(const char **pbp, const char *endptr, char *tagBuf, int tagBufSize) 1.195 +{ 1.196 + const char *bp; 1.197 + char *tagBufp; 1.198 + int taglen; 1.199 + 1.200 + PORT_Assert(tagBufSize > 0); 1.201 + 1.202 + /* skip optional leading space */ 1.203 + skipSpace(pbp, endptr); 1.204 + if (*pbp == endptr) { 1.205 + /* nothing left */ 1.206 + return SECFailure; 1.207 + } 1.208 + 1.209 + /* fill tagBuf */ 1.210 + taglen = 0; 1.211 + bp = *pbp; 1.212 + tagBufp = tagBuf; 1.213 + while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) { 1.214 + if (++taglen >= tagBufSize) { 1.215 + *pbp = bp; 1.216 + return SECFailure; 1.217 + } 1.218 + *tagBufp++ = *bp++; 1.219 + } 1.220 + /* null-terminate tagBuf -- guaranteed at least one space left */ 1.221 + *tagBufp++ = 0; 1.222 + *pbp = bp; 1.223 + 1.224 + /* skip trailing spaces till we hit something - should be an equal sign */ 1.225 + skipSpace(pbp, endptr); 1.226 + if (*pbp == endptr) { 1.227 + /* nothing left */ 1.228 + return SECFailure; 1.229 + } 1.230 + if (**pbp != C_EQUAL) { 1.231 + /* should be an equal sign */ 1.232 + return SECFailure; 1.233 + } 1.234 + /* skip over the equal sign */ 1.235 + (*pbp)++; 1.236 + 1.237 + return SECSuccess; 1.238 +} 1.239 + 1.240 +/* Returns the number of bytes in the value. 0 means failure. */ 1.241 +static int 1.242 +scanVal(const char **pbp, const char *endptr, char *valBuf, int valBufSize) 1.243 +{ 1.244 + const char *bp; 1.245 + char *valBufp; 1.246 + int vallen = 0; 1.247 + PRBool isQuoted; 1.248 + 1.249 + PORT_Assert(valBufSize > 0); 1.250 + 1.251 + /* skip optional leading space */ 1.252 + skipSpace(pbp, endptr); 1.253 + if(*pbp == endptr) { 1.254 + /* nothing left */ 1.255 + return 0; 1.256 + } 1.257 + 1.258 + bp = *pbp; 1.259 + 1.260 + /* quoted? */ 1.261 + if (*bp == C_DOUBLE_QUOTE) { 1.262 + isQuoted = PR_TRUE; 1.263 + /* skip over it */ 1.264 + bp++; 1.265 + } else { 1.266 + isQuoted = PR_FALSE; 1.267 + } 1.268 + 1.269 + valBufp = valBuf; 1.270 + while (bp < endptr) { 1.271 + char c = *bp; 1.272 + if (c == C_BACKSLASH) { 1.273 + /* escape character */ 1.274 + bp++; 1.275 + if (bp >= endptr) { 1.276 + /* escape charater must appear with paired char */ 1.277 + *pbp = bp; 1.278 + return 0; 1.279 + } 1.280 + c = *bp; 1.281 + if (IS_HEX(c) && (endptr - bp) >= 2 && IS_HEX(bp[1])) { 1.282 + bp++; 1.283 + c = (char)((x2b[(PRUint8)c] << 4) | x2b[(PRUint8)*bp]); 1.284 + } 1.285 + } else if (c == '#' && bp == *pbp) { 1.286 + /* ignore leading #, quotation not required for it. */ 1.287 + } else if (!isQuoted && SPECIAL_CHAR(c)) { 1.288 + /* unescaped special and not within quoted value */ 1.289 + break; 1.290 + } else if (c == C_DOUBLE_QUOTE) { 1.291 + /* reached unescaped double quote */ 1.292 + break; 1.293 + } 1.294 + /* append character */ 1.295 + vallen++; 1.296 + if (vallen >= valBufSize) { 1.297 + *pbp = bp; 1.298 + return 0; 1.299 + } 1.300 + *valBufp++ = c; 1.301 + bp++; 1.302 + } 1.303 + 1.304 + /* strip trailing spaces from unquoted values */ 1.305 + if (!isQuoted) { 1.306 + while (valBufp > valBuf) { 1.307 + char c = valBufp[-1]; 1.308 + if (! OPTIONAL_SPACE(c)) 1.309 + break; 1.310 + --valBufp; 1.311 + } 1.312 + vallen = valBufp - valBuf; 1.313 + } 1.314 + 1.315 + if (isQuoted) { 1.316 + /* insist that we stopped on a double quote */ 1.317 + if (*bp != C_DOUBLE_QUOTE) { 1.318 + *pbp = bp; 1.319 + return 0; 1.320 + } 1.321 + /* skip over the quote and skip optional space */ 1.322 + bp++; 1.323 + skipSpace(&bp, endptr); 1.324 + } 1.325 + 1.326 + *pbp = bp; 1.327 + 1.328 + /* null-terminate valBuf -- guaranteed at least one space left */ 1.329 + *valBufp = 0; 1.330 + 1.331 + return vallen; 1.332 +} 1.333 + 1.334 +/* Caller must set error code upon failure */ 1.335 +static SECStatus 1.336 +hexToBin(PLArenaPool *pool, SECItem * destItem, const char * src, int len) 1.337 +{ 1.338 + PRUint8 * dest; 1.339 + 1.340 + destItem->data = NULL; 1.341 + if (len <= 0 || (len & 1)) { 1.342 + goto loser; 1.343 + } 1.344 + len >>= 1; 1.345 + if (!SECITEM_AllocItem(pool, destItem, len)) 1.346 + goto loser; 1.347 + dest = destItem->data; 1.348 + for (; len > 0; len--, src += 2) { 1.349 + PRInt16 bin = (x2b[(PRUint8)src[0]] << 4) | x2b[(PRUint8)src[1]]; 1.350 + if (bin < 0) 1.351 + goto loser; 1.352 + *dest++ = (PRUint8)bin; 1.353 + } 1.354 + return SECSuccess; 1.355 +loser: 1.356 + if (!pool) 1.357 + SECITEM_FreeItem(destItem, PR_FALSE); 1.358 + return SECFailure; 1.359 +} 1.360 + 1.361 +/* Parses one AVA, starting at *pbp. Stops at endptr. 1.362 + * Advances *pbp past parsed AVA and trailing separator (if present). 1.363 + * On any error, returns NULL and *pbp is undefined. 1.364 + * On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was 1.365 + * the last character parsed. *pbp is either equal to endptr or 1.366 + * points to first character after separator. 1.367 + */ 1.368 +static CERTAVA * 1.369 +ParseRFC1485AVA(PLArenaPool *arena, const char **pbp, const char *endptr) 1.370 +{ 1.371 + CERTAVA *a; 1.372 + const NameToKind *n2k; 1.373 + const char *bp; 1.374 + int vt = -1; 1.375 + int valLen; 1.376 + SECOidTag kind = SEC_OID_UNKNOWN; 1.377 + SECStatus rv = SECFailure; 1.378 + SECItem derOid = { 0, NULL, 0 }; 1.379 + SECItem derVal = { 0, NULL, 0}; 1.380 + char sep = 0; 1.381 + 1.382 + char tagBuf[32]; 1.383 + char valBuf[1024]; 1.384 + 1.385 + PORT_Assert(arena); 1.386 + if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) || 1.387 + !(valLen = scanVal(pbp, endptr, valBuf, sizeof valBuf))) { 1.388 + goto loser; 1.389 + } 1.390 + 1.391 + bp = *pbp; 1.392 + if (bp < endptr) { 1.393 + sep = *bp++; /* skip over separator */ 1.394 + } 1.395 + *pbp = bp; 1.396 + /* if we haven't finished, insist that we've stopped on a separator */ 1.397 + if (sep && sep != ',' && sep != ';' && sep != '+') { 1.398 + goto loser; 1.399 + } 1.400 + 1.401 + /* is this a dotted decimal OID attribute type ? */ 1.402 + if (!PL_strncasecmp("oid.", tagBuf, 4)) { 1.403 + rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf)); 1.404 + } else { 1.405 + for (n2k = name2kinds; n2k->name; n2k++) { 1.406 + SECOidData *oidrec; 1.407 + if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) { 1.408 + kind = n2k->kind; 1.409 + vt = n2k->valueType; 1.410 + oidrec = SECOID_FindOIDByTag(kind); 1.411 + if (oidrec == NULL) 1.412 + goto loser; 1.413 + derOid = oidrec->oid; 1.414 + break; 1.415 + } 1.416 + } 1.417 + } 1.418 + if (kind == SEC_OID_UNKNOWN && rv != SECSuccess) 1.419 + goto loser; 1.420 + 1.421 + /* Is this a hex encoding of a DER attribute value ? */ 1.422 + if ('#' == valBuf[0]) { 1.423 + /* convert attribute value from hex to binary */ 1.424 + rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1); 1.425 + if (rv) 1.426 + goto loser; 1.427 + a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal); 1.428 + } else { 1.429 + if (kind == SEC_OID_UNKNOWN) 1.430 + goto loser; 1.431 + if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2) 1.432 + goto loser; 1.433 + if (vt == SEC_ASN1_PRINTABLE_STRING && 1.434 + !IsPrintable((unsigned char*) valBuf, valLen)) 1.435 + goto loser; 1.436 + if (vt == SEC_ASN1_DS) { 1.437 + /* RFC 4630: choose PrintableString or UTF8String */ 1.438 + if (IsPrintable((unsigned char*) valBuf, valLen)) 1.439 + vt = SEC_ASN1_PRINTABLE_STRING; 1.440 + else 1.441 + vt = SEC_ASN1_UTF8_STRING; 1.442 + } 1.443 + 1.444 + derVal.data = (unsigned char*) valBuf; 1.445 + derVal.len = valLen; 1.446 + a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal); 1.447 + } 1.448 + return a; 1.449 + 1.450 +loser: 1.451 + /* matched no kind -- invalid tag */ 1.452 + PORT_SetError(SEC_ERROR_INVALID_AVA); 1.453 + return 0; 1.454 +} 1.455 + 1.456 +static CERTName * 1.457 +ParseRFC1485Name(const char *buf, int len) 1.458 +{ 1.459 + SECStatus rv; 1.460 + CERTName *name; 1.461 + const char *bp, *e; 1.462 + CERTAVA *ava; 1.463 + CERTRDN *rdn = NULL; 1.464 + 1.465 + name = CERT_CreateName(NULL); 1.466 + if (name == NULL) { 1.467 + return NULL; 1.468 + } 1.469 + 1.470 + e = buf + len; 1.471 + bp = buf; 1.472 + while (bp < e) { 1.473 + ava = ParseRFC1485AVA(name->arena, &bp, e); 1.474 + if (ava == 0) 1.475 + goto loser; 1.476 + if (!rdn) { 1.477 + rdn = CERT_CreateRDN(name->arena, ava, (CERTAVA *)0); 1.478 + if (rdn == 0) 1.479 + goto loser; 1.480 + rv = CERT_AddRDN(name, rdn); 1.481 + } else { 1.482 + rv = CERT_AddAVA(name->arena, rdn, ava); 1.483 + } 1.484 + if (rv) 1.485 + goto loser; 1.486 + if (bp[-1] != '+') 1.487 + rdn = NULL; /* done with this RDN */ 1.488 + skipSpace(&bp, e); 1.489 + } 1.490 + 1.491 + if (name->rdns[0] == 0) { 1.492 + /* empty name -- illegal */ 1.493 + goto loser; 1.494 + } 1.495 + 1.496 + /* Reverse order of RDNS to comply with RFC */ 1.497 + { 1.498 + CERTRDN **firstRdn; 1.499 + CERTRDN **lastRdn; 1.500 + CERTRDN *tmp; 1.501 + 1.502 + /* get first one */ 1.503 + firstRdn = name->rdns; 1.504 + 1.505 + /* find last one */ 1.506 + lastRdn = name->rdns; 1.507 + while (*lastRdn) lastRdn++; 1.508 + lastRdn--; 1.509 + 1.510 + /* reverse list */ 1.511 + for ( ; firstRdn < lastRdn; firstRdn++, lastRdn--) { 1.512 + tmp = *firstRdn; 1.513 + *firstRdn = *lastRdn; 1.514 + *lastRdn = tmp; 1.515 + } 1.516 + } 1.517 + 1.518 + /* return result */ 1.519 + return name; 1.520 + 1.521 + loser: 1.522 + CERT_DestroyName(name); 1.523 + return NULL; 1.524 +} 1.525 + 1.526 +CERTName * 1.527 +CERT_AsciiToName(const char *string) 1.528 +{ 1.529 + CERTName *name; 1.530 + name = ParseRFC1485Name(string, PORT_Strlen(string)); 1.531 + return name; 1.532 +} 1.533 + 1.534 +/************************************************************************/ 1.535 + 1.536 +typedef struct stringBufStr { 1.537 + char *buffer; 1.538 + unsigned offset; 1.539 + unsigned size; 1.540 +} stringBuf; 1.541 + 1.542 +#define DEFAULT_BUFFER_SIZE 200 1.543 + 1.544 +static SECStatus 1.545 +AppendStr(stringBuf *bufp, char *str) 1.546 +{ 1.547 + char *buf; 1.548 + unsigned bufLen, bufSize, len; 1.549 + int size = 0; 1.550 + 1.551 + /* Figure out how much to grow buf by (add in the '\0') */ 1.552 + buf = bufp->buffer; 1.553 + bufLen = bufp->offset; 1.554 + len = PORT_Strlen(str); 1.555 + bufSize = bufLen + len; 1.556 + if (!buf) { 1.557 + bufSize++; 1.558 + size = PR_MAX(DEFAULT_BUFFER_SIZE,bufSize*2); 1.559 + buf = (char *) PORT_Alloc(size); 1.560 + bufp->size = size; 1.561 + } else if (bufp->size < bufSize) { 1.562 + size = bufSize*2; 1.563 + buf =(char *) PORT_Realloc(buf,size); 1.564 + bufp->size = size; 1.565 + } 1.566 + if (!buf) { 1.567 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.568 + return SECFailure; 1.569 + } 1.570 + bufp->buffer = buf; 1.571 + bufp->offset = bufSize; 1.572 + 1.573 + /* Concatenate str onto buf */ 1.574 + buf = buf + bufLen; 1.575 + if (bufLen) buf--; /* stomp on old '\0' */ 1.576 + PORT_Memcpy(buf, str, len+1); /* put in new null */ 1.577 + return SECSuccess; 1.578 +} 1.579 + 1.580 +typedef enum { 1.581 + minimalEscape = 0, /* only hex escapes, and " and \ */ 1.582 + minimalEscapeAndQuote, /* as above, plus quoting */ 1.583 + fullEscape /* no quoting, full escaping */ 1.584 +} EQMode; 1.585 + 1.586 +/* Some characters must be escaped as a hex string, e.g. c -> \nn . 1.587 + * Others must be escaped by preceding with a '\', e.g. c -> \c , but 1.588 + * there are certain "special characters" that may be handled by either 1.589 + * escaping them, or by enclosing the entire attribute value in quotes. 1.590 + * A NULL value for pEQMode implies selecting minimalEscape mode. 1.591 + * Some callers will do quoting when needed, others will not. 1.592 + * If a caller selects minimalEscapeAndQuote, and the string does not 1.593 + * need quoting, then this function changes it to minimalEscape. 1.594 + */ 1.595 +static int 1.596 +cert_RFC1485_GetRequiredLen(const char *src, int srclen, EQMode *pEQMode) 1.597 +{ 1.598 + int i, reqLen=0; 1.599 + EQMode mode = pEQMode ? *pEQMode : minimalEscape; 1.600 + PRBool needsQuoting = PR_FALSE; 1.601 + char lastC = 0; 1.602 + 1.603 + /* need to make an initial pass to determine if quoting is needed */ 1.604 + for (i = 0; i < srclen; i++) { 1.605 + char c = src[i]; 1.606 + reqLen++; 1.607 + if (NEEDS_HEX_ESCAPE(c)) { /* c -> \xx */ 1.608 + reqLen += 2; 1.609 + } else if (NEEDS_ESCAPE(c)) { /* c -> \c */ 1.610 + reqLen++; 1.611 + } else if (SPECIAL_CHAR(c)) { 1.612 + if (mode == minimalEscapeAndQuote) /* quoting is allowed */ 1.613 + needsQuoting = PR_TRUE; /* entirety will need quoting */ 1.614 + else if (mode == fullEscape) 1.615 + reqLen++; /* MAY escape this character */ 1.616 + } else if (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)) { 1.617 + if (mode == minimalEscapeAndQuote) /* quoting is allowed */ 1.618 + needsQuoting = PR_TRUE; /* entirety will need quoting */ 1.619 + } 1.620 + lastC = c; 1.621 + } 1.622 + /* if it begins or ends in optional space it needs quoting */ 1.623 + if (!needsQuoting && srclen > 0 && mode == minimalEscapeAndQuote && 1.624 + (OPTIONAL_SPACE(src[srclen-1]) || OPTIONAL_SPACE(src[0]))) { 1.625 + needsQuoting = PR_TRUE; 1.626 + } 1.627 + 1.628 + if (needsQuoting) 1.629 + reqLen += 2; 1.630 + if (pEQMode && mode == minimalEscapeAndQuote && !needsQuoting) 1.631 + *pEQMode = minimalEscape; 1.632 + return reqLen; 1.633 +} 1.634 + 1.635 +static const char hexChars[16] = { "0123456789abcdef" }; 1.636 + 1.637 +static SECStatus 1.638 +escapeAndQuote(char *dst, int dstlen, char *src, int srclen, EQMode *pEQMode) 1.639 +{ 1.640 + int i, reqLen=0; 1.641 + EQMode mode = pEQMode ? *pEQMode : minimalEscape; 1.642 + 1.643 + /* space for terminal null */ 1.644 + reqLen = cert_RFC1485_GetRequiredLen(src, srclen, &mode) + 1; 1.645 + if (reqLen > dstlen) { 1.646 + PORT_SetError(SEC_ERROR_OUTPUT_LEN); 1.647 + return SECFailure; 1.648 + } 1.649 + 1.650 + if (mode == minimalEscapeAndQuote) 1.651 + *dst++ = C_DOUBLE_QUOTE; 1.652 + for (i = 0; i < srclen; i++) { 1.653 + char c = src[i]; 1.654 + if (NEEDS_HEX_ESCAPE(c)) { 1.655 + *dst++ = C_BACKSLASH; 1.656 + *dst++ = hexChars[ (c >> 4) & 0x0f ]; 1.657 + *dst++ = hexChars[ c & 0x0f ]; 1.658 + } else { 1.659 + if (NEEDS_ESCAPE(c) || (SPECIAL_CHAR(c) && mode == fullEscape)) { 1.660 + *dst++ = C_BACKSLASH; 1.661 + } 1.662 + *dst++ = c; 1.663 + } 1.664 + } 1.665 + if (mode == minimalEscapeAndQuote) 1.666 + *dst++ = C_DOUBLE_QUOTE; 1.667 + *dst++ = 0; 1.668 + if (pEQMode) 1.669 + *pEQMode = mode; 1.670 + return SECSuccess; 1.671 +} 1.672 + 1.673 +SECStatus 1.674 +CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src, int srclen) 1.675 +{ 1.676 + EQMode mode = minimalEscapeAndQuote; 1.677 + return escapeAndQuote(dst, dstlen, src, srclen, &mode); 1.678 +} 1.679 + 1.680 + 1.681 +/* convert an OID to dotted-decimal representation */ 1.682 +/* Returns a string that must be freed with PR_smprintf_free(), */ 1.683 +char * 1.684 +CERT_GetOidString(const SECItem *oid) 1.685 +{ 1.686 + PRUint8 *stop; /* points to first byte after OID string */ 1.687 + PRUint8 *first; /* byte of an OID component integer */ 1.688 + PRUint8 *last; /* byte of an OID component integer */ 1.689 + char *rvString = NULL; 1.690 + char *prefix = NULL; 1.691 + 1.692 +#define MAX_OID_LEN 1024 /* bytes */ 1.693 + 1.694 + if (oid->len > MAX_OID_LEN) { 1.695 + PORT_SetError(SEC_ERROR_INPUT_LEN); 1.696 + return NULL; 1.697 + } 1.698 + 1.699 + /* first will point to the next sequence of bytes to decode */ 1.700 + first = (PRUint8 *)oid->data; 1.701 + /* stop points to one past the legitimate data */ 1.702 + stop = &first[ oid->len ]; 1.703 + 1.704 + /* 1.705 + * Check for our pseudo-encoded single-digit OIDs 1.706 + */ 1.707 + if ((*first == 0x80) && (2 == oid->len)) { 1.708 + /* Funky encoding. The second byte is the number */ 1.709 + rvString = PR_smprintf("%lu", (PRUint32)first[1]); 1.710 + if (!rvString) { 1.711 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.712 + } 1.713 + return rvString; 1.714 + } 1.715 + 1.716 + for (; first < stop; first = last + 1) { 1.717 + unsigned int bytesBeforeLast; 1.718 + 1.719 + for (last = first; last < stop; last++) { 1.720 + if (0 == (*last & 0x80)) { 1.721 + break; 1.722 + } 1.723 + } 1.724 + bytesBeforeLast = (unsigned int)(last - first); 1.725 + if (bytesBeforeLast <= 3U) { /* 0-28 bit number */ 1.726 + PRUint32 n = 0; 1.727 + PRUint32 c; 1.728 + 1.729 +#define CGET(i, m) \ 1.730 + c = last[-i] & m; \ 1.731 + n |= c << (7 * i) 1.732 + 1.733 +#define CASE(i, m) \ 1.734 + case i: \ 1.735 + CGET(i, m); \ 1.736 + if (!n) goto unsupported \ 1.737 + /* fall-through */ 1.738 + 1.739 + switch (bytesBeforeLast) { 1.740 + CASE(3, 0x7f); 1.741 + CASE(2, 0x7f); 1.742 + CASE(1, 0x7f); 1.743 + case 0: n |= last[0] & 0x7f; 1.744 + break; 1.745 + } 1.746 + if (last[0] & 0x80) 1.747 + goto unsupported; 1.748 + 1.749 + if (!rvString) { 1.750 + /* This is the first number.. decompose it */ 1.751 + PRUint32 one = PR_MIN(n/40, 2); /* never > 2 */ 1.752 + PRUint32 two = n - (one * 40); 1.753 + 1.754 + rvString = PR_smprintf("OID.%lu.%lu", one, two); 1.755 + } else { 1.756 + prefix = rvString; 1.757 + rvString = PR_smprintf("%s.%lu", prefix, n); 1.758 + } 1.759 + } else if (bytesBeforeLast <= 9U) { /* 29-64 bit number */ 1.760 + PRUint64 n = 0; 1.761 + PRUint64 c; 1.762 + 1.763 + switch (bytesBeforeLast) { 1.764 + CASE(9, 0x01); 1.765 + CASE(8, 0x7f); 1.766 + CASE(7, 0x7f); 1.767 + CASE(6, 0x7f); 1.768 + CASE(5, 0x7f); 1.769 + CASE(4, 0x7f); 1.770 + CGET(3, 0x7f); 1.771 + CGET(2, 0x7f); 1.772 + CGET(1, 0x7f); 1.773 + CGET(0, 0x7f); 1.774 + break; 1.775 + } 1.776 + if (last[0] & 0x80) 1.777 + goto unsupported; 1.778 + 1.779 + if (!rvString) { 1.780 + /* This is the first number.. decompose it */ 1.781 + PRUint64 one = PR_MIN(n/40, 2); /* never > 2 */ 1.782 + PRUint64 two = n - (one * 40); 1.783 + 1.784 + rvString = PR_smprintf("OID.%llu.%llu", one, two); 1.785 + } else { 1.786 + prefix = rvString; 1.787 + rvString = PR_smprintf("%s.%llu", prefix, n); 1.788 + } 1.789 + } else { 1.790 + /* More than a 64-bit number, or not minimal encoding. */ 1.791 +unsupported: 1.792 + if (!rvString) 1.793 + rvString = PR_smprintf("OID.UNSUPPORTED"); 1.794 + else { 1.795 + prefix = rvString; 1.796 + rvString = PR_smprintf("%s.UNSUPPORTED", prefix); 1.797 + } 1.798 + } 1.799 + 1.800 + if (prefix) { 1.801 + PR_smprintf_free(prefix); 1.802 + prefix = NULL; 1.803 + } 1.804 + if (!rvString) { 1.805 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.806 + break; 1.807 + } 1.808 + } 1.809 + return rvString; 1.810 +} 1.811 + 1.812 +/* convert DER-encoded hex to a string */ 1.813 +static SECItem * 1.814 +get_hex_string(SECItem *data) 1.815 +{ 1.816 + SECItem *rv; 1.817 + unsigned int i, j; 1.818 + static const char hex[] = { "0123456789ABCDEF" }; 1.819 + 1.820 + /* '#' + 2 chars per octet + terminator */ 1.821 + rv = SECITEM_AllocItem(NULL, NULL, data->len*2 + 2); 1.822 + if (!rv) { 1.823 + return NULL; 1.824 + } 1.825 + rv->data[0] = '#'; 1.826 + rv->len = 1 + 2 * data->len; 1.827 + for (i=0; i<data->len; i++) { 1.828 + j = data->data[i]; 1.829 + rv->data[2*i+1] = hex[j >> 4]; 1.830 + rv->data[2*i+2] = hex[j & 15]; 1.831 + } 1.832 + rv->data[rv->len] = 0; 1.833 + return rv; 1.834 +} 1.835 + 1.836 +/* For compliance with RFC 2253, RFC 3280 and RFC 4630, we choose to 1.837 + * use the NAME=STRING form, rather than the OID.N.N=#hexXXXX form, 1.838 + * when both of these conditions are met: 1.839 + * 1) The attribute name OID (kind) has a known name string that is 1.840 + * defined in one of those RFCs, or in RFCs that they cite, AND 1.841 + * 2) The attribute's value encoding is RFC compliant for the kind 1.842 + * (e.g., the value's encoding tag is correct for the kind, and 1.843 + * the value's length is in the range allowed for the kind, and 1.844 + * the value's contents are appropriate for the encoding tag). 1.845 + * Otherwise, we use the OID.N.N=#hexXXXX form. 1.846 + * 1.847 + * If the caller prefers maximum human readability to RFC compliance, 1.848 + * then 1.849 + * - We print the kind in NAME= string form if we know the name 1.850 + * string for the attribute type OID, regardless of whether the 1.851 + * value is correctly encoded or not. else we use the OID.N.N= form. 1.852 + * - We use the non-hex STRING form for the attribute value if the 1.853 + * value can be represented in such a form. Otherwise, we use 1.854 + * the hex string form. 1.855 + * This implies that, for maximum human readability, in addition to 1.856 + * the two forms allowed by the RFC, we allow two other forms of output: 1.857 + * - the OID.N.N=STRING form, and 1.858 + * - the NAME=#hexXXXX form 1.859 + * When the caller prefers maximum human readability, we do not allow 1.860 + * the value of any attribute to exceed the length allowed by the RFC. 1.861 + * If the attribute value exceeds the allowed length, we truncate it to 1.862 + * the allowed length and append "...". 1.863 + * Also in this case, we arbitrarily impose a limit on the length of the 1.864 + * entire AVA encoding, regardless of the form, of 384 bytes per AVA. 1.865 + * This limit includes the trailing NULL character. If the encoded 1.866 + * AVA length exceeds that limit, this function reports failure to encode 1.867 + * the AVA. 1.868 + * 1.869 + * An ASCII representation of an AVA is said to be "invertible" if 1.870 + * conversion back to DER reproduces the original DER encoding exactly. 1.871 + * The RFC 2253 rules do not ensure that all ASCII AVAs derived according 1.872 + * to its rules are invertible. That is because the RFCs allow some 1.873 + * attribute values to be encoded in any of a number of encodings, 1.874 + * and the encoding type information is lost in the non-hex STRING form. 1.875 + * This is particularly true of attributes of type DirectoryString. 1.876 + * The encoding type information is always preserved in the hex string 1.877 + * form, because the hex includes the entire DER encoding of the value. 1.878 + * 1.879 + * So, when the caller perfers maximum invertibility, we apply the 1.880 + * RFC compliance rules stated above, and add a third required 1.881 + * condition on the use of the NAME=STRING form. 1.882 + * 3) The attribute's kind is not is allowed to be encoded in any of 1.883 + * several different encodings, such as DirectoryStrings. 1.884 + * 1.885 + * The chief difference between CERT_N2A_STRICT and CERT_N2A_INVERTIBLE 1.886 + * is that the latter forces DirectoryStrings to be hex encoded. 1.887 + * 1.888 + * As a simplification, we assume the value is correctly encoded for 1.889 + * its encoding type. That is, we do not test that all the characters 1.890 + * in a string encoded type are allowed by that type. We assume it. 1.891 + */ 1.892 +static SECStatus 1.893 +AppendAVA(stringBuf *bufp, CERTAVA *ava, CertStrictnessLevel strict) 1.894 +{ 1.895 +#define TMPBUF_LEN 2048 1.896 + const NameToKind *pn2k = name2kinds; 1.897 + SECItem *avaValue = NULL; 1.898 + char *unknownTag = NULL; 1.899 + char *encodedAVA = NULL; 1.900 + PRBool useHex = PR_FALSE; /* use =#hexXXXX form */ 1.901 + PRBool truncateName = PR_FALSE; 1.902 + PRBool truncateValue = PR_FALSE; 1.903 + SECOidTag endKind; 1.904 + SECStatus rv; 1.905 + unsigned int len; 1.906 + unsigned int nameLen, valueLen; 1.907 + unsigned int maxName, maxValue; 1.908 + EQMode mode = minimalEscapeAndQuote; 1.909 + NameToKind n2k = { NULL, 32767, SEC_OID_UNKNOWN, SEC_ASN1_DS }; 1.910 + char tmpBuf[TMPBUF_LEN]; 1.911 + 1.912 +#define tagName n2k.name /* non-NULL means use NAME= form */ 1.913 +#define maxBytes n2k.maxLen 1.914 +#define tag n2k.kind 1.915 +#define vt n2k.valueType 1.916 + 1.917 + /* READABLE mode recognizes more names from the name2kinds table 1.918 + * than do STRICT or INVERTIBLE modes. This assignment chooses the 1.919 + * point in the table where the attribute type name scanning stops. 1.920 + */ 1.921 + endKind = (strict == CERT_N2A_READABLE) ? SEC_OID_UNKNOWN 1.922 + : SEC_OID_AVA_POSTAL_ADDRESS; 1.923 + tag = CERT_GetAVATag(ava); 1.924 + while (pn2k->kind != tag && pn2k->kind != endKind) { 1.925 + ++pn2k; 1.926 + } 1.927 + 1.928 + if (pn2k->kind != endKind ) { 1.929 + n2k = *pn2k; 1.930 + } else if (strict != CERT_N2A_READABLE) { 1.931 + useHex = PR_TRUE; 1.932 + } 1.933 + /* For invertable form, force Directory Strings to use hex form. */ 1.934 + if (strict == CERT_N2A_INVERTIBLE && vt == SEC_ASN1_DS) { 1.935 + tagName = NULL; /* must use OID.N form */ 1.936 + useHex = PR_TRUE; /* must use hex string */ 1.937 + } 1.938 + if (!useHex) { 1.939 + avaValue = CERT_DecodeAVAValue(&ava->value); 1.940 + if (!avaValue) { 1.941 + useHex = PR_TRUE; 1.942 + if (strict != CERT_N2A_READABLE) { 1.943 + tagName = NULL; /* must use OID.N form */ 1.944 + } 1.945 + } 1.946 + } 1.947 + if (!tagName) { 1.948 + /* handle unknown attribute types per RFC 2253 */ 1.949 + tagName = unknownTag = CERT_GetOidString(&ava->type); 1.950 + if (!tagName) { 1.951 + if (avaValue) 1.952 + SECITEM_FreeItem(avaValue, PR_TRUE); 1.953 + return SECFailure; 1.954 + } 1.955 + } 1.956 + if (useHex) { 1.957 + avaValue = get_hex_string(&ava->value); 1.958 + if (!avaValue) { 1.959 + if (unknownTag) 1.960 + PR_smprintf_free(unknownTag); 1.961 + return SECFailure; 1.962 + } 1.963 + } 1.964 + 1.965 + nameLen = strlen(tagName); 1.966 + valueLen = (useHex ? avaValue->len : 1.967 + cert_RFC1485_GetRequiredLen((char *)avaValue->data, avaValue->len, 1.968 + &mode)); 1.969 + len = nameLen + valueLen + 2; /* Add 2 for '=' and trailing NUL */ 1.970 + 1.971 + maxName = nameLen; 1.972 + maxValue = valueLen; 1.973 + if (len <= sizeof(tmpBuf)) { 1.974 + encodedAVA = tmpBuf; 1.975 + } else if (strict != CERT_N2A_READABLE) { 1.976 + encodedAVA = PORT_Alloc(len); 1.977 + if (!encodedAVA) { 1.978 + SECITEM_FreeItem(avaValue, PR_TRUE); 1.979 + if (unknownTag) 1.980 + PR_smprintf_free(unknownTag); 1.981 + return SECFailure; 1.982 + } 1.983 + } else { 1.984 + /* Must make output fit in tmpbuf */ 1.985 + unsigned int fair = (sizeof tmpBuf)/2 - 1; /* for = and \0 */ 1.986 + 1.987 + if (nameLen < fair) { 1.988 + /* just truncate the value */ 1.989 + maxValue = (sizeof tmpBuf) - (nameLen + 6); /* for "=...\0", 1.990 + and possibly '"' */ 1.991 + } else if (valueLen < fair) { 1.992 + /* just truncate the name */ 1.993 + maxName = (sizeof tmpBuf) - (valueLen + 5); /* for "=...\0" */ 1.994 + } else { 1.995 + /* truncate both */ 1.996 + maxName = maxValue = fair - 3; /* for "..." */ 1.997 + } 1.998 + if (nameLen > maxName) { 1.999 + PORT_Assert(unknownTag && unknownTag == tagName); 1.1000 + truncateName = PR_TRUE; 1.1001 + nameLen = maxName; 1.1002 + } 1.1003 + encodedAVA = tmpBuf; 1.1004 + } 1.1005 + 1.1006 + memcpy(encodedAVA, tagName, nameLen); 1.1007 + if (truncateName) { 1.1008 + /* If tag name is too long, we know it is an OID form that was 1.1009 + * allocated from the heap, so we can modify it in place 1.1010 + */ 1.1011 + encodedAVA[nameLen-1] = '.'; 1.1012 + encodedAVA[nameLen-2] = '.'; 1.1013 + encodedAVA[nameLen-3] = '.'; 1.1014 + } 1.1015 + encodedAVA[nameLen++] = '='; 1.1016 + if (unknownTag) 1.1017 + PR_smprintf_free(unknownTag); 1.1018 + 1.1019 + if (strict == CERT_N2A_READABLE && maxValue > maxBytes) 1.1020 + maxValue = maxBytes; 1.1021 + if (valueLen > maxValue) { 1.1022 + valueLen = maxValue; 1.1023 + truncateValue = PR_TRUE; 1.1024 + } 1.1025 + /* escape and quote as necessary - don't quote hex strings */ 1.1026 + if (useHex) { 1.1027 + char * end = encodedAVA + nameLen + valueLen; 1.1028 + memcpy(encodedAVA + nameLen, (char *)avaValue->data, valueLen); 1.1029 + end[0] = '\0'; 1.1030 + if (truncateValue) { 1.1031 + end[-1] = '.'; 1.1032 + end[-2] = '.'; 1.1033 + end[-3] = '.'; 1.1034 + } 1.1035 + rv = SECSuccess; 1.1036 + } else if (!truncateValue) { 1.1037 + rv = escapeAndQuote(encodedAVA + nameLen, len - nameLen, 1.1038 + (char *)avaValue->data, avaValue->len, &mode); 1.1039 + } else { 1.1040 + /* must truncate the escaped and quoted value */ 1.1041 + char bigTmpBuf[TMPBUF_LEN * 3 + 3]; 1.1042 + PORT_Assert(valueLen < sizeof tmpBuf); 1.1043 + rv = escapeAndQuote(bigTmpBuf, sizeof bigTmpBuf, 1.1044 + (char *)avaValue->data, 1.1045 + PR_MIN(avaValue->len, valueLen), &mode); 1.1046 + 1.1047 + bigTmpBuf[valueLen--] = '\0'; /* hard stop here */ 1.1048 + /* See if we're in the middle of a multi-byte UTF8 character */ 1.1049 + while (((bigTmpBuf[valueLen] & 0xc0) == 0x80) && valueLen > 0) { 1.1050 + bigTmpBuf[valueLen--] = '\0'; 1.1051 + } 1.1052 + /* add ellipsis to signify truncation. */ 1.1053 + bigTmpBuf[++valueLen] = '.'; 1.1054 + bigTmpBuf[++valueLen] = '.'; 1.1055 + bigTmpBuf[++valueLen] = '.'; 1.1056 + if (bigTmpBuf[0] == '"') 1.1057 + bigTmpBuf[++valueLen] = '"'; 1.1058 + bigTmpBuf[++valueLen] = '\0'; 1.1059 + PORT_Assert(nameLen + valueLen <= (sizeof tmpBuf) - 1); 1.1060 + memcpy(encodedAVA + nameLen, bigTmpBuf, valueLen+1); 1.1061 + } 1.1062 + 1.1063 + SECITEM_FreeItem(avaValue, PR_TRUE); 1.1064 + if (rv == SECSuccess) 1.1065 + rv = AppendStr(bufp, encodedAVA); 1.1066 + if (encodedAVA != tmpBuf) 1.1067 + PORT_Free(encodedAVA); 1.1068 + return rv; 1.1069 +} 1.1070 + 1.1071 +#undef tagName 1.1072 +#undef maxBytes 1.1073 +#undef tag 1.1074 +#undef vt 1.1075 + 1.1076 +char * 1.1077 +CERT_NameToAsciiInvertible(CERTName *name, CertStrictnessLevel strict) 1.1078 +{ 1.1079 + CERTRDN** rdns; 1.1080 + CERTRDN** lastRdn; 1.1081 + CERTRDN** rdn; 1.1082 + PRBool first = PR_TRUE; 1.1083 + stringBuf strBuf = { NULL, 0, 0 }; 1.1084 + 1.1085 + rdns = name->rdns; 1.1086 + if (rdns == NULL) { 1.1087 + return NULL; 1.1088 + } 1.1089 + 1.1090 + /* find last RDN */ 1.1091 + lastRdn = rdns; 1.1092 + while (*lastRdn) lastRdn++; 1.1093 + lastRdn--; 1.1094 + 1.1095 + /* 1.1096 + * Loop over name contents in _reverse_ RDN order appending to string 1.1097 + */ 1.1098 + for (rdn = lastRdn; rdn >= rdns; rdn--) { 1.1099 + CERTAVA** avas = (*rdn)->avas; 1.1100 + CERTAVA* ava; 1.1101 + PRBool newRDN = PR_TRUE; 1.1102 + 1.1103 + /* 1.1104 + * XXX Do we need to traverse the AVAs in reverse order, too? 1.1105 + */ 1.1106 + while (avas && (ava = *avas++) != NULL) { 1.1107 + SECStatus rv; 1.1108 + /* Put in comma or plus separator */ 1.1109 + if (!first) { 1.1110 + /* Use of spaces is deprecated in RFC 2253. */ 1.1111 + rv = AppendStr(&strBuf, newRDN ? "," : "+"); 1.1112 + if (rv) goto loser; 1.1113 + } else { 1.1114 + first = PR_FALSE; 1.1115 + } 1.1116 + 1.1117 + /* Add in tag type plus value into strBuf */ 1.1118 + rv = AppendAVA(&strBuf, ava, strict); 1.1119 + if (rv) goto loser; 1.1120 + newRDN = PR_FALSE; 1.1121 + } 1.1122 + } 1.1123 + return strBuf.buffer; 1.1124 +loser: 1.1125 + if (strBuf.buffer) { 1.1126 + PORT_Free(strBuf.buffer); 1.1127 + } 1.1128 + return NULL; 1.1129 +} 1.1130 + 1.1131 +char * 1.1132 +CERT_NameToAscii(CERTName *name) 1.1133 +{ 1.1134 + return CERT_NameToAsciiInvertible(name, CERT_N2A_READABLE); 1.1135 +} 1.1136 + 1.1137 +/* 1.1138 + * Return the string representation of a DER encoded distinguished name 1.1139 + * "dername" - The DER encoded name to convert 1.1140 + */ 1.1141 +char * 1.1142 +CERT_DerNameToAscii(SECItem *dername) 1.1143 +{ 1.1144 + int rv; 1.1145 + PLArenaPool *arena = NULL; 1.1146 + CERTName name; 1.1147 + char *retstr = NULL; 1.1148 + 1.1149 + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.1150 + 1.1151 + if ( arena == NULL) { 1.1152 + goto loser; 1.1153 + } 1.1154 + 1.1155 + rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername); 1.1156 + 1.1157 + if ( rv != SECSuccess ) { 1.1158 + goto loser; 1.1159 + } 1.1160 + 1.1161 + retstr = CERT_NameToAscii(&name); 1.1162 + 1.1163 +loser: 1.1164 + if ( arena != NULL ) { 1.1165 + PORT_FreeArena(arena, PR_FALSE); 1.1166 + } 1.1167 + 1.1168 + return(retstr); 1.1169 +} 1.1170 + 1.1171 +static char * 1.1172 +avaToString(PLArenaPool *arena, CERTAVA *ava) 1.1173 +{ 1.1174 + char * buf = NULL; 1.1175 + SECItem* avaValue; 1.1176 + int valueLen; 1.1177 + 1.1178 + avaValue = CERT_DecodeAVAValue(&ava->value); 1.1179 + if(!avaValue) { 1.1180 + return buf; 1.1181 + } 1.1182 + valueLen = cert_RFC1485_GetRequiredLen((char *)avaValue->data, 1.1183 + avaValue->len, NULL) + 1; 1.1184 + if (arena) { 1.1185 + buf = (char *)PORT_ArenaZAlloc(arena, valueLen); 1.1186 + } else { 1.1187 + buf = (char *)PORT_ZAlloc(valueLen); 1.1188 + } 1.1189 + if (buf) { 1.1190 + SECStatus rv = escapeAndQuote(buf, valueLen, (char *)avaValue->data, 1.1191 + avaValue->len, NULL); 1.1192 + if (rv != SECSuccess) { 1.1193 + if (!arena) 1.1194 + PORT_Free(buf); 1.1195 + buf = NULL; 1.1196 + } 1.1197 + } 1.1198 + SECITEM_FreeItem(avaValue, PR_TRUE); 1.1199 + return buf; 1.1200 +} 1.1201 + 1.1202 +/* RDNs are sorted from most general to most specific. 1.1203 + * This code returns the FIRST one found, the most general one found. 1.1204 + */ 1.1205 +static char * 1.1206 +CERT_GetNameElement(PLArenaPool *arena, const CERTName *name, int wantedTag) 1.1207 +{ 1.1208 + CERTRDN** rdns = name->rdns; 1.1209 + CERTRDN* rdn; 1.1210 + CERTAVA* ava = NULL; 1.1211 + 1.1212 + while (rdns && (rdn = *rdns++) != 0) { 1.1213 + CERTAVA** avas = rdn->avas; 1.1214 + while (avas && (ava = *avas++) != 0) { 1.1215 + int tag = CERT_GetAVATag(ava); 1.1216 + if ( tag == wantedTag ) { 1.1217 + avas = NULL; 1.1218 + rdns = NULL; /* break out of all loops */ 1.1219 + } 1.1220 + } 1.1221 + } 1.1222 + return ava ? avaToString(arena, ava) : NULL; 1.1223 +} 1.1224 + 1.1225 +/* RDNs are sorted from most general to most specific. 1.1226 + * This code returns the LAST one found, the most specific one found. 1.1227 + * This is particularly appropriate for Common Name. See RFC 2818. 1.1228 + */ 1.1229 +static char * 1.1230 +CERT_GetLastNameElement(PLArenaPool *arena, const CERTName *name, int wantedTag) 1.1231 +{ 1.1232 + CERTRDN** rdns = name->rdns; 1.1233 + CERTRDN* rdn; 1.1234 + CERTAVA* lastAva = NULL; 1.1235 + 1.1236 + while (rdns && (rdn = *rdns++) != 0) { 1.1237 + CERTAVA** avas = rdn->avas; 1.1238 + CERTAVA* ava; 1.1239 + while (avas && (ava = *avas++) != 0) { 1.1240 + int tag = CERT_GetAVATag(ava); 1.1241 + if ( tag == wantedTag ) { 1.1242 + lastAva = ava; 1.1243 + } 1.1244 + } 1.1245 + } 1.1246 + return lastAva ? avaToString(arena, lastAva) : NULL; 1.1247 +} 1.1248 + 1.1249 +char * 1.1250 +CERT_GetCertificateEmailAddress(CERTCertificate *cert) 1.1251 +{ 1.1252 + char *rawEmailAddr = NULL; 1.1253 + SECItem subAltName; 1.1254 + SECStatus rv; 1.1255 + CERTGeneralName *nameList = NULL; 1.1256 + CERTGeneralName *current; 1.1257 + PLArenaPool *arena = NULL; 1.1258 + int i; 1.1259 + 1.1260 + subAltName.data = NULL; 1.1261 + 1.1262 + rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), 1.1263 + SEC_OID_PKCS9_EMAIL_ADDRESS); 1.1264 + if ( rawEmailAddr == NULL ) { 1.1265 + rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), 1.1266 + SEC_OID_RFC1274_MAIL); 1.1267 + } 1.1268 + if ( rawEmailAddr == NULL) { 1.1269 + 1.1270 + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, 1.1271 + &subAltName); 1.1272 + if (rv != SECSuccess) { 1.1273 + goto finish; 1.1274 + } 1.1275 + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.1276 + if (!arena) { 1.1277 + goto finish; 1.1278 + } 1.1279 + nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); 1.1280 + if (!nameList ) { 1.1281 + goto finish; 1.1282 + } 1.1283 + if (nameList != NULL) { 1.1284 + do { 1.1285 + if (current->type == certDirectoryName) { 1.1286 + rawEmailAddr = CERT_GetNameElement(cert->arena, 1.1287 + &(current->name.directoryName), 1.1288 + SEC_OID_PKCS9_EMAIL_ADDRESS); 1.1289 + if ( rawEmailAddr == NULL ) { 1.1290 + rawEmailAddr = CERT_GetNameElement(cert->arena, 1.1291 + &(current->name.directoryName), SEC_OID_RFC1274_MAIL); 1.1292 + } 1.1293 + } else if (current->type == certRFC822Name) { 1.1294 + rawEmailAddr = (char*)PORT_ArenaZAlloc(cert->arena, 1.1295 + current->name.other.len + 1); 1.1296 + if (!rawEmailAddr) { 1.1297 + goto finish; 1.1298 + } 1.1299 + PORT_Memcpy(rawEmailAddr, current->name.other.data, 1.1300 + current->name.other.len); 1.1301 + rawEmailAddr[current->name.other.len] = '\0'; 1.1302 + } 1.1303 + if (rawEmailAddr) { 1.1304 + break; 1.1305 + } 1.1306 + current = CERT_GetNextGeneralName(current); 1.1307 + } while (current != nameList); 1.1308 + } 1.1309 + } 1.1310 + if (rawEmailAddr) { 1.1311 + for (i = 0; i <= (int) PORT_Strlen(rawEmailAddr); i++) { 1.1312 + rawEmailAddr[i] = tolower(rawEmailAddr[i]); 1.1313 + } 1.1314 + } 1.1315 + 1.1316 +finish: 1.1317 + 1.1318 + /* Don't free nameList, it's part of the arena. */ 1.1319 + 1.1320 + if (arena) { 1.1321 + PORT_FreeArena(arena, PR_FALSE); 1.1322 + } 1.1323 + 1.1324 + if ( subAltName.data ) { 1.1325 + SECITEM_FreeItem(&subAltName, PR_FALSE); 1.1326 + } 1.1327 + 1.1328 + return(rawEmailAddr); 1.1329 +} 1.1330 + 1.1331 +static char * 1.1332 +appendStringToBuf(char *dest, char *src, PRUint32 *pRemaining) 1.1333 +{ 1.1334 + PRUint32 len; 1.1335 + if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) { 1.1336 + PRUint32 i; 1.1337 + for (i = 0; i < len; ++i) 1.1338 + dest[i] = tolower(src[i]); 1.1339 + dest[len] = 0; 1.1340 + dest += len + 1; 1.1341 + *pRemaining -= len + 1; 1.1342 + } 1.1343 + return dest; 1.1344 +} 1.1345 + 1.1346 +#undef NEEDS_HEX_ESCAPE 1.1347 +#define NEEDS_HEX_ESCAPE(c) (c < 0x20) 1.1348 + 1.1349 +static char * 1.1350 +appendItemToBuf(char *dest, SECItem *src, PRUint32 *pRemaining) 1.1351 +{ 1.1352 + if (dest && src && src->data && src->len && src->data[0]) { 1.1353 + PRUint32 len = src->len; 1.1354 + PRUint32 i; 1.1355 + PRUint32 reqLen = len + 1; 1.1356 + /* are there any embedded control characters ? */ 1.1357 + for (i = 0; i < len; i++) { 1.1358 + if (NEEDS_HEX_ESCAPE(src->data[i])) 1.1359 + reqLen += 2; 1.1360 + } 1.1361 + if (*pRemaining > reqLen) { 1.1362 + for (i = 0; i < len; ++i) { 1.1363 + PRUint8 c = src->data[i]; 1.1364 + if (NEEDS_HEX_ESCAPE(c)) { 1.1365 + *dest++ = C_BACKSLASH; 1.1366 + *dest++ = hexChars[ (c >> 4) & 0x0f ]; 1.1367 + *dest++ = hexChars[ c & 0x0f ]; 1.1368 + } else { 1.1369 + *dest++ = tolower(c); 1.1370 + } 1.1371 + } 1.1372 + *dest++ = '\0'; 1.1373 + *pRemaining -= reqLen; 1.1374 + } 1.1375 + } 1.1376 + return dest; 1.1377 +} 1.1378 + 1.1379 +/* Returns a pointer to an environment-like string, a series of 1.1380 +** null-terminated strings, terminated by a zero-length string. 1.1381 +** This function is intended to be internal to NSS. 1.1382 +*/ 1.1383 +char * 1.1384 +cert_GetCertificateEmailAddresses(CERTCertificate *cert) 1.1385 +{ 1.1386 + char * rawEmailAddr = NULL; 1.1387 + char * addrBuf = NULL; 1.1388 + char * pBuf = NULL; 1.1389 + PLArenaPool * tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 1.1390 + PRUint32 maxLen = 0; 1.1391 + PRInt32 finalLen = 0; 1.1392 + SECStatus rv; 1.1393 + SECItem subAltName; 1.1394 + 1.1395 + if (!tmpArena) 1.1396 + return addrBuf; 1.1397 + 1.1398 + subAltName.data = NULL; 1.1399 + maxLen = cert->derCert.len; 1.1400 + PORT_Assert(maxLen); 1.1401 + if (!maxLen) 1.1402 + maxLen = 2000; /* a guess, should never happen */ 1.1403 + 1.1404 + pBuf = addrBuf = (char *)PORT_ArenaZAlloc(tmpArena, maxLen + 1); 1.1405 + if (!addrBuf) 1.1406 + goto loser; 1.1407 + 1.1408 + rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject, 1.1409 + SEC_OID_PKCS9_EMAIL_ADDRESS); 1.1410 + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); 1.1411 + 1.1412 + rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject, 1.1413 + SEC_OID_RFC1274_MAIL); 1.1414 + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); 1.1415 + 1.1416 + rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, 1.1417 + &subAltName); 1.1418 + if (rv == SECSuccess && subAltName.data) { 1.1419 + CERTGeneralName *nameList = NULL; 1.1420 + 1.1421 + if (!!(nameList = CERT_DecodeAltNameExtension(tmpArena, &subAltName))) { 1.1422 + CERTGeneralName *current = nameList; 1.1423 + do { 1.1424 + if (current->type == certDirectoryName) { 1.1425 + rawEmailAddr = CERT_GetNameElement(tmpArena, 1.1426 + ¤t->name.directoryName, 1.1427 + SEC_OID_PKCS9_EMAIL_ADDRESS); 1.1428 + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); 1.1429 + 1.1430 + rawEmailAddr = CERT_GetNameElement(tmpArena, 1.1431 + ¤t->name.directoryName, 1.1432 + SEC_OID_RFC1274_MAIL); 1.1433 + pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); 1.1434 + } else if (current->type == certRFC822Name) { 1.1435 + pBuf = appendItemToBuf(pBuf, ¤t->name.other, &maxLen); 1.1436 + } 1.1437 + current = CERT_GetNextGeneralName(current); 1.1438 + } while (current != nameList); 1.1439 + } 1.1440 + SECITEM_FreeItem(&subAltName, PR_FALSE); 1.1441 + /* Don't free nameList, it's part of the tmpArena. */ 1.1442 + } 1.1443 + /* now copy superstring to cert's arena */ 1.1444 + finalLen = (pBuf - addrBuf) + 1; 1.1445 + pBuf = NULL; 1.1446 + if (finalLen > 1) { 1.1447 + pBuf = PORT_ArenaAlloc(cert->arena, finalLen); 1.1448 + if (pBuf) { 1.1449 + PORT_Memcpy(pBuf, addrBuf, finalLen); 1.1450 + } 1.1451 + } 1.1452 +loser: 1.1453 + if (tmpArena) 1.1454 + PORT_FreeArena(tmpArena, PR_FALSE); 1.1455 + 1.1456 + return pBuf; 1.1457 +} 1.1458 + 1.1459 +/* returns pointer to storage in cert's arena. Storage remains valid 1.1460 +** as long as cert's reference count doesn't go to zero. 1.1461 +** Caller should strdup or otherwise copy. 1.1462 +*/ 1.1463 +const char * /* const so caller won't muck with it. */ 1.1464 +CERT_GetFirstEmailAddress(CERTCertificate * cert) 1.1465 +{ 1.1466 + if (cert && cert->emailAddr && cert->emailAddr[0]) 1.1467 + return (const char *)cert->emailAddr; 1.1468 + return NULL; 1.1469 +} 1.1470 + 1.1471 +/* returns pointer to storage in cert's arena. Storage remains valid 1.1472 +** as long as cert's reference count doesn't go to zero. 1.1473 +** Caller should strdup or otherwise copy. 1.1474 +*/ 1.1475 +const char * /* const so caller won't muck with it. */ 1.1476 +CERT_GetNextEmailAddress(CERTCertificate * cert, const char * prev) 1.1477 +{ 1.1478 + if (cert && prev && prev[0]) { 1.1479 + PRUint32 len = PL_strlen(prev); 1.1480 + prev += len + 1; 1.1481 + if (prev && prev[0]) 1.1482 + return prev; 1.1483 + } 1.1484 + return NULL; 1.1485 +} 1.1486 + 1.1487 +/* This is seriously bogus, now that certs store their email addresses in 1.1488 +** subject Alternative Name extensions. 1.1489 +** Returns a string allocated by PORT_StrDup, which the caller must free. 1.1490 +*/ 1.1491 +char * 1.1492 +CERT_GetCertEmailAddress(const CERTName *name) 1.1493 +{ 1.1494 + char *rawEmailAddr; 1.1495 + char *emailAddr; 1.1496 + 1.1497 + 1.1498 + rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS); 1.1499 + if ( rawEmailAddr == NULL ) { 1.1500 + rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL); 1.1501 + } 1.1502 + emailAddr = CERT_FixupEmailAddr(rawEmailAddr); 1.1503 + if ( rawEmailAddr ) { 1.1504 + PORT_Free(rawEmailAddr); 1.1505 + } 1.1506 + return(emailAddr); 1.1507 +} 1.1508 + 1.1509 +/* The return value must be freed with PORT_Free. */ 1.1510 +char * 1.1511 +CERT_GetCommonName(const CERTName *name) 1.1512 +{ 1.1513 + return(CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME)); 1.1514 +} 1.1515 + 1.1516 +char * 1.1517 +CERT_GetCountryName(const CERTName *name) 1.1518 +{ 1.1519 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME)); 1.1520 +} 1.1521 + 1.1522 +char * 1.1523 +CERT_GetLocalityName(const CERTName *name) 1.1524 +{ 1.1525 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY)); 1.1526 +} 1.1527 + 1.1528 +char * 1.1529 +CERT_GetStateName(const CERTName *name) 1.1530 +{ 1.1531 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE)); 1.1532 +} 1.1533 + 1.1534 +char * 1.1535 +CERT_GetOrgName(const CERTName *name) 1.1536 +{ 1.1537 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME)); 1.1538 +} 1.1539 + 1.1540 +char * 1.1541 +CERT_GetDomainComponentName(const CERTName *name) 1.1542 +{ 1.1543 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC)); 1.1544 +} 1.1545 + 1.1546 +char * 1.1547 +CERT_GetOrgUnitName(const CERTName *name) 1.1548 +{ 1.1549 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME)); 1.1550 +} 1.1551 + 1.1552 +char * 1.1553 +CERT_GetDnQualifier(const CERTName *name) 1.1554 +{ 1.1555 + return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER)); 1.1556 +} 1.1557 + 1.1558 +char * 1.1559 +CERT_GetCertUid(const CERTName *name) 1.1560 +{ 1.1561 + return(CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID)); 1.1562 +} 1.1563 +