/src/nss/lib/certdb/genname.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "plarena.h" |
6 | | #include "seccomon.h" |
7 | | #include "secitem.h" |
8 | | #include "secoidt.h" |
9 | | #include "secasn1.h" |
10 | | #include "secder.h" |
11 | | #include "certt.h" |
12 | | #include "cert.h" |
13 | | #include "certi.h" |
14 | | #include "xconst.h" |
15 | | #include "secerr.h" |
16 | | #include "secoid.h" |
17 | | #include "prprf.h" |
18 | | #include "genname.h" |
19 | | |
20 | | SEC_ASN1_MKSUB(SEC_AnyTemplate) |
21 | | SEC_ASN1_MKSUB(SEC_IntegerTemplate) |
22 | | SEC_ASN1_MKSUB(SEC_IA5StringTemplate) |
23 | | SEC_ASN1_MKSUB(SEC_ObjectIDTemplate) |
24 | | SEC_ASN1_MKSUB(SEC_OctetStringTemplate) |
25 | | |
26 | | static const SEC_ASN1Template CERTNameConstraintTemplate[] = { |
27 | | { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraint) }, |
28 | | { SEC_ASN1_ANY, offsetof(CERTNameConstraint, DERName) }, |
29 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
30 | | offsetof(CERTNameConstraint, min), SEC_ASN1_SUB(SEC_IntegerTemplate) }, |
31 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, |
32 | | offsetof(CERTNameConstraint, max), SEC_ASN1_SUB(SEC_IntegerTemplate) }, |
33 | | { 0 } |
34 | | }; |
35 | | |
36 | | const SEC_ASN1Template CERT_NameConstraintSubtreeSubTemplate[] = { |
37 | | { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } |
38 | | }; |
39 | | |
40 | | static const SEC_ASN1Template CERTNameConstraintsTemplate[] = { |
41 | | { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTNameConstraints) }, |
42 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
43 | | offsetof(CERTNameConstraints, DERPermited), |
44 | | CERT_NameConstraintSubtreeSubTemplate }, |
45 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, |
46 | | offsetof(CERTNameConstraints, DERExcluded), |
47 | | CERT_NameConstraintSubtreeSubTemplate }, |
48 | | { 0 } |
49 | | }; |
50 | | |
51 | | static const SEC_ASN1Template CERTOthNameTemplate[] = { |
52 | | { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OtherName) }, |
53 | | { SEC_ASN1_OBJECT_ID, offsetof(OtherName, oid) }, |
54 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | |
55 | | SEC_ASN1_XTRN | 0, |
56 | | offsetof(OtherName, name), SEC_ASN1_SUB(SEC_AnyTemplate) }, |
57 | | { 0 } |
58 | | }; |
59 | | |
60 | | static const SEC_ASN1Template CERTOtherNameTemplate[] = { |
61 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0, |
62 | | offsetof(CERTGeneralName, name.OthName), CERTOthNameTemplate, |
63 | | sizeof(CERTGeneralName) } |
64 | | }; |
65 | | |
66 | | static const SEC_ASN1Template CERT_RFC822NameTemplate[] = { |
67 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, |
68 | | offsetof(CERTGeneralName, name.other), |
69 | | SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } |
70 | | }; |
71 | | |
72 | | static const SEC_ASN1Template CERT_DNSNameTemplate[] = { |
73 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, |
74 | | offsetof(CERTGeneralName, name.other), |
75 | | SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } |
76 | | }; |
77 | | |
78 | | static const SEC_ASN1Template CERT_X400AddressTemplate[] = { |
79 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 3, |
80 | | offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), |
81 | | sizeof(CERTGeneralName) } |
82 | | }; |
83 | | |
84 | | static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = { |
85 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | |
86 | | SEC_ASN1_XTRN | 4, |
87 | | offsetof(CERTGeneralName, derDirectoryName), |
88 | | SEC_ASN1_SUB(SEC_AnyTemplate), sizeof(CERTGeneralName) } |
89 | | }; |
90 | | |
91 | | static const SEC_ASN1Template CERT_EDIPartyNameTemplate[] = { |
92 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_XTRN | 5, |
93 | | offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_AnyTemplate), |
94 | | sizeof(CERTGeneralName) } |
95 | | }; |
96 | | |
97 | | static const SEC_ASN1Template CERT_URITemplate[] = { |
98 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 6, |
99 | | offsetof(CERTGeneralName, name.other), |
100 | | SEC_ASN1_SUB(SEC_IA5StringTemplate), sizeof(CERTGeneralName) } |
101 | | }; |
102 | | |
103 | | static const SEC_ASN1Template CERT_IPAddressTemplate[] = { |
104 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 7, |
105 | | offsetof(CERTGeneralName, name.other), |
106 | | SEC_ASN1_SUB(SEC_OctetStringTemplate), sizeof(CERTGeneralName) } |
107 | | }; |
108 | | |
109 | | static const SEC_ASN1Template CERT_RegisteredIDTemplate[] = { |
110 | | { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 8, |
111 | | offsetof(CERTGeneralName, name.other), SEC_ASN1_SUB(SEC_ObjectIDTemplate), |
112 | | sizeof(CERTGeneralName) } |
113 | | }; |
114 | | |
115 | | const SEC_ASN1Template CERT_GeneralNamesTemplate[] = { |
116 | | { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } |
117 | | }; |
118 | | |
119 | | static struct { |
120 | | CERTGeneralNameType type; |
121 | | char *name; |
122 | | } typesArray[] = { { certOtherName, "other" }, |
123 | | { certRFC822Name, "email" }, |
124 | | { certRFC822Name, "rfc822" }, |
125 | | { certDNSName, "dns" }, |
126 | | { certX400Address, "x400" }, |
127 | | { certX400Address, "x400addr" }, |
128 | | { certDirectoryName, "directory" }, |
129 | | { certDirectoryName, "dn" }, |
130 | | { certEDIPartyName, "edi" }, |
131 | | { certEDIPartyName, "ediparty" }, |
132 | | { certURI, "uri" }, |
133 | | { certIPAddress, "ip" }, |
134 | | { certIPAddress, "ipaddr" }, |
135 | | { certRegisterID, "registerid" } }; |
136 | | |
137 | | CERTGeneralNameType |
138 | | CERT_GetGeneralNameTypeFromString(const char *string) |
139 | 0 | { |
140 | 0 | int types_count = sizeof(typesArray) / sizeof(typesArray[0]); |
141 | 0 | int i; |
142 | |
|
143 | 0 | for (i = 0; i < types_count; i++) { |
144 | 0 | if (PORT_Strcasecmp(string, typesArray[i].name) == 0) { |
145 | 0 | return typesArray[i].type; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | return 0; |
149 | 0 | } |
150 | | |
151 | | CERTGeneralName * |
152 | | CERT_NewGeneralName(PLArenaPool *arena, CERTGeneralNameType type) |
153 | 28.0k | { |
154 | 28.0k | CERTGeneralName *name = arena ? PORT_ArenaZNew(arena, CERTGeneralName) |
155 | 28.0k | : PORT_ZNew(CERTGeneralName); |
156 | 28.0k | if (name) { |
157 | 28.0k | name->type = type; |
158 | 28.0k | name->l.prev = name->l.next = &name->l; |
159 | 28.0k | } |
160 | 28.0k | return name; |
161 | 28.0k | } |
162 | | |
163 | | /* Copy content of one General Name to another. |
164 | | ** Caller has allocated destination general name. |
165 | | ** This function does not change the destinate's GeneralName's list linkage. |
166 | | */ |
167 | | SECStatus |
168 | | cert_CopyOneGeneralName(PLArenaPool *arena, CERTGeneralName *dest, |
169 | | CERTGeneralName *src) |
170 | 0 | { |
171 | 0 | SECStatus rv; |
172 | 0 | void *mark = NULL; |
173 | |
|
174 | 0 | PORT_Assert(dest != NULL); |
175 | 0 | dest->type = src->type; |
176 | |
|
177 | 0 | mark = PORT_ArenaMark(arena); |
178 | |
|
179 | 0 | switch (src->type) { |
180 | 0 | case certDirectoryName: |
181 | 0 | rv = SECITEM_CopyItem(arena, &dest->derDirectoryName, |
182 | 0 | &src->derDirectoryName); |
183 | 0 | if (rv == SECSuccess) |
184 | 0 | rv = CERT_CopyName(arena, &dest->name.directoryName, |
185 | 0 | &src->name.directoryName); |
186 | 0 | break; |
187 | | |
188 | 0 | case certOtherName: |
189 | 0 | rv = SECITEM_CopyItem(arena, &dest->name.OthName.name, |
190 | 0 | &src->name.OthName.name); |
191 | 0 | if (rv == SECSuccess) |
192 | 0 | rv = SECITEM_CopyItem(arena, &dest->name.OthName.oid, |
193 | 0 | &src->name.OthName.oid); |
194 | 0 | break; |
195 | | |
196 | 0 | default: |
197 | 0 | rv = SECITEM_CopyItem(arena, &dest->name.other, &src->name.other); |
198 | 0 | break; |
199 | 0 | } |
200 | 0 | if (rv != SECSuccess) { |
201 | 0 | PORT_ArenaRelease(arena, mark); |
202 | 0 | } else { |
203 | 0 | PORT_ArenaUnmark(arena, mark); |
204 | 0 | } |
205 | 0 | return rv; |
206 | 0 | } |
207 | | |
208 | | void |
209 | | CERT_DestroyGeneralNameList(CERTGeneralNameList *list) |
210 | 0 | { |
211 | 0 | PZLock *lock; |
212 | |
|
213 | 0 | if (list != NULL) { |
214 | 0 | lock = list->lock; |
215 | 0 | PZ_Lock(lock); |
216 | 0 | if (--list->refCount <= 0 && list->arena != NULL) { |
217 | 0 | PORT_FreeArena(list->arena, PR_FALSE); |
218 | 0 | PZ_Unlock(lock); |
219 | 0 | PZ_DestroyLock(lock); |
220 | 0 | } else { |
221 | 0 | PZ_Unlock(lock); |
222 | 0 | } |
223 | 0 | } |
224 | 0 | return; |
225 | 0 | } |
226 | | |
227 | | CERTGeneralNameList * |
228 | | CERT_CreateGeneralNameList(CERTGeneralName *name) |
229 | 0 | { |
230 | 0 | PLArenaPool *arena; |
231 | 0 | CERTGeneralNameList *list = NULL; |
232 | |
|
233 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
234 | 0 | if (arena == NULL) { |
235 | 0 | goto done; |
236 | 0 | } |
237 | 0 | list = PORT_ArenaZNew(arena, CERTGeneralNameList); |
238 | 0 | if (!list) |
239 | 0 | goto loser; |
240 | 0 | if (name != NULL) { |
241 | 0 | SECStatus rv; |
242 | 0 | list->name = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); |
243 | 0 | if (!list->name) |
244 | 0 | goto loser; |
245 | 0 | rv = CERT_CopyGeneralName(arena, list->name, name); |
246 | 0 | if (rv != SECSuccess) |
247 | 0 | goto loser; |
248 | 0 | } |
249 | 0 | list->lock = PZ_NewLock(nssILockList); |
250 | 0 | if (!list->lock) |
251 | 0 | goto loser; |
252 | 0 | list->arena = arena; |
253 | 0 | list->refCount = 1; |
254 | 0 | done: |
255 | 0 | return list; |
256 | | |
257 | 0 | loser: |
258 | 0 | PORT_FreeArena(arena, PR_FALSE); |
259 | 0 | return NULL; |
260 | 0 | } |
261 | | |
262 | | CERTGeneralName * |
263 | | CERT_GetNextGeneralName(CERTGeneralName *current) |
264 | 30.0k | { |
265 | 30.0k | PRCList *next; |
266 | | |
267 | 30.0k | next = current->l.next; |
268 | 30.0k | return (CERTGeneralName *)(((char *)next) - offsetof(CERTGeneralName, l)); |
269 | 30.0k | } |
270 | | |
271 | | CERTGeneralName * |
272 | | CERT_GetPrevGeneralName(CERTGeneralName *current) |
273 | 0 | { |
274 | 0 | PRCList *prev; |
275 | 0 | prev = current->l.prev; |
276 | 0 | return (CERTGeneralName *)(((char *)prev) - offsetof(CERTGeneralName, l)); |
277 | 0 | } |
278 | | |
279 | | CERTNameConstraint * |
280 | | CERT_GetNextNameConstraint(CERTNameConstraint *current) |
281 | 0 | { |
282 | 0 | PRCList *next; |
283 | |
|
284 | 0 | next = current->l.next; |
285 | 0 | return (CERTNameConstraint *)(((char *)next) - |
286 | 0 | offsetof(CERTNameConstraint, l)); |
287 | 0 | } |
288 | | |
289 | | CERTNameConstraint * |
290 | | CERT_GetPrevNameConstraint(CERTNameConstraint *current) |
291 | 0 | { |
292 | 0 | PRCList *prev; |
293 | 0 | prev = current->l.prev; |
294 | 0 | return (CERTNameConstraint *)(((char *)prev) - |
295 | 0 | offsetof(CERTNameConstraint, l)); |
296 | 0 | } |
297 | | |
298 | | SECItem * |
299 | | CERT_EncodeGeneralName(CERTGeneralName *genName, SECItem *dest, |
300 | | PLArenaPool *arena) |
301 | 0 | { |
302 | |
|
303 | 0 | const SEC_ASN1Template *template; |
304 | |
|
305 | 0 | PORT_Assert(arena); |
306 | 0 | if (arena == NULL || !genName) { |
307 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
308 | 0 | return NULL; |
309 | 0 | } |
310 | | /* TODO: mark arena */ |
311 | 0 | if (dest == NULL) { |
312 | 0 | dest = PORT_ArenaZNew(arena, SECItem); |
313 | 0 | if (!dest) |
314 | 0 | goto loser; |
315 | 0 | } |
316 | 0 | if (genName->type == certDirectoryName) { |
317 | 0 | if (genName->derDirectoryName.data == NULL) { |
318 | | /* The field hasn't been encoded yet. */ |
319 | 0 | SECItem *pre_dest = SEC_ASN1EncodeItem( |
320 | 0 | arena, &(genName->derDirectoryName), |
321 | 0 | &(genName->name.directoryName), CERT_NameTemplate); |
322 | 0 | if (!pre_dest) |
323 | 0 | goto loser; |
324 | 0 | } |
325 | 0 | if (genName->derDirectoryName.data == NULL) { |
326 | 0 | goto loser; |
327 | 0 | } |
328 | 0 | } |
329 | 0 | switch (genName->type) { |
330 | 0 | case certURI: |
331 | 0 | template = CERT_URITemplate; |
332 | 0 | break; |
333 | 0 | case certRFC822Name: |
334 | 0 | template = CERT_RFC822NameTemplate; |
335 | 0 | break; |
336 | 0 | case certDNSName: |
337 | 0 | template = CERT_DNSNameTemplate; |
338 | 0 | break; |
339 | 0 | case certIPAddress: |
340 | 0 | template = CERT_IPAddressTemplate; |
341 | 0 | break; |
342 | 0 | case certOtherName: |
343 | 0 | template = CERTOtherNameTemplate; |
344 | 0 | break; |
345 | 0 | case certRegisterID: |
346 | 0 | template = CERT_RegisteredIDTemplate; |
347 | 0 | break; |
348 | | /* for this type, we expect the value is already encoded */ |
349 | 0 | case certEDIPartyName: |
350 | 0 | template = CERT_EDIPartyNameTemplate; |
351 | 0 | break; |
352 | | /* for this type, we expect the value is already encoded */ |
353 | 0 | case certX400Address: |
354 | 0 | template = CERT_X400AddressTemplate; |
355 | 0 | break; |
356 | 0 | case certDirectoryName: |
357 | 0 | template = CERT_DirectoryNameTemplate; |
358 | 0 | break; |
359 | 0 | default: |
360 | 0 | PORT_Assert(0); |
361 | 0 | goto loser; |
362 | 0 | } |
363 | 0 | dest = SEC_ASN1EncodeItem(arena, dest, genName, template); |
364 | 0 | if (!dest) { |
365 | 0 | goto loser; |
366 | 0 | } |
367 | | /* TODO: unmark arena */ |
368 | 0 | return dest; |
369 | 0 | loser: |
370 | | /* TODO: release arena back to mark */ |
371 | 0 | return NULL; |
372 | 0 | } |
373 | | |
374 | | SECItem ** |
375 | | cert_EncodeGeneralNames(PLArenaPool *arena, CERTGeneralName *names) |
376 | 0 | { |
377 | 0 | CERTGeneralName *current_name; |
378 | 0 | SECItem **items = NULL; |
379 | 0 | int count = 1; |
380 | 0 | int i; |
381 | 0 | PRCList *head; |
382 | |
|
383 | 0 | if (!names) { |
384 | 0 | return NULL; |
385 | 0 | } |
386 | | |
387 | 0 | PORT_Assert(arena); |
388 | | /* TODO: mark arena */ |
389 | 0 | current_name = names; |
390 | 0 | head = &(names->l); |
391 | 0 | while (current_name->l.next != head) { |
392 | 0 | current_name = CERT_GetNextGeneralName(current_name); |
393 | 0 | ++count; |
394 | 0 | } |
395 | 0 | current_name = CERT_GetNextGeneralName(current_name); |
396 | 0 | items = PORT_ArenaNewArray(arena, SECItem *, count + 1); |
397 | 0 | if (items == NULL) { |
398 | 0 | goto loser; |
399 | 0 | } |
400 | 0 | for (i = 0; i < count; i++) { |
401 | 0 | items[i] = CERT_EncodeGeneralName(current_name, (SECItem *)NULL, arena); |
402 | 0 | if (items[i] == NULL) { |
403 | 0 | goto loser; |
404 | 0 | } |
405 | 0 | current_name = CERT_GetNextGeneralName(current_name); |
406 | 0 | } |
407 | 0 | items[i] = NULL; |
408 | | /* TODO: unmark arena */ |
409 | 0 | return items; |
410 | 0 | loser: |
411 | | /* TODO: release arena to mark */ |
412 | 0 | return NULL; |
413 | 0 | } |
414 | | |
415 | | CERTGeneralName * |
416 | | CERT_DecodeGeneralName(PLArenaPool *reqArena, SECItem *encodedName, |
417 | | CERTGeneralName *genName) |
418 | 20.7k | { |
419 | 20.7k | const SEC_ASN1Template *template; |
420 | 20.7k | CERTGeneralNameType genNameType; |
421 | 20.7k | SECStatus rv = SECSuccess; |
422 | 20.7k | SECItem *newEncodedName; |
423 | | |
424 | 20.7k | if (!reqArena) { |
425 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
426 | 0 | return NULL; |
427 | 0 | } |
428 | | /* make a copy for decoding so the data decoded with QuickDER doesn't |
429 | | point to temporary memory */ |
430 | 20.7k | newEncodedName = SECITEM_ArenaDupItem(reqArena, encodedName); |
431 | 20.7k | if (!newEncodedName) { |
432 | 0 | return NULL; |
433 | 0 | } |
434 | | /* TODO: mark arena */ |
435 | 20.7k | genNameType = (CERTGeneralNameType)((*(newEncodedName->data) & 0x0f) + 1); |
436 | 20.7k | if (genName == NULL) { |
437 | 20.7k | genName = CERT_NewGeneralName(reqArena, genNameType); |
438 | 20.7k | if (!genName) |
439 | 0 | goto loser; |
440 | 20.7k | } else { |
441 | 0 | genName->type = genNameType; |
442 | 0 | genName->l.prev = genName->l.next = &genName->l; |
443 | 0 | } |
444 | | |
445 | 20.7k | switch (genNameType) { |
446 | 972 | case certURI: |
447 | 972 | template = CERT_URITemplate; |
448 | 972 | break; |
449 | 1.70k | case certRFC822Name: |
450 | 1.70k | template = CERT_RFC822NameTemplate; |
451 | 1.70k | break; |
452 | 14.0k | case certDNSName: |
453 | 14.0k | template = CERT_DNSNameTemplate; |
454 | 14.0k | break; |
455 | 2.18k | case certIPAddress: |
456 | 2.18k | template = CERT_IPAddressTemplate; |
457 | 2.18k | break; |
458 | 480 | case certOtherName: |
459 | 480 | template = CERTOtherNameTemplate; |
460 | 480 | break; |
461 | 515 | case certRegisterID: |
462 | 515 | template = CERT_RegisteredIDTemplate; |
463 | 515 | break; |
464 | 41 | case certEDIPartyName: |
465 | 41 | template = CERT_EDIPartyNameTemplate; |
466 | 41 | break; |
467 | 102 | case certX400Address: |
468 | 102 | template = CERT_X400AddressTemplate; |
469 | 102 | break; |
470 | 594 | case certDirectoryName: |
471 | 594 | template = CERT_DirectoryNameTemplate; |
472 | 594 | break; |
473 | 114 | default: |
474 | 114 | goto loser; |
475 | 20.7k | } |
476 | 20.6k | rv = SEC_QuickDERDecodeItem(reqArena, genName, template, newEncodedName); |
477 | 20.6k | if (rv != SECSuccess) |
478 | 527 | goto loser; |
479 | 20.1k | if (genNameType == certDirectoryName) { |
480 | 543 | rv = SEC_QuickDERDecodeItem(reqArena, &(genName->name.directoryName), |
481 | 543 | CERT_NameTemplate, |
482 | 543 | &(genName->derDirectoryName)); |
483 | 543 | if (rv != SECSuccess) |
484 | 275 | goto loser; |
485 | 543 | } |
486 | | |
487 | | /* TODO: unmark arena */ |
488 | 19.8k | return genName; |
489 | 916 | loser: |
490 | | /* TODO: release arena to mark */ |
491 | 916 | return NULL; |
492 | 20.1k | } |
493 | | |
494 | | CERTGeneralName * |
495 | | cert_DecodeGeneralNames(PLArenaPool *arena, SECItem **encodedGenName) |
496 | 5.55k | { |
497 | 5.55k | PRCList *head = NULL; |
498 | 5.55k | PRCList *tail = NULL; |
499 | 5.55k | CERTGeneralName *currentName = NULL; |
500 | | |
501 | 5.55k | PORT_Assert(arena); |
502 | 5.55k | if (!encodedGenName || !arena) { |
503 | 2.80k | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
504 | 2.80k | return NULL; |
505 | 2.80k | } |
506 | | /* TODO: mark arena */ |
507 | 22.6k | while (*encodedGenName != NULL) { |
508 | 20.7k | currentName = CERT_DecodeGeneralName(arena, *encodedGenName, NULL); |
509 | 20.7k | if (currentName == NULL) |
510 | 916 | break; |
511 | 19.8k | if (head == NULL) { |
512 | 2.08k | head = &(currentName->l); |
513 | 2.08k | tail = head; |
514 | 2.08k | } |
515 | 19.8k | currentName->l.next = head; |
516 | 19.8k | currentName->l.prev = tail; |
517 | 19.8k | tail = head->prev = tail->next = &(currentName->l); |
518 | 19.8k | encodedGenName++; |
519 | 19.8k | } |
520 | 2.74k | if (currentName) { |
521 | | /* TODO: unmark arena */ |
522 | 1.82k | return CERT_GetNextGeneralName(currentName); |
523 | 1.82k | } |
524 | | /* TODO: release arena to mark */ |
525 | 916 | return NULL; |
526 | 2.74k | } |
527 | | |
528 | | void |
529 | | CERT_DestroyGeneralName(CERTGeneralName *name) |
530 | 0 | { |
531 | 0 | cert_DestroyGeneralNames(name); |
532 | 0 | } |
533 | | |
534 | | SECStatus |
535 | | cert_DestroyGeneralNames(CERTGeneralName *name) |
536 | 0 | { |
537 | 0 | CERTGeneralName *first; |
538 | 0 | CERTGeneralName *next = NULL; |
539 | |
|
540 | 0 | first = name; |
541 | 0 | do { |
542 | 0 | next = CERT_GetNextGeneralName(name); |
543 | 0 | PORT_Free(name); |
544 | 0 | name = next; |
545 | 0 | } while (name != first); |
546 | 0 | return SECSuccess; |
547 | 0 | } |
548 | | |
549 | | static SECItem * |
550 | | cert_EncodeNameConstraint(CERTNameConstraint *constraint, SECItem *dest, |
551 | | PLArenaPool *arena) |
552 | 0 | { |
553 | 0 | PORT_Assert(arena); |
554 | 0 | if (dest == NULL) { |
555 | 0 | dest = PORT_ArenaZNew(arena, SECItem); |
556 | 0 | if (dest == NULL) { |
557 | 0 | return NULL; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | CERT_EncodeGeneralName(&(constraint->name), &(constraint->DERName), arena); |
561 | |
|
562 | 0 | dest = |
563 | 0 | SEC_ASN1EncodeItem(arena, dest, constraint, CERTNameConstraintTemplate); |
564 | 0 | return dest; |
565 | 0 | } |
566 | | |
567 | | SECStatus |
568 | | cert_EncodeNameConstraintSubTree(CERTNameConstraint *constraints, |
569 | | PLArenaPool *arena, SECItem ***dest, |
570 | | PRBool permited) |
571 | 0 | { |
572 | 0 | CERTNameConstraint *current_constraint = constraints; |
573 | 0 | SECItem **items = NULL; |
574 | 0 | int count = 0; |
575 | 0 | int i; |
576 | 0 | PRCList *head; |
577 | |
|
578 | 0 | PORT_Assert(arena); |
579 | | /* TODO: mark arena */ |
580 | 0 | if (constraints != NULL) { |
581 | 0 | count = 1; |
582 | 0 | } |
583 | 0 | head = &constraints->l; |
584 | 0 | while (current_constraint->l.next != head) { |
585 | 0 | current_constraint = CERT_GetNextNameConstraint(current_constraint); |
586 | 0 | ++count; |
587 | 0 | } |
588 | 0 | current_constraint = CERT_GetNextNameConstraint(current_constraint); |
589 | 0 | items = PORT_ArenaZNewArray(arena, SECItem *, count + 1); |
590 | 0 | if (items == NULL) { |
591 | 0 | goto loser; |
592 | 0 | } |
593 | 0 | for (i = 0; i < count; i++) { |
594 | 0 | items[i] = cert_EncodeNameConstraint(current_constraint, |
595 | 0 | (SECItem *)NULL, arena); |
596 | 0 | if (items[i] == NULL) { |
597 | 0 | goto loser; |
598 | 0 | } |
599 | 0 | current_constraint = CERT_GetNextNameConstraint(current_constraint); |
600 | 0 | } |
601 | 0 | *dest = items; |
602 | 0 | if (*dest == NULL) { |
603 | 0 | goto loser; |
604 | 0 | } |
605 | | /* TODO: unmark arena */ |
606 | 0 | return SECSuccess; |
607 | 0 | loser: |
608 | | /* TODO: release arena to mark */ |
609 | 0 | return SECFailure; |
610 | 0 | } |
611 | | |
612 | | SECStatus |
613 | | cert_EncodeNameConstraints(CERTNameConstraints *constraints, PLArenaPool *arena, |
614 | | SECItem *dest) |
615 | 0 | { |
616 | 0 | SECStatus rv = SECSuccess; |
617 | |
|
618 | 0 | PORT_Assert(arena); |
619 | | /* TODO: mark arena */ |
620 | 0 | if (constraints->permited != NULL) { |
621 | 0 | rv = cert_EncodeNameConstraintSubTree( |
622 | 0 | constraints->permited, arena, &constraints->DERPermited, PR_TRUE); |
623 | 0 | if (rv == SECFailure) { |
624 | 0 | goto loser; |
625 | 0 | } |
626 | 0 | } |
627 | 0 | if (constraints->excluded != NULL) { |
628 | 0 | rv = cert_EncodeNameConstraintSubTree( |
629 | 0 | constraints->excluded, arena, &constraints->DERExcluded, PR_FALSE); |
630 | 0 | if (rv == SECFailure) { |
631 | 0 | goto loser; |
632 | 0 | } |
633 | 0 | } |
634 | 0 | dest = SEC_ASN1EncodeItem(arena, dest, constraints, |
635 | 0 | CERTNameConstraintsTemplate); |
636 | 0 | if (dest == NULL) { |
637 | 0 | goto loser; |
638 | 0 | } |
639 | | /* TODO: unmark arena */ |
640 | 0 | return SECSuccess; |
641 | 0 | loser: |
642 | | /* TODO: release arena to mark */ |
643 | 0 | return SECFailure; |
644 | 0 | } |
645 | | |
646 | | CERTNameConstraint * |
647 | | cert_DecodeNameConstraint(PLArenaPool *reqArena, SECItem *encodedConstraint) |
648 | 0 | { |
649 | 0 | CERTNameConstraint *constraint; |
650 | 0 | SECStatus rv = SECSuccess; |
651 | 0 | CERTGeneralName *temp; |
652 | 0 | SECItem *newEncodedConstraint; |
653 | |
|
654 | 0 | if (!reqArena) { |
655 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
656 | 0 | return NULL; |
657 | 0 | } |
658 | 0 | newEncodedConstraint = SECITEM_ArenaDupItem(reqArena, encodedConstraint); |
659 | 0 | if (!newEncodedConstraint) { |
660 | 0 | return NULL; |
661 | 0 | } |
662 | | /* TODO: mark arena */ |
663 | 0 | constraint = PORT_ArenaZNew(reqArena, CERTNameConstraint); |
664 | 0 | if (!constraint) |
665 | 0 | goto loser; |
666 | 0 | rv = SEC_QuickDERDecodeItem( |
667 | 0 | reqArena, constraint, CERTNameConstraintTemplate, newEncodedConstraint); |
668 | 0 | if (rv != SECSuccess) { |
669 | 0 | goto loser; |
670 | 0 | } |
671 | 0 | temp = CERT_DecodeGeneralName(reqArena, &(constraint->DERName), |
672 | 0 | &(constraint->name)); |
673 | 0 | if (temp != &(constraint->name)) { |
674 | 0 | goto loser; |
675 | 0 | } |
676 | | |
677 | | /* ### sjlee: since the name constraint contains only one |
678 | | * CERTGeneralName, the list within CERTGeneralName shouldn't |
679 | | * point anywhere else. Otherwise, bad things will happen. |
680 | | */ |
681 | 0 | constraint->name.l.prev = constraint->name.l.next = &(constraint->name.l); |
682 | | /* TODO: unmark arena */ |
683 | 0 | return constraint; |
684 | 0 | loser: |
685 | | /* TODO: release arena back to mark */ |
686 | 0 | return NULL; |
687 | 0 | } |
688 | | |
689 | | static CERTNameConstraint * |
690 | | cert_DecodeNameConstraintSubTree(PLArenaPool *arena, SECItem **subTree, |
691 | | PRBool permited) |
692 | 0 | { |
693 | 0 | CERTNameConstraint *current = NULL; |
694 | 0 | CERTNameConstraint *first = NULL; |
695 | 0 | CERTNameConstraint *last = NULL; |
696 | 0 | int i = 0; |
697 | |
|
698 | 0 | PORT_Assert(arena); |
699 | | /* TODO: mark arena */ |
700 | 0 | while (subTree[i] != NULL) { |
701 | 0 | current = cert_DecodeNameConstraint(arena, subTree[i]); |
702 | 0 | if (current == NULL) { |
703 | 0 | goto loser; |
704 | 0 | } |
705 | 0 | if (first == NULL) { |
706 | 0 | first = current; |
707 | 0 | } else { |
708 | 0 | current->l.prev = &(last->l); |
709 | 0 | last->l.next = &(current->l); |
710 | 0 | } |
711 | 0 | last = current; |
712 | 0 | i++; |
713 | 0 | } |
714 | 0 | if (first && last) { |
715 | 0 | first->l.prev = &(last->l); |
716 | 0 | last->l.next = &(first->l); |
717 | 0 | } |
718 | | /* TODO: unmark arena */ |
719 | 0 | return first; |
720 | 0 | loser: |
721 | | /* TODO: release arena back to mark */ |
722 | 0 | return NULL; |
723 | 0 | } |
724 | | |
725 | | CERTNameConstraints * |
726 | | cert_DecodeNameConstraints(PLArenaPool *reqArena, |
727 | | const SECItem *encodedConstraints) |
728 | 16 | { |
729 | 16 | CERTNameConstraints *constraints; |
730 | 16 | SECStatus rv; |
731 | 16 | SECItem *newEncodedConstraints; |
732 | | |
733 | 16 | if (!reqArena) { |
734 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
735 | 0 | return NULL; |
736 | 0 | } |
737 | 16 | PORT_Assert(encodedConstraints); |
738 | 16 | newEncodedConstraints = SECITEM_ArenaDupItem(reqArena, encodedConstraints); |
739 | | |
740 | | /* TODO: mark arena */ |
741 | 16 | constraints = PORT_ArenaZNew(reqArena, CERTNameConstraints); |
742 | 16 | if (constraints == NULL) { |
743 | 0 | goto loser; |
744 | 0 | } |
745 | 16 | rv = SEC_QuickDERDecodeItem(reqArena, constraints, |
746 | 16 | CERTNameConstraintsTemplate, |
747 | 16 | newEncodedConstraints); |
748 | 16 | if (rv != SECSuccess) { |
749 | 16 | goto loser; |
750 | 16 | } |
751 | 0 | if (constraints->DERPermited != NULL && |
752 | 0 | constraints->DERPermited[0] != NULL) { |
753 | 0 | constraints->permited = cert_DecodeNameConstraintSubTree( |
754 | 0 | reqArena, constraints->DERPermited, PR_TRUE); |
755 | 0 | if (constraints->permited == NULL) { |
756 | 0 | goto loser; |
757 | 0 | } |
758 | 0 | } |
759 | 0 | if (constraints->DERExcluded != NULL && |
760 | 0 | constraints->DERExcluded[0] != NULL) { |
761 | 0 | constraints->excluded = cert_DecodeNameConstraintSubTree( |
762 | 0 | reqArena, constraints->DERExcluded, PR_FALSE); |
763 | 0 | if (constraints->excluded == NULL) { |
764 | 0 | goto loser; |
765 | 0 | } |
766 | 0 | } |
767 | | /* TODO: unmark arena */ |
768 | 0 | return constraints; |
769 | 16 | loser: |
770 | | /* TODO: release arena back to mark */ |
771 | 16 | return NULL; |
772 | 0 | } |
773 | | |
774 | | /* Copy a chain of one or more general names to a destination chain. |
775 | | ** Caller has allocated at least the first destination GeneralName struct. |
776 | | ** Both source and destination chains are circular doubly-linked lists. |
777 | | ** The first source struct is copied to the first destination struct. |
778 | | ** If the source chain has more than one member, and the destination chain |
779 | | ** has only one member, then this function allocates new structs for all but |
780 | | ** the first copy from the arena and links them into the destination list. |
781 | | ** If the destination struct is part of a list with more than one member, |
782 | | ** then this function traverses both the source and destination lists, |
783 | | ** copying each source struct to the corresponding dest struct. |
784 | | ** In that case, the destination list MUST contain at least as many |
785 | | ** structs as the source list or some dest entries will be overwritten. |
786 | | */ |
787 | | SECStatus |
788 | | CERT_CopyGeneralName(PLArenaPool *arena, CERTGeneralName *dest, |
789 | | CERTGeneralName *src) |
790 | 0 | { |
791 | 0 | SECStatus rv; |
792 | 0 | CERTGeneralName *destHead = dest; |
793 | 0 | CERTGeneralName *srcHead = src; |
794 | |
|
795 | 0 | PORT_Assert(dest != NULL); |
796 | 0 | if (!dest) { |
797 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
798 | 0 | return SECFailure; |
799 | 0 | } |
800 | | /* TODO: mark arena */ |
801 | 0 | do { |
802 | 0 | rv = cert_CopyOneGeneralName(arena, dest, src); |
803 | 0 | if (rv != SECSuccess) |
804 | 0 | goto loser; |
805 | 0 | src = CERT_GetNextGeneralName(src); |
806 | | /* if there is only one general name, we shouldn't do this */ |
807 | 0 | if (src != srcHead) { |
808 | 0 | if (dest->l.next == &destHead->l) { |
809 | 0 | CERTGeneralName *temp; |
810 | 0 | temp = CERT_NewGeneralName(arena, (CERTGeneralNameType)0); |
811 | 0 | if (!temp) |
812 | 0 | goto loser; |
813 | 0 | temp->l.next = &destHead->l; |
814 | 0 | temp->l.prev = &dest->l; |
815 | 0 | destHead->l.prev = &temp->l; |
816 | 0 | dest->l.next = &temp->l; |
817 | 0 | dest = temp; |
818 | 0 | } else { |
819 | 0 | dest = CERT_GetNextGeneralName(dest); |
820 | 0 | } |
821 | 0 | } |
822 | 0 | } while (src != srcHead && rv == SECSuccess); |
823 | | /* TODO: unmark arena */ |
824 | 0 | return rv; |
825 | 0 | loser: |
826 | | /* TODO: release back to mark */ |
827 | 0 | return SECFailure; |
828 | 0 | } |
829 | | |
830 | | CERTGeneralNameList * |
831 | | CERT_DupGeneralNameList(CERTGeneralNameList *list) |
832 | 0 | { |
833 | 0 | if (list != NULL) { |
834 | 0 | PZ_Lock(list->lock); |
835 | 0 | list->refCount++; |
836 | 0 | PZ_Unlock(list->lock); |
837 | 0 | } |
838 | 0 | return list; |
839 | 0 | } |
840 | | |
841 | | /* Allocate space and copy CERTNameConstraint from src to dest */ |
842 | | CERTNameConstraint * |
843 | | CERT_CopyNameConstraint(PLArenaPool *arena, CERTNameConstraint *dest, |
844 | | CERTNameConstraint *src) |
845 | 0 | { |
846 | 0 | SECStatus rv; |
847 | | |
848 | | /* TODO: mark arena */ |
849 | 0 | if (dest == NULL) { |
850 | 0 | dest = PORT_ArenaZNew(arena, CERTNameConstraint); |
851 | 0 | if (!dest) |
852 | 0 | goto loser; |
853 | | /* mark that it is not linked */ |
854 | 0 | dest->name.l.prev = dest->name.l.next = &(dest->name.l); |
855 | 0 | } |
856 | 0 | rv = CERT_CopyGeneralName(arena, &dest->name, &src->name); |
857 | 0 | if (rv != SECSuccess) { |
858 | 0 | goto loser; |
859 | 0 | } |
860 | 0 | rv = SECITEM_CopyItem(arena, &dest->DERName, &src->DERName); |
861 | 0 | if (rv != SECSuccess) { |
862 | 0 | goto loser; |
863 | 0 | } |
864 | 0 | rv = SECITEM_CopyItem(arena, &dest->min, &src->min); |
865 | 0 | if (rv != SECSuccess) { |
866 | 0 | goto loser; |
867 | 0 | } |
868 | 0 | rv = SECITEM_CopyItem(arena, &dest->max, &src->max); |
869 | 0 | if (rv != SECSuccess) { |
870 | 0 | goto loser; |
871 | 0 | } |
872 | 0 | dest->l.prev = dest->l.next = &dest->l; |
873 | | /* TODO: unmark arena */ |
874 | 0 | return dest; |
875 | 0 | loser: |
876 | | /* TODO: release arena to mark */ |
877 | 0 | return NULL; |
878 | 0 | } |
879 | | |
880 | | CERTGeneralName * |
881 | | cert_CombineNamesLists(CERTGeneralName *list1, CERTGeneralName *list2) |
882 | 13.7k | { |
883 | 13.7k | PRCList *begin1; |
884 | 13.7k | PRCList *begin2; |
885 | 13.7k | PRCList *end1; |
886 | 13.7k | PRCList *end2; |
887 | | |
888 | 13.7k | if (list1 == NULL) { |
889 | 6.56k | return list2; |
890 | 7.22k | } else if (list2 == NULL) { |
891 | 6.21k | return list1; |
892 | 6.21k | } else { |
893 | 1.01k | begin1 = &list1->l; |
894 | 1.01k | begin2 = &list2->l; |
895 | 1.01k | end1 = list1->l.prev; |
896 | 1.01k | end2 = list2->l.prev; |
897 | 1.01k | end1->next = begin2; |
898 | 1.01k | end2->next = begin1; |
899 | 1.01k | begin1->prev = end2; |
900 | 1.01k | begin2->prev = end1; |
901 | 1.01k | return list1; |
902 | 1.01k | } |
903 | 13.7k | } |
904 | | |
905 | | CERTNameConstraint * |
906 | | cert_CombineConstraintsLists(CERTNameConstraint *list1, |
907 | | CERTNameConstraint *list2) |
908 | 0 | { |
909 | 0 | PRCList *begin1; |
910 | 0 | PRCList *begin2; |
911 | 0 | PRCList *end1; |
912 | 0 | PRCList *end2; |
913 | |
|
914 | 0 | if (list1 == NULL) { |
915 | 0 | return list2; |
916 | 0 | } else if (list2 == NULL) { |
917 | 0 | return list1; |
918 | 0 | } else { |
919 | 0 | begin1 = &list1->l; |
920 | 0 | begin2 = &list2->l; |
921 | 0 | end1 = list1->l.prev; |
922 | 0 | end2 = list2->l.prev; |
923 | 0 | end1->next = begin2; |
924 | 0 | end2->next = begin1; |
925 | 0 | begin1->prev = end2; |
926 | 0 | begin2->prev = end1; |
927 | 0 | return list1; |
928 | 0 | } |
929 | 0 | } |
930 | | |
931 | | /* Add a CERTNameConstraint to the CERTNameConstraint list */ |
932 | | CERTNameConstraint * |
933 | | CERT_AddNameConstraint(CERTNameConstraint *list, CERTNameConstraint *constraint) |
934 | 0 | { |
935 | 0 | PORT_Assert(constraint != NULL); |
936 | 0 | constraint->l.next = constraint->l.prev = &constraint->l; |
937 | 0 | list = cert_CombineConstraintsLists(list, constraint); |
938 | 0 | return list; |
939 | 0 | } |
940 | | |
941 | | SECStatus |
942 | | CERT_GetNameConstraintByType(CERTNameConstraint *constraints, |
943 | | CERTGeneralNameType type, |
944 | | CERTNameConstraint **returnList, |
945 | | PLArenaPool *arena) |
946 | 0 | { |
947 | 0 | CERTNameConstraint *current = NULL; |
948 | 0 | void *mark = NULL; |
949 | |
|
950 | 0 | *returnList = NULL; |
951 | 0 | if (!constraints) |
952 | 0 | return SECSuccess; |
953 | | |
954 | 0 | mark = PORT_ArenaMark(arena); |
955 | |
|
956 | 0 | current = constraints; |
957 | 0 | do { |
958 | 0 | PORT_Assert(current->name.type); |
959 | 0 | if (current->name.type == type) { |
960 | 0 | CERTNameConstraint *temp; |
961 | 0 | temp = CERT_CopyNameConstraint(arena, NULL, current); |
962 | 0 | if (temp == NULL) |
963 | 0 | goto loser; |
964 | 0 | *returnList = CERT_AddNameConstraint(*returnList, temp); |
965 | 0 | } |
966 | 0 | current = CERT_GetNextNameConstraint(current); |
967 | 0 | } while (current != constraints); |
968 | 0 | PORT_ArenaUnmark(arena, mark); |
969 | 0 | return SECSuccess; |
970 | | |
971 | 0 | loser: |
972 | 0 | PORT_ArenaRelease(arena, mark); |
973 | 0 | return SECFailure; |
974 | 0 | } |
975 | | |
976 | | void * |
977 | | CERT_GetGeneralNameByType(CERTGeneralName *genNames, CERTGeneralNameType type, |
978 | | PRBool derFormat) |
979 | 111 | { |
980 | 111 | CERTGeneralName *current; |
981 | | |
982 | 111 | if (!genNames) |
983 | 0 | return NULL; |
984 | 111 | current = genNames; |
985 | | |
986 | 153 | do { |
987 | 153 | if (current->type == type) { |
988 | 79 | switch (type) { |
989 | 0 | case certDNSName: |
990 | 0 | case certEDIPartyName: |
991 | 0 | case certIPAddress: |
992 | 0 | case certRegisterID: |
993 | 0 | case certRFC822Name: |
994 | 0 | case certX400Address: |
995 | 0 | case certURI: |
996 | 0 | return (void *)¤t->name.other; /* SECItem * */ |
997 | | |
998 | 0 | case certOtherName: |
999 | 0 | return (void *)¤t->name.OthName; /* OthName * */ |
1000 | | |
1001 | 79 | case certDirectoryName: |
1002 | 79 | return derFormat |
1003 | 79 | ? (void *)¤t |
1004 | 79 | ->derDirectoryName /* SECItem * */ |
1005 | 79 | : (void *)¤t->name |
1006 | 0 | .directoryName; /* CERTName * */ |
1007 | 79 | } |
1008 | 0 | PORT_Assert(0); |
1009 | 0 | return NULL; |
1010 | 79 | } |
1011 | 74 | current = CERT_GetNextGeneralName(current); |
1012 | 74 | } while (current != genNames); |
1013 | 32 | return NULL; |
1014 | 111 | } |
1015 | | |
1016 | | int |
1017 | | CERT_GetNamesLength(CERTGeneralName *names) |
1018 | 6.39k | { |
1019 | 6.39k | int length = 0; |
1020 | 6.39k | CERTGeneralName *first; |
1021 | | |
1022 | 6.39k | first = names; |
1023 | 6.39k | if (names != NULL) { |
1024 | 10.3k | do { |
1025 | 10.3k | length++; |
1026 | 10.3k | names = CERT_GetNextGeneralName(names); |
1027 | 10.3k | } while (names != first); |
1028 | 6.39k | } |
1029 | 6.39k | return length; |
1030 | 6.39k | } |
1031 | | |
1032 | | /* Creates new GeneralNames for any email addresses found in the |
1033 | | ** input DN, and links them onto the list for the DN. |
1034 | | */ |
1035 | | SECStatus |
1036 | | cert_ExtractDNEmailAddrs(CERTGeneralName *name, PLArenaPool *arena) |
1037 | 6.44k | { |
1038 | 6.44k | CERTGeneralName *nameList = NULL; |
1039 | 6.44k | const CERTRDN **nRDNs = (const CERTRDN **)(name->name.directoryName.rdns); |
1040 | 6.44k | SECStatus rv = SECSuccess; |
1041 | | |
1042 | 6.44k | PORT_Assert(name->type == certDirectoryName); |
1043 | 6.44k | if (name->type != certDirectoryName) { |
1044 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1045 | 0 | return SECFailure; |
1046 | 0 | } |
1047 | | /* TODO: mark arena */ |
1048 | 20.9k | while (nRDNs && *nRDNs) { /* loop over RDNs */ |
1049 | 14.5k | const CERTRDN *nRDN = *nRDNs++; |
1050 | 14.5k | CERTAVA **nAVAs = nRDN->avas; |
1051 | 28.7k | while (nAVAs && *nAVAs) { /* loop over AVAs */ |
1052 | 14.2k | int tag; |
1053 | 14.2k | CERTAVA *nAVA = *nAVAs++; |
1054 | 14.2k | tag = CERT_GetAVATag(nAVA); |
1055 | 14.2k | if (tag == SEC_OID_PKCS9_EMAIL_ADDRESS || |
1056 | 14.2k | tag == SEC_OID_RFC1274_MAIL) { /* email AVA */ |
1057 | 248 | CERTGeneralName *newName = NULL; |
1058 | 248 | SECItem *avaValue = CERT_DecodeAVAValue(&nAVA->value); |
1059 | 248 | if (!avaValue) |
1060 | 56 | goto loser; |
1061 | 192 | rv = SECFailure; |
1062 | 192 | newName = CERT_NewGeneralName(arena, certRFC822Name); |
1063 | 192 | if (newName) { |
1064 | 192 | rv = |
1065 | 192 | SECITEM_CopyItem(arena, &newName->name.other, avaValue); |
1066 | 192 | } |
1067 | 192 | SECITEM_FreeItem(avaValue, PR_TRUE); |
1068 | 192 | if (rv != SECSuccess) |
1069 | 0 | goto loser; |
1070 | 192 | nameList = cert_CombineNamesLists(nameList, newName); |
1071 | 192 | } /* handle one email AVA */ |
1072 | 14.2k | } /* loop over AVAs */ |
1073 | 14.5k | } /* loop over RDNs */ |
1074 | | /* combine new names with old one. */ |
1075 | 6.39k | (void)cert_CombineNamesLists(name, nameList); |
1076 | | /* TODO: unmark arena */ |
1077 | 6.39k | return SECSuccess; |
1078 | | |
1079 | 56 | loser: |
1080 | | /* TODO: release arena back to mark */ |
1081 | 56 | return SECFailure; |
1082 | 6.44k | } |
1083 | | |
1084 | | /* Extract all names except Subject Common Name from a cert |
1085 | | ** in preparation for a name constraints test. |
1086 | | */ |
1087 | | CERTGeneralName * |
1088 | | CERT_GetCertificateNames(CERTCertificate *cert, PLArenaPool *arena) |
1089 | 0 | { |
1090 | 0 | return CERT_GetConstrainedCertificateNames(cert, arena, PR_FALSE); |
1091 | 0 | } |
1092 | | |
1093 | | /* This function is called by CERT_VerifyCertChain to extract all |
1094 | | ** names from a cert in preparation for a name constraints test. |
1095 | | */ |
1096 | | CERTGeneralName * |
1097 | | CERT_GetConstrainedCertificateNames(const CERTCertificate *cert, |
1098 | | PLArenaPool *arena, |
1099 | | PRBool includeSubjectCommonName) |
1100 | 6.44k | { |
1101 | 6.44k | CERTGeneralName *DN; |
1102 | 6.44k | CERTGeneralName *SAN; |
1103 | 6.44k | PRUint32 numDNSNames = 0; |
1104 | 6.44k | SECStatus rv; |
1105 | | |
1106 | 6.44k | if (!arena) { |
1107 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1108 | 0 | return NULL; |
1109 | 0 | } |
1110 | | /* TODO: mark arena */ |
1111 | 6.44k | DN = CERT_NewGeneralName(arena, certDirectoryName); |
1112 | 6.44k | if (DN == NULL) { |
1113 | 0 | goto loser; |
1114 | 0 | } |
1115 | 6.44k | rv = CERT_CopyName(arena, &DN->name.directoryName, &cert->subject); |
1116 | 6.44k | if (rv != SECSuccess) { |
1117 | 0 | goto loser; |
1118 | 0 | } |
1119 | 6.44k | rv = SECITEM_CopyItem(arena, &DN->derDirectoryName, &cert->derSubject); |
1120 | 6.44k | if (rv != SECSuccess) { |
1121 | 0 | goto loser; |
1122 | 0 | } |
1123 | | /* Extract email addresses from DN, construct CERTGeneralName structs |
1124 | | ** for them, add them to the name list |
1125 | | */ |
1126 | 6.44k | rv = cert_ExtractDNEmailAddrs(DN, arena); |
1127 | 6.44k | if (rv != SECSuccess) |
1128 | 56 | goto loser; |
1129 | | |
1130 | | /* Now extract any GeneralNames from the subject name names extension. */ |
1131 | 6.39k | SAN = cert_GetSubjectAltNameList(cert, arena); |
1132 | 6.39k | if (SAN) { |
1133 | 221 | numDNSNames = cert_CountDNSPatterns(SAN); |
1134 | 221 | DN = cert_CombineNamesLists(DN, SAN); |
1135 | 221 | } |
1136 | 6.39k | if (!numDNSNames && includeSubjectCommonName) { |
1137 | 803 | char *cn = CERT_GetCommonName(&cert->subject); |
1138 | 803 | if (cn) { |
1139 | 597 | CERTGeneralName *CN = CERT_NewGeneralName(arena, certDNSName); |
1140 | 597 | if (CN) { |
1141 | 597 | SECItem cnItem = { siBuffer, NULL, 0 }; |
1142 | 597 | cnItem.data = (unsigned char *)cn; |
1143 | 597 | cnItem.len = strlen(cn); |
1144 | 597 | rv = SECITEM_CopyItem(arena, &CN->name.other, &cnItem); |
1145 | 597 | if (rv == SECSuccess) { |
1146 | 597 | DN = cert_CombineNamesLists(DN, CN); |
1147 | 597 | } |
1148 | 597 | } |
1149 | 597 | PORT_Free(cn); |
1150 | 597 | } |
1151 | 803 | } |
1152 | 6.39k | if (rv == SECSuccess) { |
1153 | | /* TODO: unmark arena */ |
1154 | 6.39k | return DN; |
1155 | 6.39k | } |
1156 | 56 | loser: |
1157 | | /* TODO: release arena to mark */ |
1158 | 56 | return NULL; |
1159 | 6.39k | } |
1160 | | |
1161 | | /* Returns SECSuccess if name matches constraint per RFC 3280 rules for |
1162 | | ** URI name constraints. SECFailure otherwise. |
1163 | | ** If the constraint begins with a dot, it is a domain name, otherwise |
1164 | | ** It is a host name. Examples: |
1165 | | ** Constraint Name Result |
1166 | | ** ------------ --------------- -------- |
1167 | | ** foo.bar.com foo.bar.com matches |
1168 | | ** foo.bar.com FoO.bAr.CoM matches |
1169 | | ** foo.bar.com www.foo.bar.com no match |
1170 | | ** foo.bar.com nofoo.bar.com no match |
1171 | | ** .foo.bar.com www.foo.bar.com matches |
1172 | | ** .foo.bar.com nofoo.bar.com no match |
1173 | | ** .foo.bar.com foo.bar.com no match |
1174 | | ** .foo.bar.com www..foo.bar.com no match |
1175 | | */ |
1176 | | static SECStatus |
1177 | | compareURIN2C(const SECItem *name, const SECItem *constraint) |
1178 | 0 | { |
1179 | 0 | int offset; |
1180 | | /* The spec is silent on intepreting zero-length constraints. |
1181 | | ** We interpret them as matching no URI names. |
1182 | | */ |
1183 | 0 | if (!constraint->len) |
1184 | 0 | return SECFailure; |
1185 | 0 | if (constraint->data[0] != '.') { |
1186 | | /* constraint is a host name. */ |
1187 | 0 | if (name->len != constraint->len || |
1188 | 0 | PL_strncasecmp((char *)name->data, (char *)constraint->data, |
1189 | 0 | constraint->len)) |
1190 | 0 | return SECFailure; |
1191 | 0 | return SECSuccess; |
1192 | 0 | } |
1193 | | /* constraint is a domain name. */ |
1194 | 0 | if (name->len < constraint->len) |
1195 | 0 | return SECFailure; |
1196 | 0 | offset = name->len - constraint->len; |
1197 | 0 | if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, |
1198 | 0 | constraint->len)) |
1199 | 0 | return SECFailure; |
1200 | 0 | if (!offset || |
1201 | 0 | (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) |
1202 | 0 | return SECSuccess; |
1203 | 0 | return SECFailure; |
1204 | 0 | } |
1205 | | |
1206 | | /* for DNSname constraints, RFC 3280 says, (section 4.2.1.11, page 38) |
1207 | | ** |
1208 | | ** DNS name restrictions are expressed as foo.bar.com. Any DNS name |
1209 | | ** that can be constructed by simply adding to the left hand side of the |
1210 | | ** name satisfies the name constraint. For example, www.foo.bar.com |
1211 | | ** would satisfy the constraint but foo1.bar.com would not. |
1212 | | ** |
1213 | | ** But NIST's PKITS test suite requires that the constraint be treated |
1214 | | ** as a domain name, and requires that any name added to the left hand |
1215 | | ** side end in a dot ".". Sensible, but not strictly following the RFC. |
1216 | | ** |
1217 | | ** Constraint Name RFC 3280 NIST PKITS |
1218 | | ** ------------ --------------- -------- ---------- |
1219 | | ** foo.bar.com foo.bar.com matches matches |
1220 | | ** foo.bar.com FoO.bAr.CoM matches matches |
1221 | | ** foo.bar.com www.foo.bar.com matches matches |
1222 | | ** foo.bar.com nofoo.bar.com MATCHES NO MATCH |
1223 | | ** .foo.bar.com www.foo.bar.com matches matches? disallowed? |
1224 | | ** .foo.bar.com foo.bar.com no match no match |
1225 | | ** .foo.bar.com www..foo.bar.com matches probably not |
1226 | | ** |
1227 | | ** We will try to conform to NIST's PKITS tests, and the unstated |
1228 | | ** rules they imply. |
1229 | | */ |
1230 | | static SECStatus |
1231 | | compareDNSN2C(const SECItem *name, const SECItem *constraint) |
1232 | 0 | { |
1233 | 0 | int offset; |
1234 | | /* The spec is silent on intepreting zero-length constraints. |
1235 | | ** We interpret them as matching all DNSnames. |
1236 | | */ |
1237 | 0 | if (!constraint->len) |
1238 | 0 | return SECSuccess; |
1239 | 0 | if (name->len < constraint->len) |
1240 | 0 | return SECFailure; |
1241 | 0 | offset = name->len - constraint->len; |
1242 | 0 | if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, |
1243 | 0 | constraint->len)) |
1244 | 0 | return SECFailure; |
1245 | 0 | if (!offset || |
1246 | 0 | (name->data[offset - 1] == '.') + (constraint->data[0] == '.') == 1) |
1247 | 0 | return SECSuccess; |
1248 | 0 | return SECFailure; |
1249 | 0 | } |
1250 | | |
1251 | | /* Returns SECSuccess if name matches constraint per RFC 3280 rules for |
1252 | | ** internet email addresses. SECFailure otherwise. |
1253 | | ** If constraint contains a '@' then the two strings much match exactly. |
1254 | | ** Else if constraint starts with a '.'. then it must match the right-most |
1255 | | ** substring of the name, |
1256 | | ** else constraint string must match entire name after the name's '@'. |
1257 | | ** Empty constraint string matches all names. All comparisons case insensitive. |
1258 | | */ |
1259 | | static SECStatus |
1260 | | compareRFC822N2C(const SECItem *name, const SECItem *constraint) |
1261 | 0 | { |
1262 | 0 | int offset; |
1263 | 0 | if (!constraint->len) |
1264 | 0 | return SECSuccess; |
1265 | 0 | if (name->len < constraint->len) |
1266 | 0 | return SECFailure; |
1267 | 0 | if (constraint->len == 1 && constraint->data[0] == '.') |
1268 | 0 | return SECSuccess; |
1269 | 0 | for (offset = constraint->len - 1; offset >= 0; --offset) { |
1270 | 0 | if (constraint->data[offset] == '@') { |
1271 | 0 | return (name->len == constraint->len && |
1272 | 0 | !PL_strncasecmp((char *)name->data, |
1273 | 0 | (char *)constraint->data, constraint->len)) |
1274 | 0 | ? SECSuccess |
1275 | 0 | : SECFailure; |
1276 | 0 | } |
1277 | 0 | } |
1278 | 0 | offset = name->len - constraint->len; |
1279 | 0 | if (PL_strncasecmp((char *)(name->data + offset), (char *)constraint->data, |
1280 | 0 | constraint->len)) |
1281 | 0 | return SECFailure; |
1282 | 0 | if (constraint->data[0] == '.') |
1283 | 0 | return SECSuccess; |
1284 | 0 | if (offset > 0 && name->data[offset - 1] == '@') |
1285 | 0 | return SECSuccess; |
1286 | 0 | return SECFailure; |
1287 | 0 | } |
1288 | | |
1289 | | /* name contains either a 4 byte IPv4 address or a 16 byte IPv6 address. |
1290 | | ** constraint contains an address of the same length, and a subnet mask |
1291 | | ** of the same length. Compare name's address to the constraint's |
1292 | | ** address, subject to the mask. |
1293 | | ** Return SECSuccess if they match, SECFailure if they don't. |
1294 | | */ |
1295 | | static SECStatus |
1296 | | compareIPaddrN2C(const SECItem *name, const SECItem *constraint) |
1297 | 0 | { |
1298 | 0 | int i; |
1299 | 0 | if (name->len == 4 && constraint->len == 8) { /* ipv4 addr */ |
1300 | 0 | for (i = 0; i < 4; i++) { |
1301 | 0 | if ((name->data[i] ^ constraint->data[i]) & constraint->data[i + 4]) |
1302 | 0 | goto loser; |
1303 | 0 | } |
1304 | 0 | return SECSuccess; |
1305 | 0 | } |
1306 | 0 | if (name->len == 16 && constraint->len == 32) { /* ipv6 addr */ |
1307 | 0 | for (i = 0; i < 16; i++) { |
1308 | 0 | if ((name->data[i] ^ constraint->data[i]) & |
1309 | 0 | constraint->data[i + 16]) |
1310 | 0 | goto loser; |
1311 | 0 | } |
1312 | 0 | return SECSuccess; |
1313 | 0 | } |
1314 | 0 | loser: |
1315 | 0 | return SECFailure; |
1316 | 0 | } |
1317 | | |
1318 | | /* start with a SECItem that points to a URI. Parse it lookingg for |
1319 | | ** a hostname. Modify item->data and item->len to define the hostname, |
1320 | | ** but do not modify and data at item->data. |
1321 | | ** If anything goes wrong, the contents of *item are undefined. |
1322 | | */ |
1323 | | static SECStatus |
1324 | | parseUriHostname(SECItem *item) |
1325 | 0 | { |
1326 | 0 | int i; |
1327 | 0 | PRBool found = PR_FALSE; |
1328 | 0 | for (i = 0; (unsigned)(i + 2) < item->len; ++i) { |
1329 | 0 | if (item->data[i] == ':' && item->data[i + 1] == '/' && |
1330 | 0 | item->data[i + 2] == '/') { |
1331 | 0 | i += 3; |
1332 | 0 | item->data += i; |
1333 | 0 | item->len -= i; |
1334 | 0 | found = PR_TRUE; |
1335 | 0 | break; |
1336 | 0 | } |
1337 | 0 | } |
1338 | 0 | if (!found) |
1339 | 0 | return SECFailure; |
1340 | | /* now look for a '/', which is an upper bound in the end of the name */ |
1341 | 0 | for (i = 0; (unsigned)i < item->len; ++i) { |
1342 | 0 | if (item->data[i] == '/') { |
1343 | 0 | item->len = i; |
1344 | 0 | break; |
1345 | 0 | } |
1346 | 0 | } |
1347 | | /* now look for a ':', which marks the end of the name */ |
1348 | 0 | for (i = item->len; --i >= 0;) { |
1349 | 0 | if (item->data[i] == ':') { |
1350 | 0 | item->len = i; |
1351 | 0 | break; |
1352 | 0 | } |
1353 | 0 | } |
1354 | | /* now look for an '@', which marks the beginning of the hostname */ |
1355 | 0 | for (i = 0; (unsigned)i < item->len; ++i) { |
1356 | 0 | if (item->data[i] == '@') { |
1357 | 0 | ++i; |
1358 | 0 | item->data += i; |
1359 | 0 | item->len -= i; |
1360 | 0 | break; |
1361 | 0 | } |
1362 | 0 | } |
1363 | 0 | return item->len ? SECSuccess : SECFailure; |
1364 | 0 | } |
1365 | | |
1366 | | /* This function takes one name, and a list of constraints. |
1367 | | ** It searches the constraints looking for a match. |
1368 | | ** It returns SECSuccess if the name satisfies the constraints, i.e., |
1369 | | ** if excluded, then the name does not match any constraint, |
1370 | | ** if permitted, then the name matches at least one constraint. |
1371 | | ** It returns SECFailure if the name fails to satisfy the constraints, |
1372 | | ** or if some code fails (e.g. out of memory, or invalid constraint) |
1373 | | */ |
1374 | | SECStatus |
1375 | | cert_CompareNameWithConstraints(const CERTGeneralName *name, |
1376 | | const CERTNameConstraint *constraints, |
1377 | | PRBool excluded) |
1378 | 0 | { |
1379 | 0 | SECStatus rv = SECSuccess; |
1380 | 0 | SECStatus matched = SECFailure; |
1381 | 0 | const CERTNameConstraint *current; |
1382 | |
|
1383 | 0 | PORT_Assert(constraints); /* caller should not call with NULL */ |
1384 | 0 | if (!constraints) { |
1385 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1386 | 0 | return SECFailure; |
1387 | 0 | } |
1388 | | |
1389 | 0 | current = constraints; |
1390 | 0 | do { |
1391 | 0 | rv = SECSuccess; |
1392 | 0 | matched = SECFailure; |
1393 | 0 | PORT_Assert(name->type == current->name.type); |
1394 | 0 | switch (name->type) { |
1395 | | |
1396 | 0 | case certDNSName: |
1397 | 0 | matched = |
1398 | 0 | compareDNSN2C(&name->name.other, ¤t->name.name.other); |
1399 | 0 | break; |
1400 | | |
1401 | 0 | case certRFC822Name: |
1402 | 0 | matched = compareRFC822N2C(&name->name.other, |
1403 | 0 | ¤t->name.name.other); |
1404 | 0 | break; |
1405 | | |
1406 | 0 | case certURI: { |
1407 | | /* make a modifiable copy of the URI SECItem. */ |
1408 | 0 | SECItem uri = name->name.other; |
1409 | | /* find the hostname in the URI */ |
1410 | 0 | rv = parseUriHostname(&uri); |
1411 | 0 | if (rv == SECSuccess) { |
1412 | | /* does our hostname meet the constraint? */ |
1413 | 0 | matched = compareURIN2C(&uri, ¤t->name.name.other); |
1414 | 0 | } |
1415 | 0 | } break; |
1416 | | |
1417 | 0 | case certDirectoryName: |
1418 | | /* Determine if the constraint directory name is a "prefix" |
1419 | | ** for the directory name being tested. |
1420 | | */ |
1421 | 0 | { |
1422 | | /* status defaults to SECEqual, so that a constraint with |
1423 | | ** no AVAs will be a wildcard, matching all directory names. |
1424 | | */ |
1425 | 0 | SECComparison status = SECEqual; |
1426 | 0 | const CERTRDN **cRDNs = |
1427 | 0 | (const CERTRDN **)current->name.name.directoryName.rdns; |
1428 | 0 | const CERTRDN **nRDNs = |
1429 | 0 | (const CERTRDN **)name->name.directoryName.rdns; |
1430 | 0 | while (cRDNs && *cRDNs && nRDNs && *nRDNs) { |
1431 | | /* loop over name RDNs and constraint RDNs in lock step |
1432 | | */ |
1433 | 0 | const CERTRDN *cRDN = *cRDNs++; |
1434 | 0 | const CERTRDN *nRDN = *nRDNs++; |
1435 | 0 | CERTAVA **cAVAs = cRDN->avas; |
1436 | 0 | while (cAVAs && |
1437 | 0 | *cAVAs) { /* loop over constraint AVAs */ |
1438 | 0 | CERTAVA *cAVA = *cAVAs++; |
1439 | 0 | CERTAVA **nAVAs = nRDN->avas; |
1440 | 0 | while (nAVAs && *nAVAs) { /* loop over name AVAs */ |
1441 | 0 | CERTAVA *nAVA = *nAVAs++; |
1442 | 0 | status = CERT_CompareAVA(cAVA, nAVA); |
1443 | 0 | if (status == SECEqual) |
1444 | 0 | break; |
1445 | 0 | } /* loop over name AVAs */ |
1446 | 0 | if (status != SECEqual) |
1447 | 0 | break; |
1448 | 0 | } /* loop over constraint AVAs */ |
1449 | 0 | if (status != SECEqual) |
1450 | 0 | break; |
1451 | 0 | } /* loop over name RDNs and constraint RDNs */ |
1452 | 0 | matched = (status == SECEqual) ? SECSuccess : SECFailure; |
1453 | 0 | break; |
1454 | 0 | } |
1455 | | |
1456 | 0 | case certIPAddress: /* type 8 */ |
1457 | 0 | matched = compareIPaddrN2C(&name->name.other, |
1458 | 0 | ¤t->name.name.other); |
1459 | 0 | break; |
1460 | | |
1461 | | /* NSS does not know how to compare these "Other" type names with |
1462 | | ** their respective constraints. But it does know how to tell |
1463 | | ** if the constraint applies to the type of name (by comparing |
1464 | | ** the constraint OID to the name OID). NSS makes no use of "Other" |
1465 | | ** type names at all, so NSS errs on the side of leniency for these |
1466 | | ** types, provided that their OIDs match. So, when an "Other" |
1467 | | ** name constraint appears in an excluded subtree, it never causes |
1468 | | ** a name to fail. When an "Other" name constraint appears in a |
1469 | | ** permitted subtree, AND the constraint's OID matches the name's |
1470 | | ** OID, then name is treated as if it matches the constraint. |
1471 | | */ |
1472 | 0 | case certOtherName: /* type 1 */ |
1473 | 0 | matched = |
1474 | 0 | (!excluded && name->type == current->name.type && |
1475 | 0 | SECITEM_ItemsAreEqual(&name->name.OthName.oid, |
1476 | 0 | ¤t->name.name.OthName.oid)) |
1477 | 0 | ? SECSuccess |
1478 | 0 | : SECFailure; |
1479 | 0 | break; |
1480 | | |
1481 | | /* NSS does not know how to compare these types of names with their |
1482 | | ** respective constraints. But NSS makes no use of these types of |
1483 | | ** names at all, so it errs on the side of leniency for these types. |
1484 | | ** Constraints for these types of names never cause the name to |
1485 | | ** fail the constraints test. NSS behaves as if the name matched |
1486 | | ** for permitted constraints, and did not match for excluded ones. |
1487 | | */ |
1488 | 0 | case certX400Address: /* type 4 */ |
1489 | 0 | case certEDIPartyName: /* type 6 */ |
1490 | 0 | case certRegisterID: /* type 9 */ |
1491 | 0 | matched = excluded ? SECFailure : SECSuccess; |
1492 | 0 | break; |
1493 | | |
1494 | 0 | default: /* non-standard types are not supported */ |
1495 | 0 | rv = SECFailure; |
1496 | 0 | break; |
1497 | 0 | } |
1498 | 0 | if (matched == SECSuccess || rv != SECSuccess) |
1499 | 0 | break; |
1500 | 0 | current = CERT_GetNextNameConstraint((CERTNameConstraint *)current); |
1501 | 0 | } while (current != constraints); |
1502 | 0 | if (rv == SECSuccess) { |
1503 | 0 | if (matched == SECSuccess) |
1504 | 0 | rv = excluded ? SECFailure : SECSuccess; |
1505 | 0 | else |
1506 | 0 | rv = excluded ? SECSuccess : SECFailure; |
1507 | 0 | return rv; |
1508 | 0 | } |
1509 | | |
1510 | 0 | return SECFailure; |
1511 | 0 | } |
1512 | | |
1513 | | /* Add and link a CERTGeneralName to a CERTNameConstraint list. Most |
1514 | | ** likely the CERTNameConstraint passed in is either the permitted |
1515 | | ** list or the excluded list of a CERTNameConstraints. |
1516 | | */ |
1517 | | SECStatus |
1518 | | CERT_AddNameConstraintByGeneralName(PLArenaPool *arena, |
1519 | | CERTNameConstraint **constraints, |
1520 | | CERTGeneralName *name) |
1521 | 0 | { |
1522 | 0 | SECStatus rv; |
1523 | 0 | CERTNameConstraint *current = NULL; |
1524 | 0 | CERTNameConstraint *first = *constraints; |
1525 | 0 | void *mark = NULL; |
1526 | |
|
1527 | 0 | mark = PORT_ArenaMark(arena); |
1528 | |
|
1529 | 0 | current = PORT_ArenaZNew(arena, CERTNameConstraint); |
1530 | 0 | if (current == NULL) { |
1531 | 0 | rv = SECFailure; |
1532 | 0 | goto done; |
1533 | 0 | } |
1534 | | |
1535 | 0 | rv = cert_CopyOneGeneralName(arena, ¤t->name, name); |
1536 | 0 | if (rv != SECSuccess) { |
1537 | 0 | goto done; |
1538 | 0 | } |
1539 | | |
1540 | 0 | current->name.l.prev = current->name.l.next = &(current->name.l); |
1541 | |
|
1542 | 0 | if (first == NULL) { |
1543 | 0 | *constraints = current; |
1544 | 0 | PR_INIT_CLIST(¤t->l); |
1545 | 0 | } else { |
1546 | 0 | PR_INSERT_BEFORE(¤t->l, &first->l); |
1547 | 0 | } |
1548 | |
|
1549 | 0 | done: |
1550 | 0 | if (rv == SECFailure) { |
1551 | 0 | PORT_ArenaRelease(arena, mark); |
1552 | 0 | } else { |
1553 | 0 | PORT_ArenaUnmark(arena, mark); |
1554 | 0 | } |
1555 | 0 | return rv; |
1556 | 0 | } |
1557 | | |
1558 | | /* |
1559 | | * Here we define a list of name constraints to be imposed on |
1560 | | * certain certificates, most importantly root certificates. |
1561 | | * |
1562 | | * Each entry in the name constraints list is constructed with this |
1563 | | * macro. An entry contains two SECItems, which have names in |
1564 | | * specific forms to make the macro work: |
1565 | | * |
1566 | | * * ${CA}_SUBJECT_DN - The subject DN for which the constraints |
1567 | | * should be applied |
1568 | | * * ${CA}_NAME_CONSTRAINTS - The name constraints extension |
1569 | | * |
1570 | | * Entities subject to name constraints are identified by subject name |
1571 | | * so that we can cover all certificates for that entity, including, e.g., |
1572 | | * cross-certificates. We use subject rather than public key because |
1573 | | * calling methods often have easy access to that field (vs., say, a key ID), |
1574 | | * and in practice, subject names and public keys are usually in one-to-one |
1575 | | * correspondence anyway. |
1576 | | * |
1577 | | */ |
1578 | | |
1579 | | #define STRING_TO_SECITEM(str) \ |
1580 | | { \ |
1581 | | siBuffer, (unsigned char *)str, sizeof(str) - 1 \ |
1582 | | } |
1583 | | |
1584 | | #define NAME_CONSTRAINTS_ENTRY(CA) \ |
1585 | | { \ |
1586 | | STRING_TO_SECITEM(CA##_SUBJECT_DN) \ |
1587 | | , \ |
1588 | | STRING_TO_SECITEM(CA##_NAME_CONSTRAINTS) \ |
1589 | | } |
1590 | | |
1591 | | /* clang-format off */ |
1592 | | |
1593 | | /* Agence Nationale de la Securite des Systemes d'Information (ANSSI) */ |
1594 | | |
1595 | | #define ANSSI_SUBJECT_DN \ |
1596 | | "\x30\x81\x85" \ |
1597 | | "\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02" "FR" /* C */ \ |
1598 | | "\x31\x0F\x30\x0D\x06\x03\x55\x04\x08\x13\x06" "France" /* ST */ \ |
1599 | | "\x31\x0E\x30\x0C\x06\x03\x55\x04\x07\x13\x05" "Paris" /* L */ \ |
1600 | | "\x31\x10\x30\x0E\x06\x03\x55\x04\x0A\x13\x07" "PM/SGDN" /* O */ \ |
1601 | | "\x31\x0E\x30\x0C\x06\x03\x55\x04\x0B\x13\x05" "DCSSI" /* OU */ \ |
1602 | | "\x31\x0E\x30\x0C\x06\x03\x55\x04\x03\x13\x05" "IGC/A" /* CN */ \ |
1603 | | "\x31\x23\x30\x21\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01" \ |
1604 | | "\x16\x14" "igca@sgdn.pm.gouv.fr" /* emailAddress */ \ |
1605 | | |
1606 | | #define ANSSI_NAME_CONSTRAINTS \ |
1607 | | "\x30\x5D\xA0\x5B" \ |
1608 | | "\x30\x05\x82\x03" ".fr" \ |
1609 | | "\x30\x05\x82\x03" ".gp" \ |
1610 | | "\x30\x05\x82\x03" ".gf" \ |
1611 | | "\x30\x05\x82\x03" ".mq" \ |
1612 | | "\x30\x05\x82\x03" ".re" \ |
1613 | | "\x30\x05\x82\x03" ".yt" \ |
1614 | | "\x30\x05\x82\x03" ".pm" \ |
1615 | | "\x30\x05\x82\x03" ".bl" \ |
1616 | | "\x30\x05\x82\x03" ".mf" \ |
1617 | | "\x30\x05\x82\x03" ".wf" \ |
1618 | | "\x30\x05\x82\x03" ".pf" \ |
1619 | | "\x30\x05\x82\x03" ".nc" \ |
1620 | | "\x30\x05\x82\x03" ".tf" |
1621 | | |
1622 | | /* TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 */ |
1623 | | |
1624 | | #define TUBITAK1_SUBJECT_DN \ |
1625 | | "\x30\x81\xd2" \ |
1626 | | "\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02" \ |
1627 | | /* C */ "TR" \ |
1628 | | "\x31\x18\x30\x16\x06\x03\x55\x04\x07\x13\x0f" \ |
1629 | | /* L */ "Gebze - Kocaeli" \ |
1630 | | "\x31\x42\x30\x40\x06\x03\x55\x04\x0a\x13\x39" \ |
1631 | | /* O */ "Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK" \ |
1632 | | "\x31\x2d\x30\x2b\x06\x03\x55\x04\x0b\x13\x24" \ |
1633 | | /* OU */ "Kamu Sertifikasyon Merkezi - Kamu SM" \ |
1634 | | "\x31\x36\x30\x34\x06\x03\x55\x04\x03\x13\x2d" \ |
1635 | | /* CN */ "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" |
1636 | | |
1637 | | #define TUBITAK1_NAME_CONSTRAINTS \ |
1638 | | "\x30\x09\xa0\x07" \ |
1639 | | "\x30\x05\x82\x03" ".tr" |
1640 | | |
1641 | | /* clang-format on */ |
1642 | | |
1643 | | static const SECItem builtInNameConstraints[][2] = { |
1644 | | NAME_CONSTRAINTS_ENTRY(ANSSI), |
1645 | | NAME_CONSTRAINTS_ENTRY(TUBITAK1) |
1646 | | }; |
1647 | | |
1648 | | SECStatus |
1649 | | CERT_GetImposedNameConstraints(const SECItem *derSubject, SECItem *extensions) |
1650 | 413 | { |
1651 | 413 | size_t i; |
1652 | | |
1653 | 413 | if (!extensions) { |
1654 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1655 | 0 | return SECFailure; |
1656 | 0 | } |
1657 | | |
1658 | 1.23k | for (i = 0; i < PR_ARRAY_SIZE(builtInNameConstraints); ++i) { |
1659 | 826 | if (SECITEM_ItemsAreEqual(derSubject, &builtInNameConstraints[i][0])) { |
1660 | 0 | return SECITEM_CopyItem(NULL, extensions, |
1661 | 0 | &builtInNameConstraints[i][1]); |
1662 | 0 | } |
1663 | 826 | } |
1664 | | |
1665 | 413 | PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND); |
1666 | 413 | return SECFailure; |
1667 | 413 | } |
1668 | | |
1669 | | /* |
1670 | | * Extract the name constraints extension from the CA cert. |
1671 | | * If the certificate contains no name constraints extension, but |
1672 | | * CERT_GetImposedNameConstraints returns a name constraints extension |
1673 | | * for the subject of the certificate, then that extension will be returned. |
1674 | | */ |
1675 | | SECStatus |
1676 | | CERT_FindNameConstraintsExten(PLArenaPool *arena, CERTCertificate *cert, |
1677 | | CERTNameConstraints **constraints) |
1678 | 429 | { |
1679 | 429 | SECStatus rv = SECSuccess; |
1680 | 429 | SECItem constraintsExtension; |
1681 | 429 | void *mark = NULL; |
1682 | | |
1683 | 429 | *constraints = NULL; |
1684 | | |
1685 | 429 | rv = CERT_FindCertExtension(cert, SEC_OID_X509_NAME_CONSTRAINTS, |
1686 | 429 | &constraintsExtension); |
1687 | 429 | if (rv != SECSuccess) { |
1688 | 413 | if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) { |
1689 | 0 | return rv; |
1690 | 0 | } |
1691 | 413 | rv = CERT_GetImposedNameConstraints(&cert->derSubject, |
1692 | 413 | &constraintsExtension); |
1693 | 413 | if (rv != SECSuccess) { |
1694 | 413 | if (PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND) { |
1695 | 413 | return SECSuccess; |
1696 | 413 | } |
1697 | 0 | return rv; |
1698 | 413 | } |
1699 | 413 | } |
1700 | | |
1701 | 16 | mark = PORT_ArenaMark(arena); |
1702 | | |
1703 | 16 | *constraints = cert_DecodeNameConstraints(arena, &constraintsExtension); |
1704 | 16 | if (*constraints == NULL) { /* decode failed */ |
1705 | 16 | rv = SECFailure; |
1706 | 16 | } |
1707 | 16 | PORT_Free(constraintsExtension.data); |
1708 | | |
1709 | 16 | if (rv == SECFailure) { |
1710 | 16 | PORT_ArenaRelease(arena, mark); |
1711 | 16 | } else { |
1712 | 0 | PORT_ArenaUnmark(arena, mark); |
1713 | 0 | } |
1714 | | |
1715 | 16 | return rv; |
1716 | 429 | } |
1717 | | |
1718 | | /* Verify name against all the constraints relevant to that type of |
1719 | | ** the name. |
1720 | | */ |
1721 | | SECStatus |
1722 | | CERT_CheckNameSpace(PLArenaPool *arena, const CERTNameConstraints *constraints, |
1723 | | const CERTGeneralName *currentName) |
1724 | 0 | { |
1725 | 0 | CERTNameConstraint *matchingConstraints; |
1726 | 0 | SECStatus rv = SECSuccess; |
1727 | |
|
1728 | 0 | if (constraints->excluded != NULL) { |
1729 | 0 | rv = CERT_GetNameConstraintByType(constraints->excluded, |
1730 | 0 | currentName->type, |
1731 | 0 | &matchingConstraints, arena); |
1732 | 0 | if (rv == SECSuccess && matchingConstraints != NULL) { |
1733 | 0 | rv = cert_CompareNameWithConstraints(currentName, |
1734 | 0 | matchingConstraints, PR_TRUE); |
1735 | 0 | } |
1736 | 0 | if (rv != SECSuccess) { |
1737 | 0 | return (rv); |
1738 | 0 | } |
1739 | 0 | } |
1740 | | |
1741 | 0 | if (constraints->permited != NULL) { |
1742 | 0 | rv = CERT_GetNameConstraintByType(constraints->permited, |
1743 | 0 | currentName->type, |
1744 | 0 | &matchingConstraints, arena); |
1745 | 0 | if (rv == SECSuccess && matchingConstraints != NULL) { |
1746 | 0 | rv = cert_CompareNameWithConstraints(currentName, |
1747 | 0 | matchingConstraints, PR_FALSE); |
1748 | 0 | } |
1749 | 0 | if (rv != SECSuccess) { |
1750 | 0 | return (rv); |
1751 | 0 | } |
1752 | 0 | } |
1753 | | |
1754 | 0 | return (SECSuccess); |
1755 | 0 | } |
1756 | | |
1757 | | /* Extract the name constraints extension from the CA cert. |
1758 | | ** Test each and every name in namesList against all the constraints |
1759 | | ** relevant to that type of name. |
1760 | | ** Returns NULL in pBadCert for success, if all names are acceptable. |
1761 | | ** If some name is not acceptable, returns a pointer to the cert that |
1762 | | ** contained that name. |
1763 | | */ |
1764 | | SECStatus |
1765 | | CERT_CompareNameSpace(CERTCertificate *cert, CERTGeneralName *namesList, |
1766 | | CERTCertificate **certsList, PLArenaPool *reqArena, |
1767 | | CERTCertificate **pBadCert) |
1768 | 429 | { |
1769 | 429 | SECStatus rv = SECSuccess; |
1770 | 429 | CERTNameConstraints *constraints; |
1771 | 429 | CERTGeneralName *currentName; |
1772 | 429 | int count = 0; |
1773 | 429 | CERTCertificate *badCert = NULL; |
1774 | | |
1775 | | /* If no names to check, then no names can be bad. */ |
1776 | 429 | if (!namesList) |
1777 | 0 | goto done; |
1778 | 429 | rv = CERT_FindNameConstraintsExten(reqArena, cert, &constraints); |
1779 | 429 | if (rv != SECSuccess) { |
1780 | 16 | count = -1; |
1781 | 16 | goto done; |
1782 | 16 | } |
1783 | | |
1784 | 413 | currentName = namesList; |
1785 | 528 | do { |
1786 | 528 | if (constraints) { |
1787 | 0 | rv = CERT_CheckNameSpace(reqArena, constraints, currentName); |
1788 | 0 | if (rv != SECSuccess) { |
1789 | 0 | break; |
1790 | 0 | } |
1791 | 0 | } |
1792 | 528 | currentName = CERT_GetNextGeneralName(currentName); |
1793 | 528 | count++; |
1794 | 528 | } while (currentName != namesList); |
1795 | | |
1796 | 429 | done: |
1797 | 429 | if (rv != SECSuccess) { |
1798 | 16 | badCert = (count >= 0) ? certsList[count] : cert; |
1799 | 16 | } |
1800 | 429 | if (pBadCert) |
1801 | 429 | *pBadCert = badCert; |
1802 | | |
1803 | 429 | return rv; |
1804 | 413 | } |
1805 | | |
1806 | | #if 0 |
1807 | | /* not exported from shared libs, not used. Turn on if we ever need it. */ |
1808 | | SECStatus |
1809 | | CERT_CompareGeneralName(CERTGeneralName *a, CERTGeneralName *b) |
1810 | | { |
1811 | | CERTGeneralName *currentA; |
1812 | | CERTGeneralName *currentB; |
1813 | | PRBool found; |
1814 | | |
1815 | | currentA = a; |
1816 | | currentB = b; |
1817 | | if (a != NULL) { |
1818 | | do { |
1819 | | if (currentB == NULL) { |
1820 | | return SECFailure; |
1821 | | } |
1822 | | currentB = CERT_GetNextGeneralName(currentB); |
1823 | | currentA = CERT_GetNextGeneralName(currentA); |
1824 | | } while (currentA != a); |
1825 | | } |
1826 | | if (currentB != b) { |
1827 | | return SECFailure; |
1828 | | } |
1829 | | currentA = a; |
1830 | | do { |
1831 | | currentB = b; |
1832 | | found = PR_FALSE; |
1833 | | do { |
1834 | | if (currentB->type == currentA->type) { |
1835 | | switch (currentB->type) { |
1836 | | case certDNSName: |
1837 | | case certEDIPartyName: |
1838 | | case certIPAddress: |
1839 | | case certRegisterID: |
1840 | | case certRFC822Name: |
1841 | | case certX400Address: |
1842 | | case certURI: |
1843 | | if (SECITEM_CompareItem(¤tA->name.other, |
1844 | | ¤tB->name.other) |
1845 | | == SECEqual) { |
1846 | | found = PR_TRUE; |
1847 | | } |
1848 | | break; |
1849 | | case certOtherName: |
1850 | | if (SECITEM_CompareItem(¤tA->name.OthName.oid, |
1851 | | ¤tB->name.OthName.oid) |
1852 | | == SECEqual && |
1853 | | SECITEM_CompareItem(¤tA->name.OthName.name, |
1854 | | ¤tB->name.OthName.name) |
1855 | | == SECEqual) { |
1856 | | found = PR_TRUE; |
1857 | | } |
1858 | | break; |
1859 | | case certDirectoryName: |
1860 | | if (CERT_CompareName(¤tA->name.directoryName, |
1861 | | ¤tB->name.directoryName) |
1862 | | == SECEqual) { |
1863 | | found = PR_TRUE; |
1864 | | } |
1865 | | } |
1866 | | |
1867 | | } |
1868 | | currentB = CERT_GetNextGeneralName(currentB); |
1869 | | } while (currentB != b && found != PR_TRUE); |
1870 | | if (found != PR_TRUE) { |
1871 | | return SECFailure; |
1872 | | } |
1873 | | currentA = CERT_GetNextGeneralName(currentA); |
1874 | | } while (currentA != a); |
1875 | | return SECSuccess; |
1876 | | } |
1877 | | |
1878 | | SECStatus |
1879 | | CERT_CompareGeneralNameLists(CERTGeneralNameList *a, CERTGeneralNameList *b) |
1880 | | { |
1881 | | SECStatus rv; |
1882 | | |
1883 | | if (a == b) { |
1884 | | return SECSuccess; |
1885 | | } |
1886 | | if (a != NULL && b != NULL) { |
1887 | | PZ_Lock(a->lock); |
1888 | | PZ_Lock(b->lock); |
1889 | | rv = CERT_CompareGeneralName(a->name, b->name); |
1890 | | PZ_Unlock(a->lock); |
1891 | | PZ_Unlock(b->lock); |
1892 | | } else { |
1893 | | rv = SECFailure; |
1894 | | } |
1895 | | return rv; |
1896 | | } |
1897 | | #endif |
1898 | | |
1899 | | #if 0 |
1900 | | /* This function is not exported from NSS shared libraries, and is not |
1901 | | ** used inside of NSS. |
1902 | | ** XXX it doesn't check for failed allocations. :-( |
1903 | | */ |
1904 | | void * |
1905 | | CERT_GetGeneralNameFromListByType(CERTGeneralNameList *list, |
1906 | | CERTGeneralNameType type, |
1907 | | PLArenaPool *arena) |
1908 | | { |
1909 | | CERTName *name = NULL; |
1910 | | SECItem *item = NULL; |
1911 | | OtherName *other = NULL; |
1912 | | OtherName *tmpOther = NULL; |
1913 | | void *data; |
1914 | | |
1915 | | PZ_Lock(list->lock); |
1916 | | data = CERT_GetGeneralNameByType(list->name, type, PR_FALSE); |
1917 | | if (data != NULL) { |
1918 | | switch (type) { |
1919 | | case certDNSName: |
1920 | | case certEDIPartyName: |
1921 | | case certIPAddress: |
1922 | | case certRegisterID: |
1923 | | case certRFC822Name: |
1924 | | case certX400Address: |
1925 | | case certURI: |
1926 | | if (arena != NULL) { |
1927 | | item = PORT_ArenaNew(arena, SECItem); |
1928 | | if (item != NULL) { |
1929 | | XXX SECITEM_CopyItem(arena, item, (SECItem *) data); |
1930 | | } |
1931 | | } else { |
1932 | | item = SECITEM_DupItem((SECItem *) data); |
1933 | | } |
1934 | | PZ_Unlock(list->lock); |
1935 | | return item; |
1936 | | case certOtherName: |
1937 | | other = (OtherName *) data; |
1938 | | if (arena != NULL) { |
1939 | | tmpOther = PORT_ArenaNew(arena, OtherName); |
1940 | | } else { |
1941 | | tmpOther = PORT_New(OtherName); |
1942 | | } |
1943 | | if (tmpOther != NULL) { |
1944 | | XXX SECITEM_CopyItem(arena, &tmpOther->oid, &other->oid); |
1945 | | XXX SECITEM_CopyItem(arena, &tmpOther->name, &other->name); |
1946 | | } |
1947 | | PZ_Unlock(list->lock); |
1948 | | return tmpOther; |
1949 | | case certDirectoryName: |
1950 | | if (arena) { |
1951 | | name = PORT_ArenaZNew(list->arena, CERTName); |
1952 | | if (name) { |
1953 | | XXX CERT_CopyName(arena, name, (CERTName *) data); |
1954 | | } |
1955 | | } |
1956 | | PZ_Unlock(list->lock); |
1957 | | return name; |
1958 | | } |
1959 | | } |
1960 | | PZ_Unlock(list->lock); |
1961 | | return NULL; |
1962 | | } |
1963 | | #endif |
1964 | | |
1965 | | #if 0 |
1966 | | /* This function is not exported from NSS shared libraries, and is not |
1967 | | ** used inside of NSS. |
1968 | | ** XXX it should NOT be a void function, since it does allocations |
1969 | | ** that can fail. |
1970 | | */ |
1971 | | void |
1972 | | CERT_AddGeneralNameToList(CERTGeneralNameList *list, |
1973 | | CERTGeneralNameType type, |
1974 | | void *data, SECItem *oid) |
1975 | | { |
1976 | | CERTGeneralName *name; |
1977 | | |
1978 | | if (list != NULL && data != NULL) { |
1979 | | PZ_Lock(list->lock); |
1980 | | name = CERT_NewGeneralName(list->arena, type); |
1981 | | if (!name) |
1982 | | goto done; |
1983 | | switch (type) { |
1984 | | case certDNSName: |
1985 | | case certEDIPartyName: |
1986 | | case certIPAddress: |
1987 | | case certRegisterID: |
1988 | | case certRFC822Name: |
1989 | | case certX400Address: |
1990 | | case certURI: |
1991 | | XXX SECITEM_CopyItem(list->arena, &name->name.other, (SECItem *)data); |
1992 | | break; |
1993 | | case certOtherName: |
1994 | | XXX SECITEM_CopyItem(list->arena, &name->name.OthName.name, |
1995 | | (SECItem *) data); |
1996 | | XXX SECITEM_CopyItem(list->arena, &name->name.OthName.oid, |
1997 | | oid); |
1998 | | break; |
1999 | | case certDirectoryName: |
2000 | | XXX CERT_CopyName(list->arena, &name->name.directoryName, |
2001 | | (CERTName *) data); |
2002 | | break; |
2003 | | } |
2004 | | list->name = cert_CombineNamesLists(list->name, name); |
2005 | | list->len++; |
2006 | | done: |
2007 | | PZ_Unlock(list->lock); |
2008 | | } |
2009 | | return; |
2010 | | } |
2011 | | #endif |