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