/src/nss-nspr/nss/lib/certdb/alg1485.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* alg1485.c - implementation of RFCs 1485, 1779 and 2253. |
2 | | * |
3 | | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include <limits.h> |
8 | | #include "prprf.h" |
9 | | #include "cert.h" |
10 | | #include "certi.h" |
11 | | #include "xconst.h" |
12 | | #include "genname.h" |
13 | | #include "secitem.h" |
14 | | #include "secerr.h" |
15 | | |
16 | | typedef struct NameToKindStr { |
17 | | const char* name; |
18 | | unsigned int maxLen; /* max bytes in UTF8 encoded string value */ |
19 | | SECOidTag kind; |
20 | | int valueType; |
21 | | } NameToKind; |
22 | | |
23 | | /* local type for directory string--could be printable_string or utf8 */ |
24 | 0 | #define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER |
25 | | |
26 | | /* clang-format off */ |
27 | | |
28 | | /* Add new entries to this table, and maybe to function ParseRFC1485AVA */ |
29 | | static const NameToKind name2kinds[] = { |
30 | | /* IANA registered type names |
31 | | * (See: http://www.iana.org/assignments/ldap-parameters) |
32 | | */ |
33 | | /* RFC 3280, 4630 MUST SUPPORT */ |
34 | | { "CN", 640, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS}, |
35 | | { "ST", 128, SEC_OID_AVA_STATE_OR_PROVINCE, |
36 | | SEC_ASN1_DS}, |
37 | | { "O", 128, SEC_OID_AVA_ORGANIZATION_NAME, |
38 | | SEC_ASN1_DS}, |
39 | | { "OU", 128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, |
40 | | SEC_ASN1_DS}, |
41 | | { "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING}, |
42 | | { "C", 2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING}, |
43 | | { "serialNumber", 64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING}, |
44 | | |
45 | | /* RFC 3280, 4630 SHOULD SUPPORT */ |
46 | | { "L", 128, SEC_OID_AVA_LOCALITY, SEC_ASN1_DS}, |
47 | | { "title", 64, SEC_OID_AVA_TITLE, SEC_ASN1_DS}, |
48 | | { "SN", 64, SEC_OID_AVA_SURNAME, SEC_ASN1_DS}, |
49 | | { "givenName", 64, SEC_OID_AVA_GIVEN_NAME, SEC_ASN1_DS}, |
50 | | { "initials", 64, SEC_OID_AVA_INITIALS, SEC_ASN1_DS}, |
51 | | { "generationQualifier", |
52 | | 64, SEC_OID_AVA_GENERATION_QUALIFIER, |
53 | | SEC_ASN1_DS}, |
54 | | /* RFC 3280, 4630 MAY SUPPORT */ |
55 | | { "DC", 128, SEC_OID_AVA_DC, SEC_ASN1_IA5_STRING}, |
56 | | { "MAIL", 256, SEC_OID_RFC1274_MAIL, SEC_ASN1_IA5_STRING}, |
57 | | { "UID", 256, SEC_OID_RFC1274_UID, SEC_ASN1_DS}, |
58 | | |
59 | | /* ------------------ "strict" boundary --------------------------------- |
60 | | * In strict mode, cert_NameToAscii does not encode any of the attributes |
61 | | * below this line. The first SECOidTag below this line must be used to |
62 | | * conditionally define the "endKind" in function AppendAVA() below. |
63 | | * Most new attribute names should be added below this line. |
64 | | * Maybe this line should be up higher? Say, after the 3280 MUSTs and |
65 | | * before the 3280 SHOULDs? |
66 | | */ |
67 | | |
68 | | /* values from draft-ietf-ldapbis-user-schema-05 (not in RFC 3280) */ |
69 | | { "postalAddress", 128, SEC_OID_AVA_POSTAL_ADDRESS, SEC_ASN1_DS}, |
70 | | { "postalCode", 40, SEC_OID_AVA_POSTAL_CODE, SEC_ASN1_DS}, |
71 | | { "postOfficeBox", 40, SEC_OID_AVA_POST_OFFICE_BOX,SEC_ASN1_DS}, |
72 | | { "houseIdentifier",64, SEC_OID_AVA_HOUSE_IDENTIFIER,SEC_ASN1_DS}, |
73 | | /* end of IANA registered type names */ |
74 | | |
75 | | /* legacy keywords */ |
76 | | { "E", 128, SEC_OID_PKCS9_EMAIL_ADDRESS,SEC_ASN1_IA5_STRING}, |
77 | | { "STREET", 128, SEC_OID_AVA_STREET_ADDRESS, SEC_ASN1_DS}, |
78 | | { "pseudonym", 64, SEC_OID_AVA_PSEUDONYM, SEC_ASN1_DS}, |
79 | | |
80 | | /* values defined by the CAB Forum for EV */ |
81 | | { "incorporationLocality", 128, SEC_OID_EV_INCORPORATION_LOCALITY, |
82 | | SEC_ASN1_DS}, |
83 | | { "incorporationState", 128, SEC_OID_EV_INCORPORATION_STATE, |
84 | | SEC_ASN1_DS}, |
85 | | { "incorporationCountry", 2, SEC_OID_EV_INCORPORATION_COUNTRY, |
86 | | SEC_ASN1_PRINTABLE_STRING}, |
87 | | { "businessCategory", 64, SEC_OID_BUSINESS_CATEGORY, SEC_ASN1_DS}, |
88 | | |
89 | | /* values defined in X.520 */ |
90 | | { "name", 64, SEC_OID_AVA_NAME, SEC_ASN1_DS}, |
91 | | |
92 | | { 0, 256, SEC_OID_UNKNOWN, 0}, |
93 | | }; |
94 | | |
95 | | /* Table facilitates conversion of ASCII hex to binary. */ |
96 | | static const PRInt16 x2b[256] = { |
97 | | /* #0x */ -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, |
99 | | /* #2x */ -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, |
101 | | /* #4x */ -1, 10, 11, 12, 13, 14, 15, -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, |
103 | | /* #6x */ -1, 10, 11, 12, 13, 14, 15, -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, |
105 | | /* #8x */ -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, |
107 | | /* #ax */ -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, |
109 | | /* #cx */ -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, |
111 | | /* #ex */ -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 |
113 | | }; |
114 | | |
115 | 0 | #define IS_HEX(c) (x2b[(PRUint8)(c)] >= 0) |
116 | | |
117 | 0 | #define C_DOUBLE_QUOTE '\042' |
118 | | |
119 | 0 | #define C_BACKSLASH '\134' |
120 | | |
121 | 0 | #define C_EQUAL '=' |
122 | | |
123 | | #define OPTIONAL_SPACE(c) \ |
124 | 0 | (((c) == ' ') || ((c) == '\r') || ((c) == '\n')) |
125 | | |
126 | | #define SPECIAL_CHAR(c) \ |
127 | 0 | (((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) || \ |
128 | 0 | ((c) == '\r') || ((c) == '\n') || ((c) == '+') || \ |
129 | 0 | ((c) == '<') || ((c) == '>') || ((c) == '#') || \ |
130 | 0 | ((c) == ';') || ((c) == C_BACKSLASH)) |
131 | | |
132 | | |
133 | | #define IS_PRINTABLE(c) \ |
134 | 0 | ((((c) >= 'a') && ((c) <= 'z')) || \ |
135 | 0 | (((c) >= 'A') && ((c) <= 'Z')) || \ |
136 | 0 | (((c) >= '0') && ((c) <= '9')) || \ |
137 | 0 | ((c) == ' ') || \ |
138 | 0 | ((c) == '\'') || \ |
139 | 0 | ((c) == '\050') || /* ( */ \ |
140 | 0 | ((c) == '\051') || /* ) */ \ |
141 | 0 | (((c) >= '+') && ((c) <= '/')) || /* + , - . / */ \ |
142 | 0 | ((c) == ':') || \ |
143 | 0 | ((c) == '=') || \ |
144 | 0 | ((c) == '?')) |
145 | | |
146 | | /* clang-format on */ |
147 | | |
148 | | /* RFC 2253 says we must escape ",+\"\\<>;=" EXCEPT inside a quoted string. |
149 | | * Inside a quoted string, we only need to escape " and \ |
150 | | * We choose to quote strings containing any of those special characters, |
151 | | * so we only need to escape " and \ |
152 | | */ |
153 | 0 | #define NEEDS_ESCAPE(c) (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) |
154 | | |
155 | 0 | #define NEEDS_HEX_ESCAPE(c) ((PRUint8)c < 0x20 || c == 0x7f) |
156 | | |
157 | | int |
158 | | cert_AVAOidTagToMaxLen(SECOidTag tag) |
159 | 0 | { |
160 | 0 | const NameToKind* n2k = name2kinds; |
161 | |
|
162 | 0 | while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) { |
163 | 0 | ++n2k; |
164 | 0 | } |
165 | 0 | return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1; |
166 | 0 | } |
167 | | |
168 | | static PRBool |
169 | | IsPrintable(unsigned char* data, unsigned len) |
170 | 0 | { |
171 | 0 | unsigned char ch, *end; |
172 | |
|
173 | 0 | end = data + len; |
174 | 0 | while (data < end) { |
175 | 0 | ch = *data++; |
176 | 0 | if (!IS_PRINTABLE(ch)) { |
177 | 0 | return PR_FALSE; |
178 | 0 | } |
179 | 0 | } |
180 | 0 | return PR_TRUE; |
181 | 0 | } |
182 | | |
183 | | static void |
184 | | skipSpace(const char** pbp, const char* endptr) |
185 | 0 | { |
186 | 0 | const char* bp = *pbp; |
187 | 0 | while (bp < endptr && OPTIONAL_SPACE(*bp)) { |
188 | 0 | bp++; |
189 | 0 | } |
190 | 0 | *pbp = bp; |
191 | 0 | } |
192 | | |
193 | | static SECStatus |
194 | | scanTag(const char** pbp, const char* endptr, char* tagBuf, int tagBufSize) |
195 | 0 | { |
196 | 0 | const char* bp; |
197 | 0 | char* tagBufp; |
198 | 0 | int taglen; |
199 | |
|
200 | 0 | PORT_Assert(tagBufSize > 0); |
201 | | |
202 | | /* skip optional leading space */ |
203 | 0 | skipSpace(pbp, endptr); |
204 | 0 | if (*pbp == endptr) { |
205 | | /* nothing left */ |
206 | 0 | return SECFailure; |
207 | 0 | } |
208 | | |
209 | | /* fill tagBuf */ |
210 | 0 | taglen = 0; |
211 | 0 | bp = *pbp; |
212 | 0 | tagBufp = tagBuf; |
213 | 0 | while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) { |
214 | 0 | if (++taglen >= tagBufSize) { |
215 | 0 | *pbp = bp; |
216 | 0 | return SECFailure; |
217 | 0 | } |
218 | 0 | *tagBufp++ = *bp++; |
219 | 0 | } |
220 | | /* null-terminate tagBuf -- guaranteed at least one space left */ |
221 | 0 | *tagBufp++ = 0; |
222 | 0 | *pbp = bp; |
223 | | |
224 | | /* skip trailing spaces till we hit something - should be an equal sign */ |
225 | 0 | skipSpace(pbp, endptr); |
226 | 0 | if (*pbp == endptr) { |
227 | | /* nothing left */ |
228 | 0 | return SECFailure; |
229 | 0 | } |
230 | 0 | if (**pbp != C_EQUAL) { |
231 | | /* should be an equal sign */ |
232 | 0 | return SECFailure; |
233 | 0 | } |
234 | | /* skip over the equal sign */ |
235 | 0 | (*pbp)++; |
236 | |
|
237 | 0 | return SECSuccess; |
238 | 0 | } |
239 | | |
240 | | /* Returns the number of bytes in the value. 0 means failure. */ |
241 | | static int |
242 | | scanVal(const char** pbp, const char* endptr, char* valBuf, int valBufSize) |
243 | 0 | { |
244 | 0 | const char* bp; |
245 | 0 | char* valBufp; |
246 | 0 | int vallen = 0; |
247 | 0 | PRBool isQuoted; |
248 | |
|
249 | 0 | PORT_Assert(valBufSize > 0); |
250 | | |
251 | | /* skip optional leading space */ |
252 | 0 | skipSpace(pbp, endptr); |
253 | 0 | if (*pbp == endptr) { |
254 | | /* nothing left */ |
255 | 0 | return 0; |
256 | 0 | } |
257 | | |
258 | 0 | bp = *pbp; |
259 | | |
260 | | /* quoted? */ |
261 | 0 | if (*bp == C_DOUBLE_QUOTE) { |
262 | 0 | isQuoted = PR_TRUE; |
263 | | /* skip over it */ |
264 | 0 | bp++; |
265 | 0 | } else { |
266 | 0 | isQuoted = PR_FALSE; |
267 | 0 | } |
268 | |
|
269 | 0 | valBufp = valBuf; |
270 | 0 | while (bp < endptr) { |
271 | 0 | char c = *bp; |
272 | 0 | if (c == C_BACKSLASH) { |
273 | | /* escape character */ |
274 | 0 | bp++; |
275 | 0 | if (bp >= endptr) { |
276 | | /* escape charater must appear with paired char */ |
277 | 0 | *pbp = bp; |
278 | 0 | return 0; |
279 | 0 | } |
280 | 0 | c = *bp; |
281 | 0 | if (IS_HEX(c) && (endptr - bp) >= 2 && IS_HEX(bp[1])) { |
282 | 0 | bp++; |
283 | 0 | c = (char)((x2b[(PRUint8)c] << 4) | x2b[(PRUint8)*bp]); |
284 | 0 | } |
285 | 0 | } else if (c == '#' && bp == *pbp) { |
286 | | /* ignore leading #, quotation not required for it. */ |
287 | 0 | } else if (!isQuoted && SPECIAL_CHAR(c)) { |
288 | | /* unescaped special and not within quoted value */ |
289 | 0 | break; |
290 | 0 | } else if (c == C_DOUBLE_QUOTE) { |
291 | | /* reached unescaped double quote */ |
292 | 0 | break; |
293 | 0 | } |
294 | | /* append character */ |
295 | 0 | vallen++; |
296 | 0 | if (vallen >= valBufSize) { |
297 | 0 | *pbp = bp; |
298 | 0 | return 0; |
299 | 0 | } |
300 | 0 | *valBufp++ = c; |
301 | 0 | bp++; |
302 | 0 | } |
303 | | |
304 | | /* strip trailing spaces from unquoted values */ |
305 | 0 | if (!isQuoted) { |
306 | 0 | while (valBufp > valBuf) { |
307 | 0 | char c = valBufp[-1]; |
308 | 0 | if (!OPTIONAL_SPACE(c)) |
309 | 0 | break; |
310 | 0 | --valBufp; |
311 | 0 | } |
312 | 0 | vallen = valBufp - valBuf; |
313 | 0 | } |
314 | |
|
315 | 0 | if (isQuoted) { |
316 | | /* insist that we stopped on a double quote */ |
317 | 0 | if (*bp != C_DOUBLE_QUOTE) { |
318 | 0 | *pbp = bp; |
319 | 0 | return 0; |
320 | 0 | } |
321 | | /* skip over the quote and skip optional space */ |
322 | 0 | bp++; |
323 | 0 | skipSpace(&bp, endptr); |
324 | 0 | } |
325 | | |
326 | 0 | *pbp = bp; |
327 | | |
328 | | /* null-terminate valBuf -- guaranteed at least one space left */ |
329 | 0 | *valBufp = 0; |
330 | |
|
331 | 0 | return vallen; |
332 | 0 | } |
333 | | |
334 | | /* Caller must set error code upon failure */ |
335 | | static SECStatus |
336 | | hexToBin(PLArenaPool* pool, SECItem* destItem, const char* src, int len) |
337 | 0 | { |
338 | 0 | PRUint8* dest; |
339 | |
|
340 | 0 | destItem->data = NULL; |
341 | 0 | if (len <= 0 || (len & 1)) { |
342 | 0 | goto loser; |
343 | 0 | } |
344 | 0 | len >>= 1; |
345 | 0 | if (!SECITEM_AllocItem(pool, destItem, len)) { |
346 | 0 | goto loser; |
347 | 0 | } |
348 | 0 | dest = destItem->data; |
349 | 0 | for (; len > 0; len--, src += 2) { |
350 | 0 | PRUint16 bin = ((PRUint16)x2b[(PRUint8)src[0]] << 4); |
351 | 0 | bin |= (PRUint16)x2b[(PRUint8)src[1]]; |
352 | 0 | if (bin >> 15) { /* is negative */ |
353 | 0 | goto loser; |
354 | 0 | } |
355 | 0 | *dest++ = (PRUint8)bin; |
356 | 0 | } |
357 | 0 | return SECSuccess; |
358 | 0 | loser: |
359 | 0 | if (!pool) |
360 | 0 | SECITEM_FreeItem(destItem, PR_FALSE); |
361 | 0 | return SECFailure; |
362 | 0 | } |
363 | | |
364 | | /* Parses one AVA, starting at *pbp. Stops at endptr. |
365 | | * Advances *pbp past parsed AVA and trailing separator (if present). |
366 | | * On any error, returns NULL and *pbp is undefined. |
367 | | * On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was |
368 | | * the last character parsed. *pbp is either equal to endptr or |
369 | | * points to first character after separator. |
370 | | */ |
371 | | static CERTAVA* |
372 | | ParseRFC1485AVA(PLArenaPool* arena, const char** pbp, const char* endptr) |
373 | 0 | { |
374 | 0 | CERTAVA* a; |
375 | 0 | const NameToKind* n2k; |
376 | 0 | const char* bp; |
377 | 0 | int vt = -1; |
378 | 0 | int valLen; |
379 | 0 | PRBool isDottedOid = PR_FALSE; |
380 | 0 | SECOidTag kind = SEC_OID_UNKNOWN; |
381 | 0 | SECStatus rv = SECFailure; |
382 | 0 | SECItem derOid = { 0, NULL, 0 }; |
383 | 0 | SECItem derVal = { 0, NULL, 0 }; |
384 | 0 | char sep = 0; |
385 | |
|
386 | 0 | char tagBuf[32]; |
387 | 0 | char valBuf[1024]; |
388 | |
|
389 | 0 | PORT_Assert(arena); |
390 | 0 | if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) || |
391 | 0 | !(valLen = scanVal(pbp, endptr, valBuf, sizeof valBuf))) { |
392 | 0 | goto loser; |
393 | 0 | } |
394 | | |
395 | 0 | bp = *pbp; |
396 | 0 | if (bp < endptr) { |
397 | 0 | sep = *bp++; /* skip over separator */ |
398 | 0 | } |
399 | 0 | *pbp = bp; |
400 | | /* if we haven't finished, insist that we've stopped on a separator */ |
401 | 0 | if (sep && sep != ',' && sep != ';' && sep != '+') { |
402 | 0 | goto loser; |
403 | 0 | } |
404 | | |
405 | | /* is this a dotted decimal OID attribute type ? */ |
406 | 0 | if (!PL_strncasecmp("oid.", tagBuf, 4) || isdigit((unsigned char)tagBuf[0])) { |
407 | 0 | rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf)); |
408 | 0 | isDottedOid = (PRBool)(rv == SECSuccess); |
409 | 0 | } else { |
410 | 0 | for (n2k = name2kinds; n2k->name; n2k++) { |
411 | 0 | SECOidData* oidrec; |
412 | 0 | if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) { |
413 | 0 | kind = n2k->kind; |
414 | 0 | vt = n2k->valueType; |
415 | 0 | oidrec = SECOID_FindOIDByTag(kind); |
416 | 0 | if (oidrec == NULL) |
417 | 0 | goto loser; |
418 | 0 | derOid = oidrec->oid; |
419 | 0 | break; |
420 | 0 | } |
421 | 0 | } |
422 | 0 | } |
423 | 0 | if (kind == SEC_OID_UNKNOWN && rv != SECSuccess) |
424 | 0 | goto loser; |
425 | | |
426 | | /* Is this a hex encoding of a DER attribute value ? */ |
427 | 0 | if ('#' == valBuf[0]) { |
428 | | /* convert attribute value from hex to binary */ |
429 | 0 | rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1); |
430 | 0 | if (rv) |
431 | 0 | goto loser; |
432 | 0 | a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal); |
433 | 0 | } else { |
434 | 0 | if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2) |
435 | 0 | goto loser; |
436 | 0 | if (vt == SEC_ASN1_PRINTABLE_STRING && |
437 | 0 | !IsPrintable((unsigned char*)valBuf, valLen)) |
438 | 0 | goto loser; |
439 | 0 | if (vt == SEC_ASN1_DS) { |
440 | | /* RFC 4630: choose PrintableString or UTF8String */ |
441 | 0 | if (IsPrintable((unsigned char*)valBuf, valLen)) |
442 | 0 | vt = SEC_ASN1_PRINTABLE_STRING; |
443 | 0 | else |
444 | 0 | vt = SEC_ASN1_UTF8_STRING; |
445 | 0 | } |
446 | |
|
447 | 0 | derVal.data = (unsigned char*)valBuf; |
448 | 0 | derVal.len = valLen; |
449 | 0 | if (kind == SEC_OID_UNKNOWN && isDottedOid) { |
450 | 0 | a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal); |
451 | 0 | } else { |
452 | 0 | a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal); |
453 | 0 | } |
454 | 0 | } |
455 | 0 | return a; |
456 | | |
457 | 0 | loser: |
458 | | /* matched no kind -- invalid tag */ |
459 | 0 | PORT_SetError(SEC_ERROR_INVALID_AVA); |
460 | 0 | return 0; |
461 | 0 | } |
462 | | |
463 | | static CERTName* |
464 | | ParseRFC1485Name(const char* buf, int len) |
465 | 0 | { |
466 | 0 | SECStatus rv; |
467 | 0 | CERTName* name; |
468 | 0 | const char *bp, *e; |
469 | 0 | CERTAVA* ava; |
470 | 0 | CERTRDN* rdn = NULL; |
471 | |
|
472 | 0 | name = CERT_CreateName(NULL); |
473 | 0 | if (name == NULL) { |
474 | 0 | return NULL; |
475 | 0 | } |
476 | | |
477 | 0 | e = buf + len; |
478 | 0 | bp = buf; |
479 | 0 | while (bp < e) { |
480 | 0 | ava = ParseRFC1485AVA(name->arena, &bp, e); |
481 | 0 | if (ava == 0) |
482 | 0 | goto loser; |
483 | 0 | if (!rdn) { |
484 | 0 | rdn = CERT_CreateRDN(name->arena, ava, (CERTAVA*)0); |
485 | 0 | if (rdn == 0) |
486 | 0 | goto loser; |
487 | 0 | rv = CERT_AddRDN(name, rdn); |
488 | 0 | } else { |
489 | 0 | rv = CERT_AddAVA(name->arena, rdn, ava); |
490 | 0 | } |
491 | 0 | if (rv) |
492 | 0 | goto loser; |
493 | 0 | if (bp[-1] != '+') |
494 | 0 | rdn = NULL; /* done with this RDN */ |
495 | 0 | skipSpace(&bp, e); |
496 | 0 | } |
497 | | |
498 | 0 | if (name->rdns[0] == 0) { |
499 | | /* empty name -- illegal */ |
500 | 0 | goto loser; |
501 | 0 | } |
502 | | |
503 | | /* Reverse order of RDNS to comply with RFC */ |
504 | 0 | { |
505 | 0 | CERTRDN** firstRdn; |
506 | 0 | CERTRDN** lastRdn; |
507 | 0 | CERTRDN* tmp; |
508 | | |
509 | | /* get first one */ |
510 | 0 | firstRdn = name->rdns; |
511 | | |
512 | | /* find last one */ |
513 | 0 | lastRdn = name->rdns; |
514 | 0 | while (*lastRdn) |
515 | 0 | lastRdn++; |
516 | 0 | lastRdn--; |
517 | | |
518 | | /* reverse list */ |
519 | 0 | for (; firstRdn < lastRdn; firstRdn++, lastRdn--) { |
520 | 0 | tmp = *firstRdn; |
521 | 0 | *firstRdn = *lastRdn; |
522 | 0 | *lastRdn = tmp; |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | | /* return result */ |
527 | 0 | return name; |
528 | | |
529 | 0 | loser: |
530 | 0 | CERT_DestroyName(name); |
531 | 0 | return NULL; |
532 | 0 | } |
533 | | |
534 | | CERTName* |
535 | | CERT_AsciiToName(const char* string) |
536 | 0 | { |
537 | 0 | CERTName* name; |
538 | 0 | name = ParseRFC1485Name(string, PORT_Strlen(string)); |
539 | 0 | return name; |
540 | 0 | } |
541 | | |
542 | | /************************************************************************/ |
543 | | |
544 | | typedef struct stringBufStr { |
545 | | char* buffer; |
546 | | unsigned offset; |
547 | | unsigned size; |
548 | | } stringBuf; |
549 | | |
550 | | #define DEFAULT_BUFFER_SIZE 200 |
551 | | |
552 | | static SECStatus |
553 | | AppendStr(stringBuf* bufp, char* str) |
554 | 0 | { |
555 | 0 | char* buf; |
556 | 0 | unsigned bufLen, bufSize, len; |
557 | 0 | int size = 0; |
558 | | |
559 | | /* Figure out how much to grow buf by (add in the '\0') */ |
560 | 0 | buf = bufp->buffer; |
561 | 0 | bufLen = bufp->offset; |
562 | 0 | len = PORT_Strlen(str); |
563 | 0 | bufSize = bufLen + len; |
564 | 0 | if (!buf) { |
565 | 0 | bufSize++; |
566 | 0 | size = PR_MAX(DEFAULT_BUFFER_SIZE, bufSize * 2); |
567 | 0 | buf = (char*)PORT_Alloc(size); |
568 | 0 | bufp->size = size; |
569 | 0 | } else if (bufp->size < bufSize) { |
570 | 0 | size = bufSize * 2; |
571 | 0 | buf = (char*)PORT_Realloc(buf, size); |
572 | 0 | bufp->size = size; |
573 | 0 | } |
574 | 0 | if (!buf) { |
575 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
576 | 0 | return SECFailure; |
577 | 0 | } |
578 | 0 | bufp->buffer = buf; |
579 | 0 | bufp->offset = bufSize; |
580 | | |
581 | | /* Concatenate str onto buf */ |
582 | 0 | buf = buf + bufLen; |
583 | 0 | if (bufLen) |
584 | 0 | buf--; /* stomp on old '\0' */ |
585 | 0 | PORT_Memcpy(buf, str, len + 1); /* put in new null */ |
586 | 0 | return SECSuccess; |
587 | 0 | } |
588 | | |
589 | | typedef enum { |
590 | | minimalEscape = 0, /* only hex escapes, and " and \ */ |
591 | | minimalEscapeAndQuote, /* as above, plus quoting */ |
592 | | fullEscape /* no quoting, full escaping */ |
593 | | } EQMode; |
594 | | |
595 | | /* Some characters must be escaped as a hex string, e.g. c -> \nn . |
596 | | * Others must be escaped by preceding with a '\', e.g. c -> \c , but |
597 | | * there are certain "special characters" that may be handled by either |
598 | | * escaping them, or by enclosing the entire attribute value in quotes. |
599 | | * A NULL value for pEQMode implies selecting minimalEscape mode. |
600 | | * Some callers will do quoting when needed, others will not. |
601 | | * If a caller selects minimalEscapeAndQuote, and the string does not |
602 | | * need quoting, then this function changes it to minimalEscape. |
603 | | * Limit source to 16K, which avoids any possibility of overflow. |
604 | | * Maximum output size would be 3*srclen+2. |
605 | | */ |
606 | | static int |
607 | | cert_RFC1485_GetRequiredLen(const char* src, int srclen, EQMode* pEQMode) |
608 | 0 | { |
609 | 0 | int i, reqLen = 0; |
610 | 0 | EQMode mode = pEQMode ? *pEQMode : minimalEscape; |
611 | 0 | PRBool needsQuoting = PR_FALSE; |
612 | 0 | char lastC = 0; |
613 | | |
614 | | /* avoids needing to check for overflow */ |
615 | 0 | if (srclen > 16384) { |
616 | 0 | return -1; |
617 | 0 | } |
618 | | /* need to make an initial pass to determine if quoting is needed */ |
619 | 0 | for (i = 0; i < srclen; i++) { |
620 | 0 | char c = src[i]; |
621 | 0 | reqLen++; |
622 | 0 | if (NEEDS_HEX_ESCAPE(c)) { /* c -> \xx */ |
623 | 0 | reqLen += 2; |
624 | 0 | } else if (NEEDS_ESCAPE(c)) { /* c -> \c */ |
625 | 0 | reqLen++; |
626 | 0 | } else if (SPECIAL_CHAR(c)) { |
627 | 0 | if (mode == minimalEscapeAndQuote) /* quoting is allowed */ |
628 | 0 | needsQuoting = PR_TRUE; /* entirety will need quoting */ |
629 | 0 | else if (mode == fullEscape) |
630 | 0 | reqLen++; /* MAY escape this character */ |
631 | 0 | } else if (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)) { |
632 | 0 | if (mode == minimalEscapeAndQuote) /* quoting is allowed */ |
633 | 0 | needsQuoting = PR_TRUE; /* entirety will need quoting */ |
634 | 0 | } |
635 | 0 | lastC = c; |
636 | 0 | } |
637 | | /* if it begins or ends in optional space it needs quoting */ |
638 | 0 | if (!needsQuoting && srclen > 0 && mode == minimalEscapeAndQuote && |
639 | 0 | (OPTIONAL_SPACE(src[srclen - 1]) || OPTIONAL_SPACE(src[0]))) { |
640 | 0 | needsQuoting = PR_TRUE; |
641 | 0 | } |
642 | |
|
643 | 0 | if (needsQuoting) |
644 | 0 | reqLen += 2; |
645 | 0 | if (pEQMode && mode == minimalEscapeAndQuote && !needsQuoting) |
646 | 0 | *pEQMode = minimalEscape; |
647 | | /* Maximum output size would be 3*srclen+2 */ |
648 | 0 | return reqLen; |
649 | 0 | } |
650 | | |
651 | | static const char hexChars[16] = { "0123456789abcdef" }; |
652 | | |
653 | | static SECStatus |
654 | | escapeAndQuote(char* dst, int dstlen, char* src, int srclen, EQMode* pEQMode) |
655 | 0 | { |
656 | 0 | int i, reqLen = 0; |
657 | 0 | EQMode mode = pEQMode ? *pEQMode : minimalEscape; |
658 | |
|
659 | 0 | reqLen = cert_RFC1485_GetRequiredLen(src, srclen, &mode); |
660 | | /* reqLen is max 16384*3 + 2 */ |
661 | | /* space for terminal null */ |
662 | 0 | if (reqLen < 0 || reqLen + 1 > dstlen) { |
663 | 0 | PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
664 | 0 | return SECFailure; |
665 | 0 | } |
666 | | |
667 | 0 | if (mode == minimalEscapeAndQuote) |
668 | 0 | *dst++ = C_DOUBLE_QUOTE; |
669 | 0 | for (i = 0; i < srclen; i++) { |
670 | 0 | char c = src[i]; |
671 | 0 | if (NEEDS_HEX_ESCAPE(c)) { |
672 | 0 | *dst++ = C_BACKSLASH; |
673 | 0 | *dst++ = hexChars[(c >> 4) & 0x0f]; |
674 | 0 | *dst++ = hexChars[c & 0x0f]; |
675 | 0 | } else { |
676 | 0 | if (NEEDS_ESCAPE(c) || (SPECIAL_CHAR(c) && mode == fullEscape)) { |
677 | 0 | *dst++ = C_BACKSLASH; |
678 | 0 | } |
679 | 0 | *dst++ = c; |
680 | 0 | } |
681 | 0 | } |
682 | 0 | if (mode == minimalEscapeAndQuote) |
683 | 0 | *dst++ = C_DOUBLE_QUOTE; |
684 | 0 | *dst++ = 0; |
685 | 0 | if (pEQMode) |
686 | 0 | *pEQMode = mode; |
687 | 0 | return SECSuccess; |
688 | 0 | } |
689 | | |
690 | | SECStatus |
691 | | CERT_RFC1485_EscapeAndQuote(char* dst, int dstlen, char* src, int srclen) |
692 | 0 | { |
693 | 0 | EQMode mode = minimalEscapeAndQuote; |
694 | 0 | return escapeAndQuote(dst, dstlen, src, srclen, &mode); |
695 | 0 | } |
696 | | |
697 | | /* convert an OID to dotted-decimal representation */ |
698 | | /* Returns a string that must be freed with PR_smprintf_free(), */ |
699 | | char* |
700 | | CERT_GetOidString(const SECItem* oid) |
701 | 0 | { |
702 | 0 | PRUint8* stop; /* points to first byte after OID string */ |
703 | 0 | PRUint8* first; /* byte of an OID component integer */ |
704 | 0 | PRUint8* last; /* byte of an OID component integer */ |
705 | 0 | char* rvString = NULL; |
706 | 0 | char* prefix = NULL; |
707 | |
|
708 | 0 | #define MAX_OID_LEN 1024 /* bytes */ |
709 | |
|
710 | 0 | if (oid->len > MAX_OID_LEN) { |
711 | 0 | PORT_SetError(SEC_ERROR_INPUT_LEN); |
712 | 0 | return NULL; |
713 | 0 | } |
714 | | |
715 | | /* If the OID has length 1, we bail. */ |
716 | 0 | if (oid->len < 2) { |
717 | 0 | return NULL; |
718 | 0 | } |
719 | | |
720 | | /* first will point to the next sequence of bytes to decode */ |
721 | 0 | first = (PRUint8*)oid->data; |
722 | | /* stop points to one past the legitimate data */ |
723 | 0 | stop = &first[oid->len]; |
724 | | |
725 | | /* |
726 | | * Check for our pseudo-encoded single-digit OIDs |
727 | | */ |
728 | 0 | if ((*first == 0x80) && (2 == oid->len)) { |
729 | | /* Funky encoding. The second byte is the number */ |
730 | 0 | rvString = PR_smprintf("%lu", (PRUint32)first[1]); |
731 | 0 | if (!rvString) { |
732 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
733 | 0 | } |
734 | 0 | return rvString; |
735 | 0 | } |
736 | | |
737 | 0 | for (; first < stop; first = last + 1) { |
738 | 0 | unsigned int bytesBeforeLast; |
739 | |
|
740 | 0 | for (last = first; last < stop; last++) { |
741 | 0 | if (0 == (*last & 0x80)) { |
742 | 0 | break; |
743 | 0 | } |
744 | 0 | } |
745 | | /* There's no first bit set, so this isn't valid. Bail.*/ |
746 | 0 | if (last == stop) { |
747 | 0 | goto unsupported; |
748 | 0 | } |
749 | 0 | bytesBeforeLast = (unsigned int)(last - first); |
750 | 0 | if (bytesBeforeLast <= 3U) { /* 0-28 bit number */ |
751 | 0 | PRUint32 n = 0; |
752 | 0 | PRUint32 c; |
753 | |
|
754 | 0 | #define CGET(i, m) \ |
755 | 0 | c = last[-i] & m; \ |
756 | 0 | n |= c << (7 * i) |
757 | |
|
758 | 0 | #define CASE(i, m) \ |
759 | 0 | case i: \ |
760 | 0 | CGET(i, m); \ |
761 | 0 | if (!n) \ |
762 | 0 | goto unsupported /* fall-through */ |
763 | |
|
764 | 0 | switch (bytesBeforeLast) { |
765 | 0 | CASE(3, 0x7f); |
766 | 0 | CASE(2, 0x7f); |
767 | 0 | CASE(1, 0x7f); |
768 | 0 | case 0: |
769 | 0 | n |= last[0] & 0x7f; |
770 | 0 | break; |
771 | 0 | } |
772 | 0 | if (last[0] & 0x80) { |
773 | 0 | goto unsupported; |
774 | 0 | } |
775 | | |
776 | 0 | if (!rvString) { |
777 | | /* This is the first number.. decompose it */ |
778 | 0 | PRUint32 one = PR_MIN(n / 40, 2); /* never > 2 */ |
779 | 0 | PRUint32 two = n - (one * 40); |
780 | |
|
781 | 0 | rvString = PR_smprintf("OID.%lu.%lu", one, two); |
782 | 0 | } else { |
783 | 0 | prefix = rvString; |
784 | 0 | rvString = PR_smprintf("%s.%lu", prefix, n); |
785 | 0 | } |
786 | 0 | } else if (bytesBeforeLast <= 9U) { /* 29-64 bit number */ |
787 | 0 | PRUint64 n = 0; |
788 | 0 | PRUint64 c; |
789 | |
|
790 | 0 | switch (bytesBeforeLast) { |
791 | 0 | CASE(9, 0x01); |
792 | 0 | CASE(8, 0x7f); |
793 | 0 | CASE(7, 0x7f); |
794 | 0 | CASE(6, 0x7f); |
795 | 0 | CASE(5, 0x7f); |
796 | 0 | CASE(4, 0x7f); |
797 | 0 | CGET(3, 0x7f); |
798 | 0 | CGET(2, 0x7f); |
799 | 0 | CGET(1, 0x7f); |
800 | 0 | CGET(0, 0x7f); |
801 | 0 | break; |
802 | 0 | } |
803 | 0 | if (last[0] & 0x80) |
804 | 0 | goto unsupported; |
805 | | |
806 | 0 | if (!rvString) { |
807 | | /* This is the first number.. decompose it */ |
808 | 0 | PRUint64 one = PR_MIN(n / 40, 2); /* never > 2 */ |
809 | 0 | PRUint64 two = n - (one * 40); |
810 | |
|
811 | 0 | rvString = PR_smprintf("OID.%llu.%llu", one, two); |
812 | 0 | } else { |
813 | 0 | prefix = rvString; |
814 | 0 | rvString = PR_smprintf("%s.%llu", prefix, n); |
815 | 0 | } |
816 | 0 | } else { |
817 | | /* More than a 64-bit number, or not minimal encoding. */ |
818 | 0 | unsupported: |
819 | 0 | if (!rvString) |
820 | 0 | rvString = PR_smprintf("OID.UNSUPPORTED"); |
821 | 0 | else { |
822 | 0 | prefix = rvString; |
823 | 0 | rvString = PR_smprintf("%s.UNSUPPORTED", prefix); |
824 | 0 | } |
825 | 0 | } |
826 | | |
827 | 0 | if (prefix) { |
828 | 0 | PR_smprintf_free(prefix); |
829 | 0 | prefix = NULL; |
830 | 0 | } |
831 | 0 | if (!rvString) { |
832 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
833 | 0 | break; |
834 | 0 | } |
835 | 0 | } |
836 | 0 | return rvString; |
837 | 0 | } |
838 | | |
839 | | /* convert DER-encoded hex to a string */ |
840 | | static SECItem* |
841 | | get_hex_string(SECItem* data) |
842 | 0 | { |
843 | 0 | SECItem* rv; |
844 | 0 | unsigned int i, j; |
845 | 0 | static const char hex[] = { "0123456789ABCDEF" }; |
846 | | |
847 | | /* '#' + 2 chars per octet + terminator */ |
848 | 0 | rv = SECITEM_AllocItem(NULL, NULL, data->len * 2 + 2); |
849 | 0 | if (!rv) { |
850 | 0 | return NULL; |
851 | 0 | } |
852 | 0 | rv->data[0] = '#'; |
853 | 0 | rv->len = 1 + 2 * data->len; |
854 | 0 | for (i = 0; i < data->len; i++) { |
855 | 0 | j = data->data[i]; |
856 | 0 | rv->data[2 * i + 1] = hex[j >> 4]; |
857 | 0 | rv->data[2 * i + 2] = hex[j & 15]; |
858 | 0 | } |
859 | 0 | rv->data[rv->len] = 0; |
860 | 0 | return rv; |
861 | 0 | } |
862 | | |
863 | | /* For compliance with RFC 2253, RFC 3280 and RFC 4630, we choose to |
864 | | * use the NAME=STRING form, rather than the OID.N.N=#hexXXXX form, |
865 | | * when both of these conditions are met: |
866 | | * 1) The attribute name OID (kind) has a known name string that is |
867 | | * defined in one of those RFCs, or in RFCs that they cite, AND |
868 | | * 2) The attribute's value encoding is RFC compliant for the kind |
869 | | * (e.g., the value's encoding tag is correct for the kind, and |
870 | | * the value's length is in the range allowed for the kind, and |
871 | | * the value's contents are appropriate for the encoding tag). |
872 | | * Otherwise, we use the OID.N.N=#hexXXXX form. |
873 | | * |
874 | | * If the caller prefers maximum human readability to RFC compliance, |
875 | | * then |
876 | | * - We print the kind in NAME= string form if we know the name |
877 | | * string for the attribute type OID, regardless of whether the |
878 | | * value is correctly encoded or not. else we use the OID.N.N= form. |
879 | | * - We use the non-hex STRING form for the attribute value if the |
880 | | * value can be represented in such a form. Otherwise, we use |
881 | | * the hex string form. |
882 | | * This implies that, for maximum human readability, in addition to |
883 | | * the two forms allowed by the RFC, we allow two other forms of output: |
884 | | * - the OID.N.N=STRING form, and |
885 | | * - the NAME=#hexXXXX form |
886 | | * When the caller prefers maximum human readability, we do not allow |
887 | | * the value of any attribute to exceed the length allowed by the RFC. |
888 | | * If the attribute value exceeds the allowed length, we truncate it to |
889 | | * the allowed length and append "...". |
890 | | * Also in this case, we arbitrarily impose a limit on the length of the |
891 | | * entire AVA encoding, regardless of the form, of 384 bytes per AVA. |
892 | | * This limit includes the trailing NULL character. If the encoded |
893 | | * AVA length exceeds that limit, this function reports failure to encode |
894 | | * the AVA. |
895 | | * |
896 | | * An ASCII representation of an AVA is said to be "invertible" if |
897 | | * conversion back to DER reproduces the original DER encoding exactly. |
898 | | * The RFC 2253 rules do not ensure that all ASCII AVAs derived according |
899 | | * to its rules are invertible. That is because the RFCs allow some |
900 | | * attribute values to be encoded in any of a number of encodings, |
901 | | * and the encoding type information is lost in the non-hex STRING form. |
902 | | * This is particularly true of attributes of type DirectoryString. |
903 | | * The encoding type information is always preserved in the hex string |
904 | | * form, because the hex includes the entire DER encoding of the value. |
905 | | * |
906 | | * So, when the caller perfers maximum invertibility, we apply the |
907 | | * RFC compliance rules stated above, and add a third required |
908 | | * condition on the use of the NAME=STRING form. |
909 | | * 3) The attribute's kind is not is allowed to be encoded in any of |
910 | | * several different encodings, such as DirectoryStrings. |
911 | | * |
912 | | * The chief difference between CERT_N2A_STRICT and CERT_N2A_INVERTIBLE |
913 | | * is that the latter forces DirectoryStrings to be hex encoded. |
914 | | * |
915 | | * As a simplification, we assume the value is correctly encoded for |
916 | | * its encoding type. That is, we do not test that all the characters |
917 | | * in a string encoded type are allowed by that type. We assume it. |
918 | | */ |
919 | | static SECStatus |
920 | | AppendAVA(stringBuf* bufp, CERTAVA* ava, CertStrictnessLevel strict) |
921 | 0 | { |
922 | 0 | #define TMPBUF_LEN 2048 |
923 | 0 | const NameToKind* pn2k = name2kinds; |
924 | 0 | SECItem* avaValue = NULL; |
925 | 0 | char* unknownTag = NULL; |
926 | 0 | char* encodedAVA = NULL; |
927 | 0 | PRBool useHex = PR_FALSE; /* use =#hexXXXX form */ |
928 | 0 | PRBool truncateName = PR_FALSE; |
929 | 0 | PRBool truncateValue = PR_FALSE; |
930 | 0 | SECOidTag endKind; |
931 | 0 | SECStatus rv; |
932 | 0 | unsigned int len; |
933 | 0 | unsigned int nameLen, valueLen; |
934 | 0 | unsigned int maxName, maxValue; |
935 | 0 | EQMode mode = minimalEscapeAndQuote; |
936 | 0 | NameToKind n2k = { NULL, 32767, SEC_OID_UNKNOWN, SEC_ASN1_DS }; |
937 | 0 | char tmpBuf[TMPBUF_LEN]; |
938 | |
|
939 | 0 | #define tagName n2k.name /* non-NULL means use NAME= form */ |
940 | 0 | #define maxBytes n2k.maxLen |
941 | 0 | #define tag n2k.kind |
942 | 0 | #define vt n2k.valueType |
943 | | |
944 | | /* READABLE mode recognizes more names from the name2kinds table |
945 | | * than do STRICT or INVERTIBLE modes. This assignment chooses the |
946 | | * point in the table where the attribute type name scanning stops. |
947 | | */ |
948 | 0 | endKind = (strict == CERT_N2A_READABLE) ? SEC_OID_UNKNOWN |
949 | 0 | : SEC_OID_AVA_POSTAL_ADDRESS; |
950 | 0 | tag = CERT_GetAVATag(ava); |
951 | 0 | while (pn2k->kind != tag && pn2k->kind != endKind) { |
952 | 0 | ++pn2k; |
953 | 0 | } |
954 | |
|
955 | 0 | if (pn2k->kind != endKind) { |
956 | 0 | n2k = *pn2k; |
957 | 0 | } else if (strict != CERT_N2A_READABLE) { |
958 | 0 | useHex = PR_TRUE; |
959 | 0 | } |
960 | | /* For invertable form, force Directory Strings to use hex form. */ |
961 | 0 | if (strict == CERT_N2A_INVERTIBLE && vt == SEC_ASN1_DS) { |
962 | 0 | tagName = NULL; /* must use OID.N form */ |
963 | 0 | useHex = PR_TRUE; /* must use hex string */ |
964 | 0 | } |
965 | 0 | if (!useHex) { |
966 | 0 | avaValue = CERT_DecodeAVAValue(&ava->value); |
967 | 0 | if (!avaValue) { |
968 | 0 | useHex = PR_TRUE; |
969 | 0 | if (strict != CERT_N2A_READABLE) { |
970 | 0 | tagName = NULL; /* must use OID.N form */ |
971 | 0 | } |
972 | 0 | } |
973 | 0 | } |
974 | 0 | if (!tagName) { |
975 | | /* handle unknown attribute types per RFC 2253 */ |
976 | 0 | tagName = unknownTag = CERT_GetOidString(&ava->type); |
977 | 0 | if (!tagName) { |
978 | 0 | if (avaValue) |
979 | 0 | SECITEM_FreeItem(avaValue, PR_TRUE); |
980 | 0 | return SECFailure; |
981 | 0 | } |
982 | 0 | } |
983 | 0 | if (useHex) { |
984 | 0 | avaValue = get_hex_string(&ava->value); |
985 | 0 | if (!avaValue) { |
986 | 0 | if (unknownTag) |
987 | 0 | PR_smprintf_free(unknownTag); |
988 | 0 | return SECFailure; |
989 | 0 | } |
990 | 0 | } |
991 | | |
992 | 0 | nameLen = strlen(tagName); |
993 | |
|
994 | 0 | if (useHex) { |
995 | 0 | valueLen = avaValue->len; |
996 | 0 | } else { |
997 | 0 | int reqLen = cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, &mode); |
998 | 0 | if (reqLen < 0) { |
999 | 0 | SECITEM_FreeItem(avaValue, PR_TRUE); |
1000 | 0 | return SECFailure; |
1001 | 0 | } |
1002 | 0 | valueLen = reqLen; |
1003 | 0 | } |
1004 | 0 | if (UINT_MAX - nameLen < 2 || |
1005 | 0 | valueLen > UINT_MAX - nameLen - 2) { |
1006 | 0 | SECITEM_FreeItem(avaValue, PR_TRUE); |
1007 | 0 | return SECFailure; |
1008 | 0 | } |
1009 | 0 | len = nameLen + valueLen + 2; /* Add 2 for '=' and trailing NUL */ |
1010 | |
|
1011 | 0 | maxName = nameLen; |
1012 | 0 | maxValue = valueLen; |
1013 | 0 | if (len <= sizeof(tmpBuf)) { |
1014 | 0 | encodedAVA = tmpBuf; |
1015 | 0 | } else if (strict != CERT_N2A_READABLE) { |
1016 | 0 | encodedAVA = PORT_Alloc(len); |
1017 | 0 | if (!encodedAVA) { |
1018 | 0 | SECITEM_FreeItem(avaValue, PR_TRUE); |
1019 | 0 | if (unknownTag) |
1020 | 0 | PR_smprintf_free(unknownTag); |
1021 | 0 | return SECFailure; |
1022 | 0 | } |
1023 | 0 | } else { |
1024 | | /* Must make output fit in tmpbuf */ |
1025 | 0 | unsigned int fair = (sizeof tmpBuf) / 2 - 1; /* for = and \0 */ |
1026 | |
|
1027 | 0 | if (nameLen < fair) { |
1028 | | /* just truncate the value */ |
1029 | 0 | maxValue = (sizeof tmpBuf) - (nameLen + 6); /* for "=...\0", |
1030 | | and possibly '"' */ |
1031 | 0 | } else if (valueLen < fair) { |
1032 | | /* just truncate the name */ |
1033 | 0 | maxName = (sizeof tmpBuf) - (valueLen + 5); /* for "=...\0" */ |
1034 | 0 | } else { |
1035 | | /* truncate both */ |
1036 | 0 | maxName = maxValue = fair - 3; /* for "..." */ |
1037 | 0 | } |
1038 | 0 | if (nameLen > maxName) { |
1039 | 0 | PORT_Assert(unknownTag && unknownTag == tagName); |
1040 | 0 | truncateName = PR_TRUE; |
1041 | 0 | nameLen = maxName; |
1042 | 0 | } |
1043 | 0 | encodedAVA = tmpBuf; |
1044 | 0 | } |
1045 | | |
1046 | 0 | memcpy(encodedAVA, tagName, nameLen); |
1047 | 0 | if (truncateName) { |
1048 | | /* If tag name is too long, we know it is an OID form that was |
1049 | | * allocated from the heap, so we can modify it in place |
1050 | | */ |
1051 | 0 | encodedAVA[nameLen - 1] = '.'; |
1052 | 0 | encodedAVA[nameLen - 2] = '.'; |
1053 | 0 | encodedAVA[nameLen - 3] = '.'; |
1054 | 0 | } |
1055 | 0 | encodedAVA[nameLen++] = '='; |
1056 | 0 | if (unknownTag) |
1057 | 0 | PR_smprintf_free(unknownTag); |
1058 | |
|
1059 | 0 | if (strict == CERT_N2A_READABLE && maxValue > maxBytes) |
1060 | 0 | maxValue = maxBytes; |
1061 | 0 | if (valueLen > maxValue) { |
1062 | 0 | valueLen = maxValue; |
1063 | 0 | truncateValue = PR_TRUE; |
1064 | 0 | } |
1065 | | /* escape and quote as necessary - don't quote hex strings */ |
1066 | 0 | if (useHex) { |
1067 | 0 | char* end = encodedAVA + nameLen + valueLen; |
1068 | 0 | memcpy(encodedAVA + nameLen, (char*)avaValue->data, valueLen); |
1069 | 0 | end[0] = '\0'; |
1070 | 0 | if (truncateValue) { |
1071 | 0 | end[-1] = '.'; |
1072 | 0 | end[-2] = '.'; |
1073 | 0 | end[-3] = '.'; |
1074 | 0 | } |
1075 | 0 | rv = SECSuccess; |
1076 | 0 | } else if (!truncateValue) { |
1077 | 0 | rv = escapeAndQuote(encodedAVA + nameLen, len - nameLen, |
1078 | 0 | (char*)avaValue->data, avaValue->len, &mode); |
1079 | 0 | } else { |
1080 | | /* must truncate the escaped and quoted value */ |
1081 | 0 | char bigTmpBuf[TMPBUF_LEN * 3 + 3]; |
1082 | 0 | PORT_Assert(valueLen < sizeof tmpBuf); |
1083 | 0 | rv = escapeAndQuote(bigTmpBuf, sizeof bigTmpBuf, (char*)avaValue->data, |
1084 | 0 | PR_MIN(avaValue->len, valueLen), &mode); |
1085 | |
|
1086 | 0 | bigTmpBuf[valueLen--] = '\0'; /* hard stop here */ |
1087 | | /* See if we're in the middle of a multi-byte UTF8 character */ |
1088 | 0 | while (((bigTmpBuf[valueLen] & 0xc0) == 0x80) && valueLen > 0) { |
1089 | 0 | bigTmpBuf[valueLen--] = '\0'; |
1090 | 0 | } |
1091 | | /* add ellipsis to signify truncation. */ |
1092 | 0 | bigTmpBuf[++valueLen] = '.'; |
1093 | 0 | bigTmpBuf[++valueLen] = '.'; |
1094 | 0 | bigTmpBuf[++valueLen] = '.'; |
1095 | 0 | if (bigTmpBuf[0] == '"') |
1096 | 0 | bigTmpBuf[++valueLen] = '"'; |
1097 | 0 | bigTmpBuf[++valueLen] = '\0'; |
1098 | 0 | PORT_Assert(nameLen + valueLen <= (sizeof tmpBuf) - 1); |
1099 | 0 | memcpy(encodedAVA + nameLen, bigTmpBuf, valueLen + 1); |
1100 | 0 | } |
1101 | |
|
1102 | 0 | SECITEM_FreeItem(avaValue, PR_TRUE); |
1103 | 0 | if (rv == SECSuccess) |
1104 | 0 | rv = AppendStr(bufp, encodedAVA); |
1105 | 0 | if (encodedAVA != tmpBuf) |
1106 | 0 | PORT_Free(encodedAVA); |
1107 | 0 | return rv; |
1108 | 0 | } |
1109 | | |
1110 | | #undef tagName |
1111 | | #undef maxBytes |
1112 | | #undef tag |
1113 | | #undef vt |
1114 | | |
1115 | | char* |
1116 | | CERT_NameToAsciiInvertible(CERTName* name, CertStrictnessLevel strict) |
1117 | 0 | { |
1118 | 0 | CERTRDN** rdns; |
1119 | 0 | CERTRDN** lastRdn; |
1120 | 0 | CERTRDN** rdn; |
1121 | 0 | PRBool first = PR_TRUE; |
1122 | 0 | stringBuf strBuf = { NULL, 0, 0 }; |
1123 | |
|
1124 | 0 | rdns = name->rdns; |
1125 | 0 | if (rdns == NULL) { |
1126 | 0 | return NULL; |
1127 | 0 | } |
1128 | | |
1129 | | /* find last RDN */ |
1130 | 0 | lastRdn = rdns; |
1131 | 0 | while (*lastRdn) |
1132 | 0 | lastRdn++; |
1133 | 0 | lastRdn--; |
1134 | | |
1135 | | /* |
1136 | | * Loop over name contents in _reverse_ RDN order appending to string |
1137 | | */ |
1138 | 0 | for (rdn = lastRdn; rdn >= rdns; rdn--) { |
1139 | 0 | CERTAVA** avas = (*rdn)->avas; |
1140 | 0 | CERTAVA* ava; |
1141 | 0 | PRBool newRDN = PR_TRUE; |
1142 | | |
1143 | | /* |
1144 | | * XXX Do we need to traverse the AVAs in reverse order, too? |
1145 | | */ |
1146 | 0 | while (avas && (ava = *avas++) != NULL) { |
1147 | 0 | SECStatus rv; |
1148 | | /* Put in comma or plus separator */ |
1149 | 0 | if (!first) { |
1150 | | /* Use of spaces is deprecated in RFC 2253. */ |
1151 | 0 | rv = AppendStr(&strBuf, newRDN ? "," : "+"); |
1152 | 0 | if (rv) |
1153 | 0 | goto loser; |
1154 | 0 | } else { |
1155 | 0 | first = PR_FALSE; |
1156 | 0 | } |
1157 | | |
1158 | | /* Add in tag type plus value into strBuf */ |
1159 | 0 | rv = AppendAVA(&strBuf, ava, strict); |
1160 | 0 | if (rv) |
1161 | 0 | goto loser; |
1162 | 0 | newRDN = PR_FALSE; |
1163 | 0 | } |
1164 | 0 | } |
1165 | 0 | return strBuf.buffer; |
1166 | 0 | loser: |
1167 | 0 | if (strBuf.buffer) { |
1168 | 0 | PORT_Free(strBuf.buffer); |
1169 | 0 | } |
1170 | 0 | return NULL; |
1171 | 0 | } |
1172 | | |
1173 | | char* |
1174 | | CERT_NameToAscii(CERTName* name) |
1175 | 0 | { |
1176 | 0 | return CERT_NameToAsciiInvertible(name, CERT_N2A_READABLE); |
1177 | 0 | } |
1178 | | |
1179 | | /* |
1180 | | * Return the string representation of a DER encoded distinguished name |
1181 | | * "dername" - The DER encoded name to convert |
1182 | | */ |
1183 | | char* |
1184 | | CERT_DerNameToAscii(SECItem* dername) |
1185 | 0 | { |
1186 | 0 | int rv; |
1187 | 0 | PLArenaPool* arena = NULL; |
1188 | 0 | CERTName name; |
1189 | 0 | char* retstr = NULL; |
1190 | |
|
1191 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
1192 | |
|
1193 | 0 | if (arena == NULL) { |
1194 | 0 | goto loser; |
1195 | 0 | } |
1196 | | |
1197 | 0 | rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername); |
1198 | |
|
1199 | 0 | if (rv != SECSuccess) { |
1200 | 0 | goto loser; |
1201 | 0 | } |
1202 | | |
1203 | 0 | retstr = CERT_NameToAscii(&name); |
1204 | |
|
1205 | 0 | loser: |
1206 | 0 | if (arena != NULL) { |
1207 | 0 | PORT_FreeArena(arena, PR_FALSE); |
1208 | 0 | } |
1209 | |
|
1210 | 0 | return (retstr); |
1211 | 0 | } |
1212 | | |
1213 | | static char* |
1214 | | avaToString(PLArenaPool* arena, CERTAVA* ava) |
1215 | 0 | { |
1216 | 0 | char* buf = NULL; |
1217 | 0 | SECItem* avaValue; |
1218 | 0 | int valueLen; |
1219 | |
|
1220 | 0 | avaValue = CERT_DecodeAVAValue(&ava->value); |
1221 | 0 | if (!avaValue) { |
1222 | 0 | return buf; |
1223 | 0 | } |
1224 | 0 | int reqLen = cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, NULL); |
1225 | | /* reqLen is max 16384*3 + 2 */ |
1226 | 0 | if (reqLen >= 0) { |
1227 | 0 | valueLen = reqLen + 1; |
1228 | 0 | if (arena) { |
1229 | 0 | buf = (char*)PORT_ArenaZAlloc(arena, valueLen); |
1230 | 0 | } else { |
1231 | 0 | buf = (char*)PORT_ZAlloc(valueLen); |
1232 | 0 | } |
1233 | 0 | if (buf) { |
1234 | 0 | SECStatus rv = |
1235 | 0 | escapeAndQuote(buf, valueLen, (char*)avaValue->data, avaValue->len, NULL); |
1236 | 0 | if (rv != SECSuccess) { |
1237 | 0 | if (!arena) |
1238 | 0 | PORT_Free(buf); |
1239 | 0 | buf = NULL; |
1240 | 0 | } |
1241 | 0 | } |
1242 | 0 | } |
1243 | 0 | SECITEM_FreeItem(avaValue, PR_TRUE); |
1244 | 0 | return buf; |
1245 | 0 | } |
1246 | | |
1247 | | /* RDNs are sorted from most general to most specific. |
1248 | | * This code returns the FIRST one found, the most general one found. |
1249 | | */ |
1250 | | static char* |
1251 | | CERT_GetNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag) |
1252 | 0 | { |
1253 | 0 | CERTRDN** rdns = name->rdns; |
1254 | 0 | CERTRDN* rdn; |
1255 | 0 | CERTAVA* ava = NULL; |
1256 | |
|
1257 | 0 | while (rdns && (rdn = *rdns++) != 0) { |
1258 | 0 | CERTAVA** avas = rdn->avas; |
1259 | 0 | while (avas && (ava = *avas++) != 0) { |
1260 | 0 | int tag = CERT_GetAVATag(ava); |
1261 | 0 | if (tag == wantedTag) { |
1262 | 0 | avas = NULL; |
1263 | 0 | rdns = NULL; /* break out of all loops */ |
1264 | 0 | } |
1265 | 0 | } |
1266 | 0 | } |
1267 | 0 | return ava ? avaToString(arena, ava) : NULL; |
1268 | 0 | } |
1269 | | |
1270 | | /* RDNs are sorted from most general to most specific. |
1271 | | * This code returns the LAST one found, the most specific one found. |
1272 | | * This is particularly appropriate for Common Name. See RFC 2818. |
1273 | | */ |
1274 | | static char* |
1275 | | CERT_GetLastNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag) |
1276 | 0 | { |
1277 | 0 | CERTRDN** rdns = name->rdns; |
1278 | 0 | CERTRDN* rdn; |
1279 | 0 | CERTAVA* lastAva = NULL; |
1280 | |
|
1281 | 0 | while (rdns && (rdn = *rdns++) != 0) { |
1282 | 0 | CERTAVA** avas = rdn->avas; |
1283 | 0 | CERTAVA* ava; |
1284 | 0 | while (avas && (ava = *avas++) != 0) { |
1285 | 0 | int tag = CERT_GetAVATag(ava); |
1286 | 0 | if (tag == wantedTag) { |
1287 | 0 | lastAva = ava; |
1288 | 0 | } |
1289 | 0 | } |
1290 | 0 | } |
1291 | 0 | return lastAva ? avaToString(arena, lastAva) : NULL; |
1292 | 0 | } |
1293 | | |
1294 | | char* |
1295 | | CERT_GetCertificateEmailAddress(CERTCertificate* cert) |
1296 | 0 | { |
1297 | 0 | char* rawEmailAddr = NULL; |
1298 | 0 | SECItem subAltName; |
1299 | 0 | SECStatus rv; |
1300 | 0 | CERTGeneralName* nameList = NULL; |
1301 | 0 | CERTGeneralName* current; |
1302 | 0 | PLArenaPool* arena = NULL; |
1303 | 0 | int i; |
1304 | |
|
1305 | 0 | subAltName.data = NULL; |
1306 | |
|
1307 | 0 | rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), |
1308 | 0 | SEC_OID_PKCS9_EMAIL_ADDRESS); |
1309 | 0 | if (rawEmailAddr == NULL) { |
1310 | 0 | rawEmailAddr = |
1311 | 0 | CERT_GetNameElement(cert->arena, &(cert->subject), SEC_OID_RFC1274_MAIL); |
1312 | 0 | } |
1313 | 0 | if (rawEmailAddr == NULL) { |
1314 | |
|
1315 | 0 | rv = |
1316 | 0 | CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName); |
1317 | 0 | if (rv != SECSuccess) { |
1318 | 0 | goto finish; |
1319 | 0 | } |
1320 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
1321 | 0 | if (!arena) { |
1322 | 0 | goto finish; |
1323 | 0 | } |
1324 | 0 | nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); |
1325 | 0 | if (!nameList) { |
1326 | 0 | goto finish; |
1327 | 0 | } |
1328 | 0 | if (nameList != NULL) { |
1329 | 0 | do { |
1330 | 0 | if (current->type == certDirectoryName) { |
1331 | 0 | rawEmailAddr = |
1332 | 0 | CERT_GetNameElement(cert->arena, &(current->name.directoryName), |
1333 | 0 | SEC_OID_PKCS9_EMAIL_ADDRESS); |
1334 | 0 | if (rawEmailAddr == |
1335 | 0 | NULL) { |
1336 | 0 | rawEmailAddr = |
1337 | 0 | CERT_GetNameElement(cert->arena, &(current->name.directoryName), |
1338 | 0 | SEC_OID_RFC1274_MAIL); |
1339 | 0 | } |
1340 | 0 | } else if (current->type == certRFC822Name) { |
1341 | 0 | rawEmailAddr = |
1342 | 0 | (char*)PORT_ArenaZAlloc(cert->arena, current->name.other.len + 1); |
1343 | 0 | if (!rawEmailAddr) { |
1344 | 0 | goto finish; |
1345 | 0 | } |
1346 | 0 | PORT_Memcpy(rawEmailAddr, current->name.other.data, |
1347 | 0 | current->name.other.len); |
1348 | 0 | rawEmailAddr[current->name.other.len] = |
1349 | 0 | '\0'; |
1350 | 0 | } |
1351 | 0 | if (rawEmailAddr) { |
1352 | 0 | break; |
1353 | 0 | } |
1354 | 0 | current = CERT_GetNextGeneralName(current); |
1355 | 0 | } while (current != nameList); |
1356 | 0 | } |
1357 | 0 | } |
1358 | 0 | if (rawEmailAddr) { |
1359 | 0 | for (i = 0; i <= (int)PORT_Strlen(rawEmailAddr); i++) { |
1360 | 0 | rawEmailAddr[i] = tolower((unsigned char)rawEmailAddr[i]); |
1361 | 0 | } |
1362 | 0 | } |
1363 | |
|
1364 | 0 | finish: |
1365 | | |
1366 | | /* Don't free nameList, it's part of the arena. */ |
1367 | |
|
1368 | 0 | if (arena) { |
1369 | 0 | PORT_FreeArena(arena, PR_FALSE); |
1370 | 0 | } |
1371 | |
|
1372 | 0 | if (subAltName.data) { |
1373 | 0 | SECITEM_FreeItem(&subAltName, PR_FALSE); |
1374 | 0 | } |
1375 | |
|
1376 | 0 | return (rawEmailAddr); |
1377 | 0 | } |
1378 | | |
1379 | | static char* |
1380 | | appendStringToBuf(char* dest, char* src, PRUint32* pRemaining) |
1381 | 0 | { |
1382 | 0 | PRUint32 len; |
1383 | 0 | if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) { |
1384 | 0 | PRUint32 i; |
1385 | 0 | for (i = 0; i < len; ++i) |
1386 | 0 | dest[i] = tolower((unsigned char)src[i]); |
1387 | 0 | dest[len] = 0; |
1388 | 0 | dest += len + 1; |
1389 | 0 | *pRemaining -= len + 1; |
1390 | 0 | } |
1391 | 0 | return dest; |
1392 | 0 | } |
1393 | | |
1394 | | #undef NEEDS_HEX_ESCAPE |
1395 | 0 | #define NEEDS_HEX_ESCAPE(c) (c < 0x20) |
1396 | | |
1397 | | static char* |
1398 | | appendItemToBuf(char* dest, SECItem* src, PRUint32* pRemaining) |
1399 | 0 | { |
1400 | 0 | if (dest && src && src->data && src->len && src->data[0]) { |
1401 | 0 | PRUint32 len = src->len; |
1402 | 0 | PRUint32 i; |
1403 | 0 | PRUint32 reqLen = len + 1; |
1404 | | /* are there any embedded control characters ? */ |
1405 | 0 | for (i = 0; i < len; i++) { |
1406 | 0 | if (NEEDS_HEX_ESCAPE(src->data[i])) |
1407 | 0 | reqLen += 2; |
1408 | 0 | } |
1409 | 0 | if (*pRemaining > reqLen) { |
1410 | 0 | for (i = 0; i < len; ++i) { |
1411 | 0 | PRUint8 c = src->data[i]; |
1412 | 0 | if (NEEDS_HEX_ESCAPE(c)) { |
1413 | 0 | *dest++ = |
1414 | 0 | C_BACKSLASH; |
1415 | 0 | *dest++ = |
1416 | 0 | hexChars[(c >> 4) & 0x0f]; |
1417 | 0 | *dest++ = |
1418 | 0 | hexChars[c & 0x0f]; |
1419 | 0 | } else { |
1420 | 0 | *dest++ = |
1421 | 0 | tolower(c); |
1422 | 0 | } |
1423 | 0 | } |
1424 | 0 | *dest++ = '\0'; |
1425 | 0 | *pRemaining -= reqLen; |
1426 | 0 | } |
1427 | 0 | } |
1428 | 0 | return dest; |
1429 | 0 | } |
1430 | | |
1431 | | /* Returns a pointer to an environment-like string, a series of |
1432 | | ** null-terminated strings, terminated by a zero-length string. |
1433 | | ** This function is intended to be internal to NSS. |
1434 | | */ |
1435 | | char* |
1436 | | cert_GetCertificateEmailAddresses(CERTCertificate* cert) |
1437 | 0 | { |
1438 | 0 | char* rawEmailAddr = NULL; |
1439 | 0 | char* addrBuf = NULL; |
1440 | 0 | char* pBuf = NULL; |
1441 | 0 | PORTCheapArenaPool tmpArena; |
1442 | 0 | PRUint32 maxLen = 0; |
1443 | 0 | PRInt32 finalLen = 0; |
1444 | 0 | SECStatus rv; |
1445 | 0 | SECItem subAltName; |
1446 | |
|
1447 | 0 | PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); |
1448 | |
|
1449 | 0 | subAltName.data = NULL; |
1450 | 0 | maxLen = cert->derCert.len; |
1451 | 0 | PORT_Assert(maxLen); |
1452 | 0 | if (!maxLen) |
1453 | 0 | maxLen = 2000; /* a guess, should never happen */ |
1454 | |
|
1455 | 0 | pBuf = addrBuf = (char*)PORT_ArenaZAlloc(&tmpArena.arena, maxLen + 1); |
1456 | 0 | if (!addrBuf) |
1457 | 0 | goto loser; |
1458 | | |
1459 | 0 | rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject, |
1460 | 0 | SEC_OID_PKCS9_EMAIL_ADDRESS); |
1461 | 0 | pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); |
1462 | |
|
1463 | 0 | rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject, |
1464 | 0 | SEC_OID_RFC1274_MAIL); |
1465 | 0 | pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen); |
1466 | |
|
1467 | 0 | rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName); |
1468 | 0 | if (rv == SECSuccess && subAltName.data) { |
1469 | 0 | CERTGeneralName* nameList = NULL; |
1470 | |
|
1471 | 0 | if (!!(nameList = CERT_DecodeAltNameExtension(&tmpArena.arena, &subAltName))) { |
1472 | 0 | CERTGeneralName* current = nameList; |
1473 | 0 | do { |
1474 | 0 | if (current->type == certDirectoryName) { |
1475 | 0 | rawEmailAddr = |
1476 | 0 | CERT_GetNameElement(&tmpArena.arena, |
1477 | 0 | ¤t->name.directoryName, |
1478 | 0 | SEC_OID_PKCS9_EMAIL_ADDRESS); |
1479 | 0 | pBuf = |
1480 | 0 | appendStringToBuf(pBuf, rawEmailAddr, &maxLen); |
1481 | |
|
1482 | 0 | rawEmailAddr = |
1483 | 0 | CERT_GetNameElement(&tmpArena.arena, |
1484 | 0 | ¤t->name.directoryName, |
1485 | 0 | SEC_OID_RFC1274_MAIL); |
1486 | 0 | pBuf = |
1487 | 0 | appendStringToBuf(pBuf, rawEmailAddr, &maxLen); |
1488 | 0 | } else if (current->type == certRFC822Name) { |
1489 | 0 | pBuf = |
1490 | 0 | appendItemToBuf(pBuf, ¤t->name.other, &maxLen); |
1491 | 0 | } |
1492 | 0 | current = CERT_GetNextGeneralName(current); |
1493 | 0 | } while (current != nameList); |
1494 | 0 | } |
1495 | 0 | SECITEM_FreeItem(&subAltName, PR_FALSE); |
1496 | | /* Don't free nameList, it's part of the tmpArena. */ |
1497 | 0 | } |
1498 | | /* now copy superstring to cert's arena */ |
1499 | 0 | finalLen = (pBuf - addrBuf) + 1; |
1500 | 0 | pBuf = NULL; |
1501 | 0 | if (finalLen > 1) { |
1502 | 0 | pBuf = PORT_ArenaAlloc(cert->arena, finalLen); |
1503 | 0 | if (pBuf) { |
1504 | 0 | PORT_Memcpy(pBuf, addrBuf, finalLen); |
1505 | 0 | } |
1506 | 0 | } |
1507 | 0 | loser: |
1508 | 0 | PORT_DestroyCheapArena(&tmpArena); |
1509 | |
|
1510 | 0 | return pBuf; |
1511 | 0 | } |
1512 | | |
1513 | | /* returns pointer to storage in cert's arena. Storage remains valid |
1514 | | ** as long as cert's reference count doesn't go to zero. |
1515 | | ** Caller should strdup or otherwise copy. |
1516 | | */ |
1517 | | const char* /* const so caller won't muck with it. */ |
1518 | | CERT_GetFirstEmailAddress(CERTCertificate* cert) |
1519 | 0 | { |
1520 | 0 | if (cert && cert->emailAddr && cert->emailAddr[0]) |
1521 | 0 | return (const char*)cert->emailAddr; |
1522 | 0 | return NULL; |
1523 | 0 | } |
1524 | | |
1525 | | /* returns pointer to storage in cert's arena. Storage remains valid |
1526 | | ** as long as cert's reference count doesn't go to zero. |
1527 | | ** Caller should strdup or otherwise copy. |
1528 | | */ |
1529 | | const char* /* const so caller won't muck with it. */ |
1530 | | CERT_GetNextEmailAddress(CERTCertificate* cert, const char* prev) |
1531 | 0 | { |
1532 | 0 | if (cert && prev && prev[0]) { |
1533 | 0 | PRUint32 len = PL_strlen(prev); |
1534 | 0 | prev += len + 1; |
1535 | 0 | if (prev && prev[0]) |
1536 | 0 | return prev; |
1537 | 0 | } |
1538 | 0 | return NULL; |
1539 | 0 | } |
1540 | | |
1541 | | /* This is seriously bogus, now that certs store their email addresses in |
1542 | | ** subject Alternative Name extensions. |
1543 | | ** Returns a string allocated by PORT_StrDup, which the caller must free. |
1544 | | */ |
1545 | | char* |
1546 | | CERT_GetCertEmailAddress(const CERTName* name) |
1547 | 0 | { |
1548 | 0 | char* rawEmailAddr; |
1549 | 0 | char* emailAddr; |
1550 | |
|
1551 | 0 | rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS); |
1552 | 0 | if (rawEmailAddr == NULL) { |
1553 | 0 | rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL); |
1554 | 0 | } |
1555 | 0 | emailAddr = CERT_FixupEmailAddr(rawEmailAddr); |
1556 | 0 | if (rawEmailAddr) { |
1557 | 0 | PORT_Free(rawEmailAddr); |
1558 | 0 | } |
1559 | 0 | return (emailAddr); |
1560 | 0 | } |
1561 | | |
1562 | | /* The return value must be freed with PORT_Free. */ |
1563 | | char* |
1564 | | CERT_GetCommonName(const CERTName* name) |
1565 | 0 | { |
1566 | 0 | return (CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME)); |
1567 | 0 | } |
1568 | | |
1569 | | char* |
1570 | | CERT_GetCountryName(const CERTName* name) |
1571 | 0 | { |
1572 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME)); |
1573 | 0 | } |
1574 | | |
1575 | | char* |
1576 | | CERT_GetLocalityName(const CERTName* name) |
1577 | 0 | { |
1578 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY)); |
1579 | 0 | } |
1580 | | |
1581 | | char* |
1582 | | CERT_GetStateName(const CERTName* name) |
1583 | 0 | { |
1584 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE)); |
1585 | 0 | } |
1586 | | |
1587 | | char* |
1588 | | CERT_GetOrgName(const CERTName* name) |
1589 | 0 | { |
1590 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME)); |
1591 | 0 | } |
1592 | | |
1593 | | char* |
1594 | | CERT_GetDomainComponentName(const CERTName* name) |
1595 | 0 | { |
1596 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC)); |
1597 | 0 | } |
1598 | | |
1599 | | char* |
1600 | | CERT_GetOrgUnitName(const CERTName* name) |
1601 | 0 | { |
1602 | 0 | return ( |
1603 | 0 | CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME)); |
1604 | 0 | } |
1605 | | |
1606 | | char* |
1607 | | CERT_GetDnQualifier(const CERTName* name) |
1608 | 0 | { |
1609 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER)); |
1610 | 0 | } |
1611 | | |
1612 | | char* |
1613 | | CERT_GetCertUid(const CERTName* name) |
1614 | 0 | { |
1615 | 0 | return (CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID)); |
1616 | 0 | } |