certread.c (14972B)
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 "cert.h" 6 #include "base64.h" 7 #include "secitem.h" 8 #include "secder.h" 9 #include "secasn1.h" 10 #include "secoid.h" 11 #include "secerr.h" 12 13 SEC_ASN1_MKSUB(SEC_AnyTemplate) 14 SEC_ASN1_MKSUB(SEC_SetOfAnyTemplate) 15 16 typedef struct ContentInfoStr ContentInfo; 17 typedef struct DegenerateSignedDataStr DegenerateSignedData; 18 19 struct ContentInfoStr { 20 SECOidTag contentTypeTag; /* local; not part of encoding */ 21 SECItem contentType; 22 union { 23 SECItem *data; 24 DegenerateSignedData *signedData; 25 } content; 26 }; 27 28 struct DegenerateSignedDataStr { 29 SECItem version; 30 SECItem **digestAlgorithms; 31 ContentInfo contentInfo; 32 SECItem **certificates; 33 SECItem **crls; 34 SECItem **signerInfos; 35 }; 36 37 static const SEC_ASN1Template * 38 choose_content_template(void *src_or_dest, PRBool encoding); 39 40 static const SEC_ASN1TemplateChooserPtr template_chooser = choose_content_template; 41 42 static const SEC_ASN1Template ContentInfoTemplate[] = { 43 { SEC_ASN1_SEQUENCE, 44 0, NULL, sizeof(ContentInfo) }, 45 { SEC_ASN1_OBJECT_ID, 46 offsetof(ContentInfo, contentType) }, 47 { SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | 48 SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, 49 offsetof(ContentInfo, content), 50 &template_chooser }, 51 { 0 } 52 }; 53 54 static const SEC_ASN1Template DegenerateSignedDataTemplate[] = { 55 { SEC_ASN1_SEQUENCE, 56 0, NULL, sizeof(DegenerateSignedData) }, 57 { SEC_ASN1_INTEGER, 58 offsetof(DegenerateSignedData, version) }, 59 { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, 60 offsetof(DegenerateSignedData, digestAlgorithms), 61 SEC_ASN1_SUB(SEC_AnyTemplate) }, 62 { SEC_ASN1_INLINE, 63 offsetof(DegenerateSignedData, contentInfo), 64 ContentInfoTemplate }, 65 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 66 SEC_ASN1_XTRN | 0, 67 offsetof(DegenerateSignedData, certificates), 68 SEC_ASN1_SUB(SEC_SetOfAnyTemplate) }, 69 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 70 SEC_ASN1_XTRN | 1, 71 offsetof(DegenerateSignedData, crls), 72 SEC_ASN1_SUB(SEC_SetOfAnyTemplate) }, 73 { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, 74 offsetof(DegenerateSignedData, signerInfos), 75 SEC_ASN1_SUB(SEC_AnyTemplate) }, 76 { 0 } 77 }; 78 79 static const SEC_ASN1Template PointerToDegenerateSignedDataTemplate[] = { 80 { SEC_ASN1_POINTER, 0, DegenerateSignedDataTemplate } 81 }; 82 83 static SECOidTag 84 GetContentTypeTag(ContentInfo *cinfo) 85 { 86 if (cinfo->contentTypeTag == SEC_OID_UNKNOWN) 87 cinfo->contentTypeTag = SECOID_FindOIDTag(&cinfo->contentType); 88 return cinfo->contentTypeTag; 89 } 90 91 static const SEC_ASN1Template * 92 choose_content_template(void *src_or_dest, PRBool encoding) 93 { 94 const SEC_ASN1Template *theTemplate; 95 ContentInfo *cinfo; 96 SECOidTag kind; 97 98 PORT_Assert(src_or_dest != NULL); 99 if (src_or_dest == NULL) 100 return NULL; 101 102 cinfo = (ContentInfo *)src_or_dest; 103 kind = GetContentTypeTag(cinfo); 104 switch (kind) { 105 default: 106 theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate); 107 break; 108 case SEC_OID_PKCS7_DATA: 109 theTemplate = SEC_ASN1_GET(SEC_PointerToOctetStringTemplate); 110 break; 111 case SEC_OID_PKCS7_SIGNED_DATA: 112 theTemplate = PointerToDegenerateSignedDataTemplate; 113 break; 114 } 115 return theTemplate; 116 } 117 118 static SECStatus 119 SEC_ReadPKCS7Certs(SECItem *pkcs7Item, CERTImportCertificateFunc f, void *arg) 120 { 121 ContentInfo contentInfo; 122 SECStatus rv = SECFailure; 123 SECItem **certs; 124 int count; 125 PLArenaPool *arena; 126 127 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 128 if (arena == NULL) { 129 return rv; 130 } 131 132 PORT_Memset(&contentInfo, 0, sizeof(contentInfo)); 133 if (SEC_ASN1DecodeItem(arena, &contentInfo, ContentInfoTemplate, 134 pkcs7Item) != SECSuccess) { 135 goto done; 136 } 137 138 if (GetContentTypeTag(&contentInfo) != SEC_OID_PKCS7_SIGNED_DATA) { 139 goto done; 140 } 141 142 if (contentInfo.content.signedData == NULL) { 143 PORT_SetError(SEC_ERROR_BAD_DER); 144 goto done; 145 } 146 147 rv = SECSuccess; 148 149 certs = contentInfo.content.signedData->certificates; 150 if (certs) { 151 count = 0; 152 153 while (*certs) { 154 count++; 155 certs++; 156 } 157 rv = (*f)(arg, contentInfo.content.signedData->certificates, count); 158 } 159 160 done: 161 if (arena) { 162 PORT_FreeArena(arena, PR_FALSE); 163 } 164 165 return rv; 166 } 167 168 const SEC_ASN1Template SEC_CertSequenceTemplate[] = { 169 { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN, 0, SEC_ASN1_SUB(SEC_AnyTemplate) } 170 }; 171 172 static SECStatus 173 SEC_ReadCertSequence(SECItem *certsItem, CERTImportCertificateFunc f, void *arg) 174 { 175 SECStatus rv = SECFailure; 176 SECItem **certs; 177 int count; 178 SECItem **rawCerts = NULL; 179 PLArenaPool *arena; 180 ContentInfo contentInfo; 181 182 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 183 if (arena == NULL) { 184 return rv; 185 } 186 187 PORT_Memset(&contentInfo, 0, sizeof(contentInfo)); 188 if (SEC_ASN1DecodeItem(arena, &contentInfo, ContentInfoTemplate, 189 certsItem) != SECSuccess) { 190 goto done; 191 } 192 193 if (GetContentTypeTag(&contentInfo) != SEC_OID_NS_TYPE_CERT_SEQUENCE) { 194 goto done; 195 } 196 197 if (SEC_QuickDERDecodeItem(arena, &rawCerts, SEC_CertSequenceTemplate, 198 contentInfo.content.data) != SECSuccess) { 199 goto done; 200 } 201 202 rv = SECSuccess; 203 204 certs = rawCerts; 205 if (certs) { 206 count = 0; 207 208 while (*certs) { 209 count++; 210 certs++; 211 } 212 rv = (*f)(arg, rawCerts, count); 213 } 214 215 done: 216 if (arena) { 217 PORT_FreeArena(arena, PR_FALSE); 218 } 219 220 return rv; 221 } 222 223 CERTCertificate * 224 CERT_ConvertAndDecodeCertificate(char *certstr) 225 { 226 CERTCertificate *cert; 227 SECStatus rv; 228 SECItem der; 229 230 rv = ATOB_ConvertAsciiToItem(&der, certstr); 231 if (rv != SECSuccess) 232 return NULL; 233 234 cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), 235 &der, NULL, PR_FALSE, PR_TRUE); 236 237 PORT_Free(der.data); 238 return cert; 239 } 240 241 static const char NS_CERT_HEADER[] = "-----BEGIN CERTIFICATE-----"; 242 static const char NS_CERT_TRAILER[] = "-----END CERTIFICATE-----"; 243 #define NS_CERT_HEADER_LEN ((sizeof NS_CERT_HEADER) - 1) 244 #define NS_CERT_TRAILER_LEN ((sizeof NS_CERT_TRAILER) - 1) 245 246 /* 247 * read an old style ascii or binary certificate chain 248 */ 249 SECStatus 250 CERT_DecodeCertPackage(char *certbuf, 251 int certlen, 252 CERTImportCertificateFunc f, 253 void *arg) 254 { 255 unsigned char *cp; 256 unsigned char *bincert = NULL; 257 char *ascCert = NULL; 258 SECStatus rv; 259 260 if (certbuf == NULL) { 261 PORT_SetError(SEC_ERROR_INVALID_ARGS); 262 return (SECFailure); 263 } 264 /* 265 * Make sure certlen is long enough to handle the longest possible 266 * reference in the code below: 267 * 0x30 0x84 l1 l2 l3 l4 + 268 * tag 9 o1 o2 o3 o4 o5 o6 o7 o8 o9 269 * where 9 is the longest length of the expected oids we are testing. 270 * 6 + 11 = 17. 17 bytes is clearly too small to code any kind of 271 * certificate (a 128 bit ECC certificate contains at least an 8 byte 272 * key and a 16 byte signature, plus coding overhead). Typically a cert 273 * is much larger. So it's safe to require certlen to be at least 17 274 * bytes. 275 */ 276 if (certlen < 17) { 277 PORT_SetError(SEC_ERROR_INPUT_LEN); 278 return (SECFailure); 279 } 280 281 cp = (unsigned char *)certbuf; 282 283 /* is a DER encoded certificate of some type? */ 284 if ((*cp & 0x1f) == SEC_ASN1_SEQUENCE) { 285 SECItem certitem; 286 SECItem *pcertitem = &certitem; 287 PRUint64 seqLen, seqLenLen; 288 289 cp++; 290 291 if (*cp & 0x80) { 292 /* Multibyte length */ 293 seqLenLen = cp[0] & 0x7f; 294 295 switch (seqLenLen) { 296 case 4: 297 seqLen = ((unsigned long)cp[1] << 24) | 298 ((unsigned long)cp[2] << 16) | (cp[3] << 8) | cp[4]; 299 break; 300 case 3: 301 seqLen = ((unsigned long)cp[1] << 16) | (cp[2] << 8) | cp[3]; 302 break; 303 case 2: 304 seqLen = (cp[1] << 8) | cp[2]; 305 break; 306 case 1: 307 seqLen = cp[1]; 308 break; 309 case 0: 310 /* indefinite length */ 311 seqLen = 0; 312 break; 313 default: 314 goto notder; 315 } 316 cp += (seqLenLen + 1); 317 318 } else { 319 seqLenLen = 0; 320 seqLen = *cp; 321 cp++; 322 } 323 324 /* check entire length if definite length */ 325 if (seqLen || seqLenLen) { 326 if (certlen != (seqLen + seqLenLen + 2L)) { 327 if (certlen > (seqLen + seqLenLen + 2L)) { 328 PORT_SetError(SEC_ERROR_EXTRA_INPUT); 329 } else { 330 PORT_SetError(SEC_ERROR_INPUT_LEN); 331 } 332 goto notder; 333 } 334 } 335 336 /* check the type oid */ 337 if (cp[0] == SEC_ASN1_OBJECT_ID) { 338 SECOidData *oiddata; 339 SECItem oiditem; 340 /* XXX - assume DER encoding of OID len!! */ 341 oiditem.len = cp[1]; 342 /* if we add an oid below that is longer than 9 bytes, then we 343 * need to change the certlen check at the top of the function 344 * to prevent a buffer overflow 345 */ 346 if (oiditem.len > 9) { 347 PORT_SetError(SEC_ERROR_UNRECOGNIZED_OID); 348 return (SECFailure); 349 } 350 oiditem.data = (unsigned char *)&cp[2]; 351 oiddata = SECOID_FindOID(&oiditem); 352 if (oiddata == NULL) { 353 return (SECFailure); 354 } 355 356 certitem.data = (unsigned char *)certbuf; 357 certitem.len = certlen; 358 359 switch (oiddata->offset) { 360 case SEC_OID_PKCS7_SIGNED_DATA: 361 /* oid: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 */ 362 return (SEC_ReadPKCS7Certs(&certitem, f, arg)); 363 break; 364 case SEC_OID_NS_TYPE_CERT_SEQUENCE: 365 /* oid: 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x02, 0x05 */ 366 return (SEC_ReadCertSequence(&certitem, f, arg)); 367 break; 368 default: 369 break; 370 } 371 372 } else { 373 /* it had better be a certificate by now!! */ 374 certitem.data = (unsigned char *)certbuf; 375 certitem.len = certlen; 376 377 rv = (*f)(arg, &pcertitem, 1); 378 return (rv); 379 } 380 } 381 382 /* now look for a netscape base64 ascii encoded cert */ 383 notder : { 384 unsigned char *certbegin = NULL; 385 unsigned char *certend = NULL; 386 char *pc; 387 int cl; 388 389 /* Convert the ASCII data into a nul-terminated string */ 390 ascCert = (char *)PORT_Alloc(certlen + 1); 391 if (!ascCert) { 392 rv = SECFailure; 393 goto loser; 394 } 395 396 PORT_Memcpy(ascCert, certbuf, certlen); 397 ascCert[certlen] = '\0'; 398 399 pc = PORT_Strchr(ascCert, '\n'); /* find an EOL */ 400 if (!pc) { /* maybe this is a MAC file */ 401 pc = ascCert; 402 while (*pc && NULL != (pc = PORT_Strchr(pc, '\r'))) { 403 *pc++ = '\n'; 404 } 405 } 406 407 cp = (unsigned char *)ascCert; 408 cl = certlen; 409 410 /* find the beginning marker */ 411 while (cl > NS_CERT_HEADER_LEN) { 412 int found = 0; 413 if (!PORT_Strncasecmp((char *)cp, NS_CERT_HEADER, 414 NS_CERT_HEADER_LEN)) { 415 cl -= NS_CERT_HEADER_LEN; 416 cp += NS_CERT_HEADER_LEN; 417 found = 1; 418 } 419 420 /* skip to next eol */ 421 while (cl && (*cp != '\n')) { 422 cp++; 423 cl--; 424 } 425 426 /* skip all blank lines */ 427 while (cl && (*cp == '\n' || *cp == '\r')) { 428 cp++; 429 cl--; 430 } 431 if (cl && found) { 432 certbegin = cp; 433 break; 434 } 435 } 436 437 if (certbegin) { 438 /* find the ending marker */ 439 while (cl >= NS_CERT_TRAILER_LEN) { 440 if (!PORT_Strncasecmp((char *)cp, NS_CERT_TRAILER, 441 NS_CERT_TRAILER_LEN)) { 442 certend = cp; 443 break; 444 } 445 446 /* skip to next eol */ 447 while (cl && (*cp != '\n')) { 448 cp++; 449 cl--; 450 } 451 452 /* skip all blank lines */ 453 while (cl && (*cp == '\n' || *cp == '\r')) { 454 cp++; 455 cl--; 456 } 457 } 458 } 459 460 if (certbegin && certend) { 461 unsigned int binLen; 462 463 *certend = 0; 464 /* convert to binary */ 465 bincert = ATOB_AsciiToData((char *)certbegin, &binLen); 466 if (!bincert) { 467 rv = SECFailure; 468 goto loser; 469 } 470 471 /* now recurse to decode the binary */ 472 rv = CERT_DecodeCertPackage((char *)bincert, binLen, f, arg); 473 474 } else { 475 PORT_SetError(SEC_ERROR_BAD_DER); 476 rv = SECFailure; 477 } 478 } 479 480 loser: 481 482 if (bincert) { 483 PORT_Free(bincert); 484 } 485 486 if (ascCert) { 487 PORT_Free(ascCert); 488 } 489 490 return (rv); 491 } 492 493 typedef struct { 494 PLArenaPool *arena; 495 SECItem cert; 496 } collect_args; 497 498 static SECStatus 499 collect_certs(void *arg, SECItem **certs, int numcerts) 500 { 501 collect_args *collectArgs = (collect_args *)arg; 502 if (!collectArgs || !collectArgs->arena) { 503 PORT_SetError(SEC_ERROR_INVALID_ARGS); 504 return SECFailure; 505 } 506 if (numcerts < 1 || !certs || !*certs) { 507 PORT_SetError(SEC_ERROR_BAD_DER); 508 return SECFailure; 509 } 510 return SECITEM_CopyItem(collectArgs->arena, &collectArgs->cert, *certs); 511 } 512 513 /* 514 * read an old style ascii or binary certificate 515 */ 516 CERTCertificate * 517 CERT_DecodeCertFromPackage(char *certbuf, int certlen) 518 { 519 collect_args collectArgs; 520 SECStatus rv; 521 CERTCertificate *cert = NULL; 522 523 collectArgs.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); 524 collectArgs.cert.data = NULL; 525 collectArgs.cert.len = 0; 526 527 rv = CERT_DecodeCertPackage(certbuf, certlen, collect_certs, 528 (void *)&collectArgs); 529 if (rv == SECSuccess) { 530 cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), 531 &collectArgs.cert, NULL, 532 PR_FALSE, PR_TRUE); 533 } 534 535 PORT_FreeArena(collectArgs.arena, PR_FALSE); 536 537 return (cert); 538 }