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