1*bb0e26feStb /* $OpenBSD: mft.c,v 1.121 2024/12/24 10:03:59 tb Exp $ */ 29a7e9e7fSjob /* 3740e9a54Stb * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> 49a7e9e7fSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 59a7e9e7fSjob * 69a7e9e7fSjob * Permission to use, copy, modify, and distribute this software for any 79a7e9e7fSjob * purpose with or without fee is hereby granted, provided that the above 89a7e9e7fSjob * copyright notice and this permission notice appear in all copies. 99a7e9e7fSjob * 109a7e9e7fSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 119a7e9e7fSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 129a7e9e7fSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 139a7e9e7fSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 149a7e9e7fSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 159a7e9e7fSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 169a7e9e7fSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 179a7e9e7fSjob */ 189a7e9e7fSjob 199a7e9e7fSjob #include <assert.h> 209a7e9e7fSjob #include <err.h> 21fa2ce094Sclaudio #include <limits.h> 229a7e9e7fSjob #include <stdint.h> 239a7e9e7fSjob #include <stdlib.h> 249a7e9e7fSjob #include <string.h> 259a7e9e7fSjob #include <unistd.h> 269a7e9e7fSjob 27ab5c69fdSjob #include <openssl/bn.h> 28a1753de6Sclaudio #include <openssl/asn1.h> 2923e50b68Stb #include <openssl/asn1t.h> 3023e50b68Stb #include <openssl/safestack.h> 3196932cf3Sderaadt #include <openssl/sha.h> 3223e50b68Stb #include <openssl/stack.h> 33a1753de6Sclaudio #include <openssl/x509.h> 349a7e9e7fSjob 359a7e9e7fSjob #include "extern.h" 369a7e9e7fSjob 37de9b6f5dSclaudio extern ASN1_OBJECT *mft_oid; 38318f0572Sjob BN_CTX *bn_ctx; 39d2e465bbSclaudio 409a7e9e7fSjob /* 4123e50b68Stb * Types and templates for the Manifest eContent, RFC 6486, section 4.2. 4223e50b68Stb */ 4323e50b68Stb 44d3d26873Sjob ASN1_ITEM_EXP FileAndHash_it; 45d3d26873Sjob ASN1_ITEM_EXP Manifest_it; 46d3d26873Sjob 4723e50b68Stb typedef struct { 4823e50b68Stb ASN1_IA5STRING *file; 4923e50b68Stb ASN1_BIT_STRING *hash; 5023e50b68Stb } FileAndHash; 5123e50b68Stb 5223e50b68Stb DECLARE_STACK_OF(FileAndHash); 5323e50b68Stb 549395df6fStb #ifndef DEFINE_STACK_OF 5558c75743Stb #define sk_FileAndHash_dup(sk) SKM_sk_dup(FileAndHash, (sk)) 5658c75743Stb #define sk_FileAndHash_free(sk) SKM_sk_free(FileAndHash, (sk)) 5723e50b68Stb #define sk_FileAndHash_num(sk) SKM_sk_num(FileAndHash, (sk)) 5823e50b68Stb #define sk_FileAndHash_value(sk, i) SKM_sk_value(FileAndHash, (sk), (i)) 5958c75743Stb #define sk_FileAndHash_sort(sk) SKM_sk_sort(FileAndHash, (sk)) 6058c75743Stb #define sk_FileAndHash_set_cmp_func(sk, cmp) \ 6158c75743Stb SKM_sk_set_cmp_func(FileAndHash, (sk), (cmp)) 6223e50b68Stb #endif 6323e50b68Stb 6423e50b68Stb typedef struct { 6523e50b68Stb ASN1_INTEGER *version; 6623e50b68Stb ASN1_INTEGER *manifestNumber; 6723e50b68Stb ASN1_GENERALIZEDTIME *thisUpdate; 6823e50b68Stb ASN1_GENERALIZEDTIME *nextUpdate; 6923e50b68Stb ASN1_OBJECT *fileHashAlg; 7023e50b68Stb STACK_OF(FileAndHash) *fileList; 7123e50b68Stb } Manifest; 7223e50b68Stb 7323e50b68Stb ASN1_SEQUENCE(FileAndHash) = { 7423e50b68Stb ASN1_SIMPLE(FileAndHash, file, ASN1_IA5STRING), 7523e50b68Stb ASN1_SIMPLE(FileAndHash, hash, ASN1_BIT_STRING), 7623e50b68Stb } ASN1_SEQUENCE_END(FileAndHash); 7723e50b68Stb 7823e50b68Stb ASN1_SEQUENCE(Manifest) = { 79bb9f70d1Stb ASN1_EXP_OPT(Manifest, version, ASN1_INTEGER, 0), 8023e50b68Stb ASN1_SIMPLE(Manifest, manifestNumber, ASN1_INTEGER), 8123e50b68Stb ASN1_SIMPLE(Manifest, thisUpdate, ASN1_GENERALIZEDTIME), 8223e50b68Stb ASN1_SIMPLE(Manifest, nextUpdate, ASN1_GENERALIZEDTIME), 8323e50b68Stb ASN1_SIMPLE(Manifest, fileHashAlg, ASN1_OBJECT), 8423e50b68Stb ASN1_SEQUENCE_OF(Manifest, fileList, FileAndHash), 8523e50b68Stb } ASN1_SEQUENCE_END(Manifest); 8623e50b68Stb 8723e50b68Stb DECLARE_ASN1_FUNCTIONS(Manifest); 8823e50b68Stb IMPLEMENT_ASN1_FUNCTIONS(Manifest); 8923e50b68Stb 908a0e7acdStb #define GENTIME_LENGTH 15 918a0e7acdStb 9223e50b68Stb /* 93043caafeStb * Determine rtype corresponding to file extension. Returns RTYPE_INVALID 94f452fe1cStb * on error or unknown extension. 95043caafeStb */ 96043caafeStb enum rtype 97043caafeStb rtype_from_file_extension(const char *fn) 98043caafeStb { 99043caafeStb size_t sz; 100043caafeStb 101043caafeStb sz = strlen(fn); 102043caafeStb if (sz < 5) 103043caafeStb return RTYPE_INVALID; 104043caafeStb 105043caafeStb if (strcasecmp(fn + sz - 4, ".tal") == 0) 106043caafeStb return RTYPE_TAL; 107043caafeStb if (strcasecmp(fn + sz - 4, ".cer") == 0) 108043caafeStb return RTYPE_CER; 109043caafeStb if (strcasecmp(fn + sz - 4, ".crl") == 0) 110043caafeStb return RTYPE_CRL; 111043caafeStb if (strcasecmp(fn + sz - 4, ".mft") == 0) 112043caafeStb return RTYPE_MFT; 113043caafeStb if (strcasecmp(fn + sz - 4, ".roa") == 0) 114043caafeStb return RTYPE_ROA; 115043caafeStb if (strcasecmp(fn + sz - 4, ".gbr") == 0) 116043caafeStb return RTYPE_GBR; 11704834fbdSjob if (strcasecmp(fn + sz - 4, ".sig") == 0) 11804834fbdSjob return RTYPE_RSC; 119a29ddfd5Sjob if (strcasecmp(fn + sz - 4, ".asa") == 0) 120a29ddfd5Sjob return RTYPE_ASPA; 121ee2a33daSjob if (strcasecmp(fn + sz - 4, ".tak") == 0) 122ee2a33daSjob return RTYPE_TAK; 123ef3f6f56Sjob if (strcasecmp(fn + sz - 4, ".csv") == 0) 124ef3f6f56Sjob return RTYPE_GEOFEED; 125d4be4cdeSjob if (strcasecmp(fn + sz - 4, ".spl") == 0) 126d4be4cdeSjob return RTYPE_SPL; 127043caafeStb 128043caafeStb return RTYPE_INVALID; 129043caafeStb } 130043caafeStb 131043caafeStb /* 132043caafeStb * Validate that a filename listed on a Manifest only contains characters 133c7a965b3Stb * permitted in RFC 9286 section 4.2.2. 134203dfefcStb * Also ensure that there is exactly one '.'. 135043caafeStb */ 136685326f5Sclaudio static int 137203dfefcStb valid_mft_filename(const char *fn, size_t len) 138043caafeStb { 139043caafeStb const unsigned char *c; 140043caafeStb 141203dfefcStb if (!valid_filename(fn, len)) 142685326f5Sclaudio return 0; 143043caafeStb 144685326f5Sclaudio c = memchr(fn, '.', len); 145685326f5Sclaudio if (c == NULL || c != memrchr(fn, '.', len)) 146685326f5Sclaudio return 0; 147685326f5Sclaudio 148685326f5Sclaudio return 1; 149685326f5Sclaudio } 150685326f5Sclaudio 151685326f5Sclaudio /* 152a7b03bb4Sclaudio * Check that the file is allowed to be part of a manifest and the parser 153a7b03bb4Sclaudio * for this type is implemented in rpki-client. 154a7b03bb4Sclaudio * Returns corresponding rtype or RTYPE_INVALID to mark the file as unknown. 155685326f5Sclaudio */ 156685326f5Sclaudio static enum rtype 157685326f5Sclaudio rtype_from_mftfile(const char *fn) 158685326f5Sclaudio { 159685326f5Sclaudio enum rtype type; 160043caafeStb 161043caafeStb type = rtype_from_file_extension(fn); 162043caafeStb switch (type) { 163043caafeStb case RTYPE_CER: 164043caafeStb case RTYPE_CRL: 165043caafeStb case RTYPE_GBR: 166043caafeStb case RTYPE_ROA: 167a29ddfd5Sjob case RTYPE_ASPA: 168d4be4cdeSjob case RTYPE_SPL: 169ee2a33daSjob case RTYPE_TAK: 170043caafeStb return type; 171043caafeStb default: 172043caafeStb return RTYPE_INVALID; 173043caafeStb } 174043caafeStb } 175043caafeStb 176043caafeStb /* 1779a7e9e7fSjob * Parse an individual "FileAndHash", RFC 6486, sec. 4.2. 1789a7e9e7fSjob * Return zero on failure, non-zero on success. 1799a7e9e7fSjob */ 1809a7e9e7fSjob static int 181be6e5ad5Stb mft_parse_filehash(const char *fn, struct mft *mft, const FileAndHash *fh, 182be6e5ad5Stb int *found_crl) 1839a7e9e7fSjob { 184d7658e14Stb char *file = NULL; 185630e12adSclaudio int rc = 0; 1869a7e9e7fSjob struct mftfile *fent; 18723bc08f8Sclaudio enum rtype type; 18831ac5904Sjob size_t new_idx = 0; 1899a7e9e7fSjob 19023e50b68Stb if (!valid_mft_filename(fh->file->data, fh->file->length)) { 191be6e5ad5Stb warnx("%s: RFC 6486 section 4.2.2: bad filename", fn); 192685326f5Sclaudio goto out; 193685326f5Sclaudio } 194d7658e14Stb file = strndup(fh->file->data, fh->file->length); 195d7658e14Stb if (file == NULL) 1967addb040Sbenno err(1, NULL); 1979a7e9e7fSjob 19823e50b68Stb if (fh->hash->length != SHA256_DIGEST_LENGTH) { 1999a7e9e7fSjob warnx("%s: RFC 6486 section 4.2.1: hash: " 200be6e5ad5Stb "invalid SHA256 length, have %d", fn, fh->hash->length); 201630e12adSclaudio goto out; 2029a7e9e7fSjob } 2039a7e9e7fSjob 204d7658e14Stb type = rtype_from_mftfile(file); 2058a9424f8Sjob if (type == RTYPE_CRL) { 2068a9424f8Sjob if (*found_crl == 1) { 2078a9424f8Sjob warnx("%s: RFC 6487: too many CRLs listed on MFT", fn); 2088a9424f8Sjob goto out; 2098a9424f8Sjob } 2108a9424f8Sjob if (strcmp(file, mft->crl) != 0) { 2118a9424f8Sjob warnx("%s: RFC 6487: name (%s) doesn't match CRLDP " 2128a9424f8Sjob "(%s)", fn, file, mft->crl); 2138a9424f8Sjob goto out; 2148a9424f8Sjob } 21523bc08f8Sclaudio /* remember the filehash for the CRL in struct mft */ 216be6e5ad5Stb memcpy(mft->crlhash, fh->hash->data, SHA256_DIGEST_LENGTH); 217be6e5ad5Stb *found_crl = 1; 21823bc08f8Sclaudio } 21923bc08f8Sclaudio 22031ac5904Sjob if (filemode) 221be6e5ad5Stb fent = &mft->files[mft->filesz++]; 22231ac5904Sjob else { 22331ac5904Sjob /* Fisher-Yates shuffle */ 224be6e5ad5Stb new_idx = arc4random_uniform(mft->filesz + 1); 225be6e5ad5Stb mft->files[mft->filesz++] = mft->files[new_idx]; 226be6e5ad5Stb fent = &mft->files[new_idx]; 22731ac5904Sjob } 22831ac5904Sjob 22923bc08f8Sclaudio fent->type = type; 230d7658e14Stb fent->file = file; 231d7658e14Stb file = NULL; 23223e50b68Stb memcpy(fent->hash, fh->hash->data, SHA256_DIGEST_LENGTH); 2339a7e9e7fSjob 234630e12adSclaudio rc = 1; 235630e12adSclaudio out: 236d7658e14Stb free(file); 2379a7e9e7fSjob return rc; 2389a7e9e7fSjob } 2399a7e9e7fSjob 24058c75743Stb static int 24158c75743Stb mft_fh_cmp_name(const FileAndHash *const *a, const FileAndHash *const *b) 24258c75743Stb { 24358c75743Stb if ((*a)->file->length < (*b)->file->length) 24458c75743Stb return -1; 24558c75743Stb if ((*a)->file->length > (*b)->file->length) 24658c75743Stb return 1; 24758c75743Stb 24858c75743Stb return memcmp((*a)->file->data, (*b)->file->data, (*b)->file->length); 24958c75743Stb } 25058c75743Stb 25158c75743Stb static int 25258c75743Stb mft_fh_cmp_hash(const FileAndHash *const *a, const FileAndHash *const *b) 25358c75743Stb { 25458c75743Stb assert((*a)->hash->length == SHA256_DIGEST_LENGTH); 25558c75743Stb assert((*b)->hash->length == SHA256_DIGEST_LENGTH); 25658c75743Stb 25758c75743Stb return memcmp((*a)->hash->data, (*b)->hash->data, (*b)->hash->length); 25858c75743Stb } 25958c75743Stb 26058c75743Stb /* 26158c75743Stb * Assuming that the hash lengths are validated, this checks that all file names 26258c75743Stb * and hashes in a manifest are unique. Returns 1 on success, 0 on failure. 26358c75743Stb */ 26458c75743Stb static int 26558c75743Stb mft_has_unique_names_and_hashes(const char *fn, const Manifest *mft) 26658c75743Stb { 26758c75743Stb STACK_OF(FileAndHash) *fhs; 26858c75743Stb int i, ret = 0; 26958c75743Stb 27058c75743Stb if ((fhs = sk_FileAndHash_dup(mft->fileList)) == NULL) 27158c75743Stb err(1, NULL); 27258c75743Stb 27358c75743Stb (void)sk_FileAndHash_set_cmp_func(fhs, mft_fh_cmp_name); 27458c75743Stb sk_FileAndHash_sort(fhs); 27558c75743Stb 27658c75743Stb for (i = 0; i < sk_FileAndHash_num(fhs) - 1; i++) { 27758c75743Stb const FileAndHash *curr = sk_FileAndHash_value(fhs, i); 27858c75743Stb const FileAndHash *next = sk_FileAndHash_value(fhs, i + 1); 27958c75743Stb 28058c75743Stb if (mft_fh_cmp_name(&curr, &next) == 0) { 28158c75743Stb warnx("%s: duplicate name: %.*s", fn, 28258c75743Stb curr->file->length, curr->file->data); 28358c75743Stb goto err; 28458c75743Stb } 28558c75743Stb } 28658c75743Stb 28758c75743Stb (void)sk_FileAndHash_set_cmp_func(fhs, mft_fh_cmp_hash); 28858c75743Stb sk_FileAndHash_sort(fhs); 28958c75743Stb 29058c75743Stb for (i = 0; i < sk_FileAndHash_num(fhs) - 1; i++) { 29158c75743Stb const FileAndHash *curr = sk_FileAndHash_value(fhs, i); 29258c75743Stb const FileAndHash *next = sk_FileAndHash_value(fhs, i + 1); 29358c75743Stb 29458c75743Stb if (mft_fh_cmp_hash(&curr, &next) == 0) { 29558c75743Stb warnx("%s: duplicate hash for %.*s and %.*s", fn, 29658c75743Stb curr->file->length, curr->file->data, 29758c75743Stb next->file->length, next->file->data); 29858c75743Stb goto err; 29958c75743Stb } 30058c75743Stb } 30158c75743Stb 30258c75743Stb ret = 1; 30358c75743Stb 30458c75743Stb err: 30558c75743Stb sk_FileAndHash_free(fhs); 30658c75743Stb 30758c75743Stb return ret; 30858c75743Stb } 30958c75743Stb 3109a7e9e7fSjob /* 3119a7e9e7fSjob * Handle the eContent of the manifest object, RFC 6486 sec. 4.2. 312651b53faSclaudio * Returns 0 on failure and 1 on success. 3139a7e9e7fSjob */ 3149a7e9e7fSjob static int 315be6e5ad5Stb mft_parse_econtent(const char *fn, struct mft *mft, const unsigned char *d, 316be6e5ad5Stb size_t dsz) 3179a7e9e7fSjob { 318d115f50dSjob const unsigned char *oder; 319cd9dc441Stb Manifest *mft_asn1; 32023e50b68Stb FileAndHash *fh; 321be6e5ad5Stb int found_crl, i, rc = 0; 3229a7e9e7fSjob 323d115f50dSjob oder = d; 324cd9dc441Stb if ((mft_asn1 = d2i_Manifest(NULL, &d, dsz)) == NULL) { 325be6e5ad5Stb warnx("%s: RFC 6486 section 4: failed to parse Manifest", fn); 3269a7e9e7fSjob goto out; 3279a7e9e7fSjob } 328d115f50dSjob if (d != oder + dsz) { 329be6e5ad5Stb warnx("%s: %td bytes trailing garbage in eContent", fn, 330d115f50dSjob oder + dsz - d); 331d115f50dSjob goto out; 332d115f50dSjob } 3339a7e9e7fSjob 334be6e5ad5Stb if (!valid_econtent_version(fn, mft_asn1->version, 0)) 3359a7e9e7fSjob goto out; 336ab5c69fdSjob 337904d9c60Stb mft->seqnum = x509_convert_seqnum(fn, "manifest number", 338904d9c60Stb mft_asn1->manifestNumber); 339be6e5ad5Stb if (mft->seqnum == NULL) 340ab5c69fdSjob goto out; 341ab5c69fdSjob 342851cae3dSjob /* 343851cae3dSjob * OpenSSL's DER decoder implementation will accept a GeneralizedTime 344851cae3dSjob * which doesn't conform to RFC 5280. So, double check. 345851cae3dSjob */ 346cd9dc441Stb if (ASN1_STRING_length(mft_asn1->thisUpdate) != GENTIME_LENGTH) { 347be6e5ad5Stb warnx("%s: embedded from time format invalid", fn); 3489a7e9e7fSjob goto out; 349851cae3dSjob } 350cd9dc441Stb if (ASN1_STRING_length(mft_asn1->nextUpdate) != GENTIME_LENGTH) { 351be6e5ad5Stb warnx("%s: embedded until time format invalid", fn); 352851cae3dSjob goto out; 353851cae3dSjob } 354851cae3dSjob 355be6e5ad5Stb if (!x509_get_time(mft_asn1->thisUpdate, &mft->thisupdate)) { 356be6e5ad5Stb warn("%s: parsing manifest thisUpdate failed", fn); 357851cae3dSjob goto out; 358851cae3dSjob } 359be6e5ad5Stb if (!x509_get_time(mft_asn1->nextUpdate, &mft->nextupdate)) { 360be6e5ad5Stb warn("%s: parsing manifest nextUpdate failed", fn); 361851cae3dSjob goto out; 362851cae3dSjob } 363851cae3dSjob 364be6e5ad5Stb if (mft->thisupdate > mft->nextupdate) { 365be6e5ad5Stb warnx("%s: bad update interval", fn); 366851cae3dSjob goto out; 367851cae3dSjob } 368bf682897Stb 369cd9dc441Stb if (OBJ_obj2nid(mft_asn1->fileHashAlg) != NID_sha256) { 3709a7e9e7fSjob warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: " 3711731179dStb "want SHA256 object, have %s", fn, 3721731179dStb nid2str(OBJ_obj2nid(mft_asn1->fileHashAlg))); 3739a7e9e7fSjob goto out; 3749a7e9e7fSjob } 3759a7e9e7fSjob 376*bb0e26feStb if (sk_FileAndHash_num(mft_asn1->fileList) <= 0) { 377*bb0e26feStb warnx("%s: no files in manifest fileList", fn); 378*bb0e26feStb goto out; 379*bb0e26feStb } 380cd9dc441Stb if (sk_FileAndHash_num(mft_asn1->fileList) >= MAX_MANIFEST_ENTRIES) { 381be6e5ad5Stb warnx("%s: %d exceeds manifest entry limit (%d)", fn, 382cd9dc441Stb sk_FileAndHash_num(mft_asn1->fileList), 383cd9dc441Stb MAX_MANIFEST_ENTRIES); 3849a7e9e7fSjob goto out; 385651b53faSclaudio } 386651b53faSclaudio 387be6e5ad5Stb mft->files = calloc(sk_FileAndHash_num(mft_asn1->fileList), 38823e50b68Stb sizeof(struct mftfile)); 389be6e5ad5Stb if (mft->files == NULL) 39023e50b68Stb err(1, NULL); 39123e50b68Stb 392be6e5ad5Stb found_crl = 0; 393cd9dc441Stb for (i = 0; i < sk_FileAndHash_num(mft_asn1->fileList); i++) { 394cd9dc441Stb fh = sk_FileAndHash_value(mft_asn1->fileList, i); 395be6e5ad5Stb if (!mft_parse_filehash(fn, mft, fh, &found_crl)) 3969a7e9e7fSjob goto out; 39723e50b68Stb } 39823e50b68Stb 399be6e5ad5Stb if (!found_crl) { 400be6e5ad5Stb warnx("%s: CRL not part of MFT fileList", fn); 40123e50b68Stb goto out; 40223e50b68Stb } 4039a7e9e7fSjob 404be6e5ad5Stb if (!mft_has_unique_names_and_hashes(fn, mft_asn1)) 40558c75743Stb goto out; 40658c75743Stb 407bf682897Stb rc = 1; 4089a7e9e7fSjob out: 409cd9dc441Stb Manifest_free(mft_asn1); 4109a7e9e7fSjob return rc; 4119a7e9e7fSjob } 4129a7e9e7fSjob 4139a7e9e7fSjob /* 4149a7e9e7fSjob * Parse the objects that have been published in the manifest. 415c6be5ad7Sjob * Return mft if it conforms to RFC 6486, otherwise NULL. 4169a7e9e7fSjob */ 4179a7e9e7fSjob struct mft * 4180636c4d0Stb mft_parse(X509 **x509, const char *fn, int talid, const unsigned char *der, 4190636c4d0Stb size_t len) 4209a7e9e7fSjob { 421be6e5ad5Stb struct mft *mft; 422b454bae8Sjob struct cert *cert = NULL; 423651b53faSclaudio int rc = 0; 424651b53faSclaudio size_t cmsz; 4259a7e9e7fSjob unsigned char *cms; 42605ed64a0Stb char *crldp = NULL, *crlfile; 427beccb378Stb time_t signtime = 0; 4289a7e9e7fSjob 4291330c972Stb cms = cms_parse_validate(x509, fn, der, len, mft_oid, &cmsz, &signtime); 4309a7e9e7fSjob if (cms == NULL) 4319a7e9e7fSjob return NULL; 4329a7e9e7fSjob assert(*x509 != NULL); 4339a7e9e7fSjob 434be6e5ad5Stb if ((mft = calloc(1, sizeof(*mft))) == NULL) 4357addb040Sbenno err(1, NULL); 436be6e5ad5Stb mft->signtime = signtime; 4371f25fa5dStb 438be6e5ad5Stb if (!x509_get_aia(*x509, fn, &mft->aia)) 439f999fe57Sclaudio goto out; 440be6e5ad5Stb if (!x509_get_aki(*x509, fn, &mft->aki)) 441f999fe57Sclaudio goto out; 442be6e5ad5Stb if (!x509_get_sia(*x509, fn, &mft->sia)) 4432cf0e122Sjob goto out; 444be6e5ad5Stb if (!x509_get_ski(*x509, fn, &mft->ski)) 445f999fe57Sclaudio goto out; 446be6e5ad5Stb if (mft->aia == NULL || mft->aki == NULL || mft->sia == NULL || 447be6e5ad5Stb mft->ski == NULL) { 4481f25fa5dStb warnx("%s: RFC 6487 section 4.8: " 4492cf0e122Sjob "missing AIA, AKI, SIA, or SKI X509 extension", fn); 4509a7e9e7fSjob goto out; 4511f25fa5dStb } 4529a7e9e7fSjob 4533a363cbdSjob if (!x509_inherits(*x509)) { 4543a363cbdSjob warnx("%s: RFC 3779 extension not set to inherit", fn); 4553a363cbdSjob goto out; 4563a363cbdSjob } 4573a363cbdSjob 45823bc08f8Sclaudio /* get CRL info for later */ 45923bc08f8Sclaudio if (!x509_get_crl(*x509, fn, &crldp)) 46023bc08f8Sclaudio goto out; 46123bc08f8Sclaudio if (crldp == NULL) { 46223bc08f8Sclaudio warnx("%s: RFC 6487 section 4.8.6: CRL: " 46323bc08f8Sclaudio "missing CRL distribution point extension", fn); 46423bc08f8Sclaudio goto out; 46523bc08f8Sclaudio } 4666747684dSjob crlfile = strrchr(crldp, '/'); 4676747684dSjob if (crlfile == NULL) { 4686747684dSjob warnx("%s: RFC 6487 section 4.8.6: " 4696747684dSjob "invalid CRL distribution point", fn); 4706747684dSjob goto out; 4716747684dSjob } 4726747684dSjob crlfile++; 4736747684dSjob if (!valid_mft_filename(crlfile, strlen(crlfile)) || 4746747684dSjob rtype_from_file_extension(crlfile) != RTYPE_CRL) { 47523bc08f8Sclaudio warnx("%s: RFC 6487 section 4.8.6: CRL: " 47623bc08f8Sclaudio "bad CRL distribution point extension", fn); 47723bc08f8Sclaudio goto out; 47823bc08f8Sclaudio } 479be6e5ad5Stb if ((mft->crl = strdup(crlfile)) == NULL) 48023bc08f8Sclaudio err(1, NULL); 48123bc08f8Sclaudio 482be6e5ad5Stb if (mft_parse_econtent(fn, mft, cms, cmsz) == 0) 4839a7e9e7fSjob goto out; 4849a7e9e7fSjob 485891d6bceSjob if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL) 486b454bae8Sjob goto out; 487b454bae8Sjob 488be6e5ad5Stb if (mft->signtime > mft->nextupdate) { 48936b57f80Sjob warnx("%s: dating issue: CMS signing-time after MFT nextUpdate", 49036b57f80Sjob fn); 49136b57f80Sjob goto out; 49236b57f80Sjob } 49336b57f80Sjob 4949a7e9e7fSjob rc = 1; 4959a7e9e7fSjob out: 4969a7e9e7fSjob if (rc == 0) { 497be6e5ad5Stb mft_free(mft); 498be6e5ad5Stb mft = NULL; 4999a7e9e7fSjob X509_free(*x509); 5009a7e9e7fSjob *x509 = NULL; 5019a7e9e7fSjob } 50205ed64a0Stb free(crldp); 503b454bae8Sjob cert_free(cert); 5049a7e9e7fSjob free(cms); 505be6e5ad5Stb return mft; 5069a7e9e7fSjob } 5079a7e9e7fSjob 5089a7e9e7fSjob /* 5099a7e9e7fSjob * Free an MFT pointer. 5109a7e9e7fSjob * Safe to call with NULL. 5119a7e9e7fSjob */ 5129a7e9e7fSjob void 5139a7e9e7fSjob mft_free(struct mft *p) 5149a7e9e7fSjob { 5159a7e9e7fSjob size_t i; 5169a7e9e7fSjob 5179a7e9e7fSjob if (p == NULL) 5189a7e9e7fSjob return; 5199a7e9e7fSjob 5209a7e9e7fSjob for (i = 0; i < p->filesz; i++) 5219a7e9e7fSjob free(p->files[i].file); 5229a7e9e7fSjob 523a9a51c4fStb free(p->path); 524a9a51c4fStb free(p->files); 525a9a51c4fStb free(p->seqnum); 526ebd55816Sjob free(p->aia); 5279a7e9e7fSjob free(p->aki); 5282cf0e122Sjob free(p->sia); 5299a7e9e7fSjob free(p->ski); 530a9a51c4fStb free(p->crl); 5319a7e9e7fSjob free(p); 5329a7e9e7fSjob } 5339a7e9e7fSjob 5349a7e9e7fSjob /* 5359a7e9e7fSjob * Serialise MFT parsed content into the given buffer. 5369a7e9e7fSjob * See mft_read() for the other side of the pipe. 5379a7e9e7fSjob */ 5389a7e9e7fSjob void 53908db1177Sclaudio mft_buffer(struct ibuf *b, const struct mft *p) 5409a7e9e7fSjob { 5419a7e9e7fSjob size_t i; 5429a7e9e7fSjob 543100ded9eSclaudio io_simple_buffer(b, &p->repoid, sizeof(p->repoid)); 5441fc2657fSclaudio io_simple_buffer(b, &p->talid, sizeof(p->talid)); 5450bc420b9Sclaudio io_simple_buffer(b, &p->certid, sizeof(p->certid)); 546318f0572Sjob io_simple_buffer(b, &p->seqnum_gap, sizeof(p->seqnum_gap)); 547100ded9eSclaudio io_str_buffer(b, p->path); 5489a7e9e7fSjob 549ebd55816Sjob io_str_buffer(b, p->aia); 55008db1177Sclaudio io_str_buffer(b, p->aki); 55108db1177Sclaudio io_str_buffer(b, p->ski); 552100ded9eSclaudio 553100ded9eSclaudio io_simple_buffer(b, &p->filesz, sizeof(size_t)); 554100ded9eSclaudio for (i = 0; i < p->filesz; i++) { 555100ded9eSclaudio io_str_buffer(b, p->files[i].file); 55622cec6c4Stb io_simple_buffer(b, &p->files[i].type, 55722cec6c4Stb sizeof(p->files[i].type)); 558df512fbcSclaudio io_simple_buffer(b, &p->files[i].location, 559df512fbcSclaudio sizeof(p->files[i].location)); 560100ded9eSclaudio io_simple_buffer(b, p->files[i].hash, SHA256_DIGEST_LENGTH); 561100ded9eSclaudio } 5629a7e9e7fSjob } 5639a7e9e7fSjob 5649a7e9e7fSjob /* 5659a7e9e7fSjob * Read an MFT structure from the file descriptor. 5669a7e9e7fSjob * Result must be passed to mft_free(). 5679a7e9e7fSjob */ 5689a7e9e7fSjob struct mft * 5697eb79a4aSclaudio mft_read(struct ibuf *b) 5709a7e9e7fSjob { 5719a7e9e7fSjob struct mft *p = NULL; 5729a7e9e7fSjob size_t i; 5739a7e9e7fSjob 5749a7e9e7fSjob if ((p = calloc(1, sizeof(struct mft))) == NULL) 5757addb040Sbenno err(1, NULL); 5769a7e9e7fSjob 577100ded9eSclaudio io_read_buf(b, &p->repoid, sizeof(p->repoid)); 5781fc2657fSclaudio io_read_buf(b, &p->talid, sizeof(p->talid)); 5790bc420b9Sclaudio io_read_buf(b, &p->certid, sizeof(p->certid)); 580318f0572Sjob io_read_buf(b, &p->seqnum_gap, sizeof(p->seqnum_gap)); 581100ded9eSclaudio io_read_str(b, &p->path); 5829a7e9e7fSjob 583100ded9eSclaudio io_read_str(b, &p->aia); 584100ded9eSclaudio io_read_str(b, &p->aki); 585100ded9eSclaudio io_read_str(b, &p->ski); 586100ded9eSclaudio assert(p->aia && p->aki && p->ski); 587100ded9eSclaudio 588100ded9eSclaudio io_read_buf(b, &p->filesz, sizeof(size_t)); 5899a7e9e7fSjob if ((p->files = calloc(p->filesz, sizeof(struct mftfile))) == NULL) 5907addb040Sbenno err(1, NULL); 5919a7e9e7fSjob 5929a7e9e7fSjob for (i = 0; i < p->filesz; i++) { 5937eb79a4aSclaudio io_read_str(b, &p->files[i].file); 59422cec6c4Stb io_read_buf(b, &p->files[i].type, sizeof(p->files[i].type)); 595df512fbcSclaudio io_read_buf(b, &p->files[i].location, 596df512fbcSclaudio sizeof(p->files[i].location)); 5977eb79a4aSclaudio io_read_buf(b, p->files[i].hash, SHA256_DIGEST_LENGTH); 5989a7e9e7fSjob } 5999a7e9e7fSjob 6009a7e9e7fSjob return p; 6019a7e9e7fSjob } 602df512fbcSclaudio 603df512fbcSclaudio /* 6041039ba60Stb * Compare the thisupdate time of two mft files. 6051039ba60Stb */ 6061039ba60Stb int 6071039ba60Stb mft_compare_issued(const struct mft *a, const struct mft *b) 6081039ba60Stb { 6091039ba60Stb if (a->thisupdate > b->thisupdate) 6101039ba60Stb return 1; 6111039ba60Stb if (a->thisupdate < b->thisupdate) 6121039ba60Stb return -1; 6131039ba60Stb return 0; 6141039ba60Stb } 6151039ba60Stb 6161039ba60Stb /* 617e73085f8Stb * Compare the manifestNumber of two mft files. 618df512fbcSclaudio */ 619df512fbcSclaudio int 620e73085f8Stb mft_compare_seqnum(const struct mft *a, const struct mft *b) 621df512fbcSclaudio { 622df512fbcSclaudio int r; 623df512fbcSclaudio 624df512fbcSclaudio r = strlen(a->seqnum) - strlen(b->seqnum); 625df512fbcSclaudio if (r > 0) /* seqnum in a is longer -> higher */ 626df512fbcSclaudio return 1; 627df512fbcSclaudio if (r < 0) /* seqnum in a is shorter -> smaller */ 6280c60a445Sjob return -1; 629df512fbcSclaudio 630df512fbcSclaudio r = strcmp(a->seqnum, b->seqnum); 63173a8ca21Sclaudio if (r > 0) /* a is greater, prefer a */ 632df512fbcSclaudio return 1; 6330c60a445Sjob if (r < 0) /* b is greater, prefer b */ 6340c60a445Sjob return -1; 6350c60a445Sjob 636df512fbcSclaudio return 0; 637df512fbcSclaudio } 638318f0572Sjob 639318f0572Sjob /* 640318f0572Sjob * Test if there is a gap in the sequence numbers of two MFTs. 641318f0572Sjob * Return 1 if a gap is detected. 642318f0572Sjob */ 643318f0572Sjob int 644318f0572Sjob mft_seqnum_gap_present(const struct mft *a, const struct mft *b) 645318f0572Sjob { 646318f0572Sjob BIGNUM *diff, *seqnum_a, *seqnum_b; 647318f0572Sjob int ret = 0; 648318f0572Sjob 649318f0572Sjob BN_CTX_start(bn_ctx); 650318f0572Sjob if ((diff = BN_CTX_get(bn_ctx)) == NULL || 651318f0572Sjob (seqnum_a = BN_CTX_get(bn_ctx)) == NULL || 652318f0572Sjob (seqnum_b = BN_CTX_get(bn_ctx)) == NULL) 653318f0572Sjob errx(1, "BN_CTX_get"); 654318f0572Sjob 655318f0572Sjob if (!BN_hex2bn(&seqnum_a, a->seqnum)) 656318f0572Sjob errx(1, "BN_hex2bn"); 657318f0572Sjob 658318f0572Sjob if (!BN_hex2bn(&seqnum_b, b->seqnum)) 659318f0572Sjob errx(1, "BN_hex2bn"); 660318f0572Sjob 661318f0572Sjob if (!BN_sub(diff, seqnum_a, seqnum_b)) 662318f0572Sjob errx(1, "BN_sub"); 663318f0572Sjob 664318f0572Sjob ret = !BN_is_one(diff); 665318f0572Sjob 666318f0572Sjob BN_CTX_end(bn_ctx); 667318f0572Sjob 668318f0572Sjob return ret; 669318f0572Sjob } 670