xref: /openbsd/usr.sbin/rpki-client/mft.c (revision bb0e26fe)
1 /*	$OpenBSD: mft.c,v 1.121 2024/12/24 10:03:59 tb Exp $ */
2 /*
3  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <assert.h>
20 #include <err.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <openssl/bn.h>
28 #include <openssl/asn1.h>
29 #include <openssl/asn1t.h>
30 #include <openssl/safestack.h>
31 #include <openssl/sha.h>
32 #include <openssl/stack.h>
33 #include <openssl/x509.h>
34 
35 #include "extern.h"
36 
37 extern ASN1_OBJECT	*mft_oid;
38 BN_CTX			*bn_ctx;
39 
40 /*
41  * Types and templates for the Manifest eContent, RFC 6486, section 4.2.
42  */
43 
44 ASN1_ITEM_EXP FileAndHash_it;
45 ASN1_ITEM_EXP Manifest_it;
46 
47 typedef struct {
48 	ASN1_IA5STRING	*file;
49 	ASN1_BIT_STRING	*hash;
50 } FileAndHash;
51 
52 DECLARE_STACK_OF(FileAndHash);
53 
54 #ifndef DEFINE_STACK_OF
55 #define sk_FileAndHash_dup(sk)		SKM_sk_dup(FileAndHash, (sk))
56 #define sk_FileAndHash_free(sk)		SKM_sk_free(FileAndHash, (sk))
57 #define sk_FileAndHash_num(sk)		SKM_sk_num(FileAndHash, (sk))
58 #define sk_FileAndHash_value(sk, i)	SKM_sk_value(FileAndHash, (sk), (i))
59 #define sk_FileAndHash_sort(sk)		SKM_sk_sort(FileAndHash, (sk))
60 #define sk_FileAndHash_set_cmp_func(sk, cmp) \
61     SKM_sk_set_cmp_func(FileAndHash, (sk), (cmp))
62 #endif
63 
64 typedef struct {
65 	ASN1_INTEGER		*version;
66 	ASN1_INTEGER		*manifestNumber;
67 	ASN1_GENERALIZEDTIME	*thisUpdate;
68 	ASN1_GENERALIZEDTIME	*nextUpdate;
69 	ASN1_OBJECT		*fileHashAlg;
70 	STACK_OF(FileAndHash)	*fileList;
71 } Manifest;
72 
73 ASN1_SEQUENCE(FileAndHash) = {
74 	ASN1_SIMPLE(FileAndHash, file, ASN1_IA5STRING),
75 	ASN1_SIMPLE(FileAndHash, hash, ASN1_BIT_STRING),
76 } ASN1_SEQUENCE_END(FileAndHash);
77 
78 ASN1_SEQUENCE(Manifest) = {
79 	ASN1_EXP_OPT(Manifest, version, ASN1_INTEGER, 0),
80 	ASN1_SIMPLE(Manifest, manifestNumber, ASN1_INTEGER),
81 	ASN1_SIMPLE(Manifest, thisUpdate, ASN1_GENERALIZEDTIME),
82 	ASN1_SIMPLE(Manifest, nextUpdate, ASN1_GENERALIZEDTIME),
83 	ASN1_SIMPLE(Manifest, fileHashAlg, ASN1_OBJECT),
84 	ASN1_SEQUENCE_OF(Manifest, fileList, FileAndHash),
85 } ASN1_SEQUENCE_END(Manifest);
86 
87 DECLARE_ASN1_FUNCTIONS(Manifest);
88 IMPLEMENT_ASN1_FUNCTIONS(Manifest);
89 
90 #define GENTIME_LENGTH 15
91 
92 /*
93  * Determine rtype corresponding to file extension. Returns RTYPE_INVALID
94  * on error or unknown extension.
95  */
96 enum rtype
rtype_from_file_extension(const char * fn)97 rtype_from_file_extension(const char *fn)
98 {
99 	size_t	 sz;
100 
101 	sz = strlen(fn);
102 	if (sz < 5)
103 		return RTYPE_INVALID;
104 
105 	if (strcasecmp(fn + sz - 4, ".tal") == 0)
106 		return RTYPE_TAL;
107 	if (strcasecmp(fn + sz - 4, ".cer") == 0)
108 		return RTYPE_CER;
109 	if (strcasecmp(fn + sz - 4, ".crl") == 0)
110 		return RTYPE_CRL;
111 	if (strcasecmp(fn + sz - 4, ".mft") == 0)
112 		return RTYPE_MFT;
113 	if (strcasecmp(fn + sz - 4, ".roa") == 0)
114 		return RTYPE_ROA;
115 	if (strcasecmp(fn + sz - 4, ".gbr") == 0)
116 		return RTYPE_GBR;
117 	if (strcasecmp(fn + sz - 4, ".sig") == 0)
118 		return RTYPE_RSC;
119 	if (strcasecmp(fn + sz - 4, ".asa") == 0)
120 		return RTYPE_ASPA;
121 	if (strcasecmp(fn + sz - 4, ".tak") == 0)
122 		return RTYPE_TAK;
123 	if (strcasecmp(fn + sz - 4, ".csv") == 0)
124 		return RTYPE_GEOFEED;
125 	if (strcasecmp(fn + sz - 4, ".spl") == 0)
126 		return RTYPE_SPL;
127 
128 	return RTYPE_INVALID;
129 }
130 
131 /*
132  * Validate that a filename listed on a Manifest only contains characters
133  * permitted in RFC 9286 section 4.2.2.
134  * Also ensure that there is exactly one '.'.
135  */
136 static int
valid_mft_filename(const char * fn,size_t len)137 valid_mft_filename(const char *fn, size_t len)
138 {
139 	const unsigned char *c;
140 
141 	if (!valid_filename(fn, len))
142 		return 0;
143 
144 	c = memchr(fn, '.', len);
145 	if (c == NULL || c != memrchr(fn, '.', len))
146 		return 0;
147 
148 	return 1;
149 }
150 
151 /*
152  * Check that the file is allowed to be part of a manifest and the parser
153  * for this type is implemented in rpki-client.
154  * Returns corresponding rtype or RTYPE_INVALID to mark the file as unknown.
155  */
156 static enum rtype
rtype_from_mftfile(const char * fn)157 rtype_from_mftfile(const char *fn)
158 {
159 	enum rtype		 type;
160 
161 	type = rtype_from_file_extension(fn);
162 	switch (type) {
163 	case RTYPE_CER:
164 	case RTYPE_CRL:
165 	case RTYPE_GBR:
166 	case RTYPE_ROA:
167 	case RTYPE_ASPA:
168 	case RTYPE_SPL:
169 	case RTYPE_TAK:
170 		return type;
171 	default:
172 		return RTYPE_INVALID;
173 	}
174 }
175 
176 /*
177  * Parse an individual "FileAndHash", RFC 6486, sec. 4.2.
178  * Return zero on failure, non-zero on success.
179  */
180 static int
mft_parse_filehash(const char * fn,struct mft * mft,const FileAndHash * fh,int * found_crl)181 mft_parse_filehash(const char *fn, struct mft *mft, const FileAndHash *fh,
182     int *found_crl)
183 {
184 	char			*file = NULL;
185 	int			 rc = 0;
186 	struct mftfile		*fent;
187 	enum rtype		 type;
188 	size_t			 new_idx = 0;
189 
190 	if (!valid_mft_filename(fh->file->data, fh->file->length)) {
191 		warnx("%s: RFC 6486 section 4.2.2: bad filename", fn);
192 		goto out;
193 	}
194 	file = strndup(fh->file->data, fh->file->length);
195 	if (file == NULL)
196 		err(1, NULL);
197 
198 	if (fh->hash->length != SHA256_DIGEST_LENGTH) {
199 		warnx("%s: RFC 6486 section 4.2.1: hash: "
200 		    "invalid SHA256 length, have %d", fn, fh->hash->length);
201 		goto out;
202 	}
203 
204 	type = rtype_from_mftfile(file);
205 	if (type == RTYPE_CRL) {
206 		if (*found_crl == 1) {
207 			warnx("%s: RFC 6487: too many CRLs listed on MFT", fn);
208 			goto out;
209 		}
210 		if (strcmp(file, mft->crl) != 0) {
211 			warnx("%s: RFC 6487: name (%s) doesn't match CRLDP "
212 			    "(%s)", fn, file, mft->crl);
213 			goto out;
214 		}
215 		/* remember the filehash for the CRL in struct mft */
216 		memcpy(mft->crlhash, fh->hash->data, SHA256_DIGEST_LENGTH);
217 		*found_crl = 1;
218 	}
219 
220 	if (filemode)
221 		fent = &mft->files[mft->filesz++];
222 	else {
223 		/* Fisher-Yates shuffle */
224 		new_idx = arc4random_uniform(mft->filesz + 1);
225 		mft->files[mft->filesz++] = mft->files[new_idx];
226 		fent = &mft->files[new_idx];
227 	}
228 
229 	fent->type = type;
230 	fent->file = file;
231 	file = NULL;
232 	memcpy(fent->hash, fh->hash->data, SHA256_DIGEST_LENGTH);
233 
234 	rc = 1;
235  out:
236 	free(file);
237 	return rc;
238 }
239 
240 static int
mft_fh_cmp_name(const FileAndHash * const * a,const FileAndHash * const * b)241 mft_fh_cmp_name(const FileAndHash *const *a, const FileAndHash *const *b)
242 {
243 	if ((*a)->file->length < (*b)->file->length)
244 		return -1;
245 	if ((*a)->file->length > (*b)->file->length)
246 		return 1;
247 
248 	return memcmp((*a)->file->data, (*b)->file->data, (*b)->file->length);
249 }
250 
251 static int
mft_fh_cmp_hash(const FileAndHash * const * a,const FileAndHash * const * b)252 mft_fh_cmp_hash(const FileAndHash *const *a, const FileAndHash *const *b)
253 {
254 	assert((*a)->hash->length == SHA256_DIGEST_LENGTH);
255 	assert((*b)->hash->length == SHA256_DIGEST_LENGTH);
256 
257 	return memcmp((*a)->hash->data, (*b)->hash->data, (*b)->hash->length);
258 }
259 
260 /*
261  * Assuming that the hash lengths are validated, this checks that all file names
262  * and hashes in a manifest are unique. Returns 1 on success, 0 on failure.
263  */
264 static int
mft_has_unique_names_and_hashes(const char * fn,const Manifest * mft)265 mft_has_unique_names_and_hashes(const char *fn, const Manifest *mft)
266 {
267 	STACK_OF(FileAndHash)	*fhs;
268 	int			 i, ret = 0;
269 
270 	if ((fhs = sk_FileAndHash_dup(mft->fileList)) == NULL)
271 		err(1, NULL);
272 
273 	(void)sk_FileAndHash_set_cmp_func(fhs, mft_fh_cmp_name);
274 	sk_FileAndHash_sort(fhs);
275 
276 	for (i = 0; i < sk_FileAndHash_num(fhs) - 1; i++) {
277 		const FileAndHash *curr = sk_FileAndHash_value(fhs, i);
278 		const FileAndHash *next = sk_FileAndHash_value(fhs, i + 1);
279 
280 		if (mft_fh_cmp_name(&curr, &next) == 0) {
281 			warnx("%s: duplicate name: %.*s", fn,
282 			    curr->file->length, curr->file->data);
283 			goto err;
284 		}
285 	}
286 
287 	(void)sk_FileAndHash_set_cmp_func(fhs, mft_fh_cmp_hash);
288 	sk_FileAndHash_sort(fhs);
289 
290 	for (i = 0; i < sk_FileAndHash_num(fhs) - 1; i++) {
291 		const FileAndHash *curr = sk_FileAndHash_value(fhs, i);
292 		const FileAndHash *next = sk_FileAndHash_value(fhs, i + 1);
293 
294 		if (mft_fh_cmp_hash(&curr, &next) == 0) {
295 			warnx("%s: duplicate hash for %.*s and %.*s", fn,
296 			    curr->file->length, curr->file->data,
297 			    next->file->length, next->file->data);
298 			goto err;
299 		}
300 	}
301 
302 	ret = 1;
303 
304  err:
305 	sk_FileAndHash_free(fhs);
306 
307 	return ret;
308 }
309 
310 /*
311  * Handle the eContent of the manifest object, RFC 6486 sec. 4.2.
312  * Returns 0 on failure and 1 on success.
313  */
314 static int
mft_parse_econtent(const char * fn,struct mft * mft,const unsigned char * d,size_t dsz)315 mft_parse_econtent(const char *fn, struct mft *mft, const unsigned char *d,
316     size_t dsz)
317 {
318 	const unsigned char	*oder;
319 	Manifest		*mft_asn1;
320 	FileAndHash		*fh;
321 	int			 found_crl, i, rc = 0;
322 
323 	oder = d;
324 	if ((mft_asn1 = d2i_Manifest(NULL, &d, dsz)) == NULL) {
325 		warnx("%s: RFC 6486 section 4: failed to parse Manifest", fn);
326 		goto out;
327 	}
328 	if (d != oder + dsz) {
329 		warnx("%s: %td bytes trailing garbage in eContent", fn,
330 		    oder + dsz - d);
331 		goto out;
332 	}
333 
334 	if (!valid_econtent_version(fn, mft_asn1->version, 0))
335 		goto out;
336 
337 	mft->seqnum = x509_convert_seqnum(fn, "manifest number",
338 	    mft_asn1->manifestNumber);
339 	if (mft->seqnum == NULL)
340 		goto out;
341 
342 	/*
343 	 * OpenSSL's DER decoder implementation will accept a GeneralizedTime
344 	 * which doesn't conform to RFC 5280. So, double check.
345 	 */
346 	if (ASN1_STRING_length(mft_asn1->thisUpdate) != GENTIME_LENGTH) {
347 		warnx("%s: embedded from time format invalid", fn);
348 		goto out;
349 	}
350 	if (ASN1_STRING_length(mft_asn1->nextUpdate) != GENTIME_LENGTH) {
351 		warnx("%s: embedded until time format invalid", fn);
352 		goto out;
353 	}
354 
355 	if (!x509_get_time(mft_asn1->thisUpdate, &mft->thisupdate)) {
356 		warn("%s: parsing manifest thisUpdate failed", fn);
357 		goto out;
358 	}
359 	if (!x509_get_time(mft_asn1->nextUpdate, &mft->nextupdate)) {
360 		warn("%s: parsing manifest nextUpdate failed", fn);
361 		goto out;
362 	}
363 
364 	if (mft->thisupdate > mft->nextupdate) {
365 		warnx("%s: bad update interval", fn);
366 		goto out;
367 	}
368 
369 	if (OBJ_obj2nid(mft_asn1->fileHashAlg) != NID_sha256) {
370 		warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: "
371 		    "want SHA256 object, have %s", fn,
372 		    nid2str(OBJ_obj2nid(mft_asn1->fileHashAlg)));
373 		goto out;
374 	}
375 
376 	if (sk_FileAndHash_num(mft_asn1->fileList) <= 0) {
377 		warnx("%s: no files in manifest fileList", fn);
378 		goto out;
379 	}
380 	if (sk_FileAndHash_num(mft_asn1->fileList) >= MAX_MANIFEST_ENTRIES) {
381 		warnx("%s: %d exceeds manifest entry limit (%d)", fn,
382 		    sk_FileAndHash_num(mft_asn1->fileList),
383 		    MAX_MANIFEST_ENTRIES);
384 		goto out;
385 	}
386 
387 	mft->files = calloc(sk_FileAndHash_num(mft_asn1->fileList),
388 	    sizeof(struct mftfile));
389 	if (mft->files == NULL)
390 		err(1, NULL);
391 
392 	found_crl = 0;
393 	for (i = 0; i < sk_FileAndHash_num(mft_asn1->fileList); i++) {
394 		fh = sk_FileAndHash_value(mft_asn1->fileList, i);
395 		if (!mft_parse_filehash(fn, mft, fh, &found_crl))
396 			goto out;
397 	}
398 
399 	if (!found_crl) {
400 		warnx("%s: CRL not part of MFT fileList", fn);
401 		goto out;
402 	}
403 
404 	if (!mft_has_unique_names_and_hashes(fn, mft_asn1))
405 		goto out;
406 
407 	rc = 1;
408  out:
409 	Manifest_free(mft_asn1);
410 	return rc;
411 }
412 
413 /*
414  * Parse the objects that have been published in the manifest.
415  * Return mft if it conforms to RFC 6486, otherwise NULL.
416  */
417 struct mft *
mft_parse(X509 ** x509,const char * fn,int talid,const unsigned char * der,size_t len)418 mft_parse(X509 **x509, const char *fn, int talid, const unsigned char *der,
419     size_t len)
420 {
421 	struct mft	*mft;
422 	struct cert	*cert = NULL;
423 	int		 rc = 0;
424 	size_t		 cmsz;
425 	unsigned char	*cms;
426 	char		*crldp = NULL, *crlfile;
427 	time_t		 signtime = 0;
428 
429 	cms = cms_parse_validate(x509, fn, der, len, mft_oid, &cmsz, &signtime);
430 	if (cms == NULL)
431 		return NULL;
432 	assert(*x509 != NULL);
433 
434 	if ((mft = calloc(1, sizeof(*mft))) == NULL)
435 		err(1, NULL);
436 	mft->signtime = signtime;
437 
438 	if (!x509_get_aia(*x509, fn, &mft->aia))
439 		goto out;
440 	if (!x509_get_aki(*x509, fn, &mft->aki))
441 		goto out;
442 	if (!x509_get_sia(*x509, fn, &mft->sia))
443 		goto out;
444 	if (!x509_get_ski(*x509, fn, &mft->ski))
445 		goto out;
446 	if (mft->aia == NULL || mft->aki == NULL || mft->sia == NULL ||
447 	    mft->ski == NULL) {
448 		warnx("%s: RFC 6487 section 4.8: "
449 		    "missing AIA, AKI, SIA, or SKI X509 extension", fn);
450 		goto out;
451 	}
452 
453 	if (!x509_inherits(*x509)) {
454 		warnx("%s: RFC 3779 extension not set to inherit", fn);
455 		goto out;
456 	}
457 
458 	/* get CRL info for later */
459 	if (!x509_get_crl(*x509, fn, &crldp))
460 		goto out;
461 	if (crldp == NULL) {
462 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
463 		    "missing CRL distribution point extension", fn);
464 		goto out;
465 	}
466 	crlfile = strrchr(crldp, '/');
467 	if (crlfile == NULL) {
468 		warnx("%s: RFC 6487 section 4.8.6: "
469 		    "invalid CRL distribution point", fn);
470 		goto out;
471 	}
472 	crlfile++;
473 	if (!valid_mft_filename(crlfile, strlen(crlfile)) ||
474 	    rtype_from_file_extension(crlfile) != RTYPE_CRL) {
475 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
476 		    "bad CRL distribution point extension", fn);
477 		goto out;
478 	}
479 	if ((mft->crl = strdup(crlfile)) == NULL)
480 		err(1, NULL);
481 
482 	if (mft_parse_econtent(fn, mft, cms, cmsz) == 0)
483 		goto out;
484 
485 	if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL)
486 		goto out;
487 
488 	if (mft->signtime > mft->nextupdate) {
489 		warnx("%s: dating issue: CMS signing-time after MFT nextUpdate",
490 		    fn);
491 		goto out;
492 	}
493 
494 	rc = 1;
495 out:
496 	if (rc == 0) {
497 		mft_free(mft);
498 		mft = NULL;
499 		X509_free(*x509);
500 		*x509 = NULL;
501 	}
502 	free(crldp);
503 	cert_free(cert);
504 	free(cms);
505 	return mft;
506 }
507 
508 /*
509  * Free an MFT pointer.
510  * Safe to call with NULL.
511  */
512 void
mft_free(struct mft * p)513 mft_free(struct mft *p)
514 {
515 	size_t	 i;
516 
517 	if (p == NULL)
518 		return;
519 
520 	for (i = 0; i < p->filesz; i++)
521 		free(p->files[i].file);
522 
523 	free(p->path);
524 	free(p->files);
525 	free(p->seqnum);
526 	free(p->aia);
527 	free(p->aki);
528 	free(p->sia);
529 	free(p->ski);
530 	free(p->crl);
531 	free(p);
532 }
533 
534 /*
535  * Serialise MFT parsed content into the given buffer.
536  * See mft_read() for the other side of the pipe.
537  */
538 void
mft_buffer(struct ibuf * b,const struct mft * p)539 mft_buffer(struct ibuf *b, const struct mft *p)
540 {
541 	size_t		 i;
542 
543 	io_simple_buffer(b, &p->repoid, sizeof(p->repoid));
544 	io_simple_buffer(b, &p->talid, sizeof(p->talid));
545 	io_simple_buffer(b, &p->certid, sizeof(p->certid));
546 	io_simple_buffer(b, &p->seqnum_gap, sizeof(p->seqnum_gap));
547 	io_str_buffer(b, p->path);
548 
549 	io_str_buffer(b, p->aia);
550 	io_str_buffer(b, p->aki);
551 	io_str_buffer(b, p->ski);
552 
553 	io_simple_buffer(b, &p->filesz, sizeof(size_t));
554 	for (i = 0; i < p->filesz; i++) {
555 		io_str_buffer(b, p->files[i].file);
556 		io_simple_buffer(b, &p->files[i].type,
557 		    sizeof(p->files[i].type));
558 		io_simple_buffer(b, &p->files[i].location,
559 		    sizeof(p->files[i].location));
560 		io_simple_buffer(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
561 	}
562 }
563 
564 /*
565  * Read an MFT structure from the file descriptor.
566  * Result must be passed to mft_free().
567  */
568 struct mft *
mft_read(struct ibuf * b)569 mft_read(struct ibuf *b)
570 {
571 	struct mft	*p = NULL;
572 	size_t		 i;
573 
574 	if ((p = calloc(1, sizeof(struct mft))) == NULL)
575 		err(1, NULL);
576 
577 	io_read_buf(b, &p->repoid, sizeof(p->repoid));
578 	io_read_buf(b, &p->talid, sizeof(p->talid));
579 	io_read_buf(b, &p->certid, sizeof(p->certid));
580 	io_read_buf(b, &p->seqnum_gap, sizeof(p->seqnum_gap));
581 	io_read_str(b, &p->path);
582 
583 	io_read_str(b, &p->aia);
584 	io_read_str(b, &p->aki);
585 	io_read_str(b, &p->ski);
586 	assert(p->aia && p->aki && p->ski);
587 
588 	io_read_buf(b, &p->filesz, sizeof(size_t));
589 	if ((p->files = calloc(p->filesz, sizeof(struct mftfile))) == NULL)
590 		err(1, NULL);
591 
592 	for (i = 0; i < p->filesz; i++) {
593 		io_read_str(b, &p->files[i].file);
594 		io_read_buf(b, &p->files[i].type, sizeof(p->files[i].type));
595 		io_read_buf(b, &p->files[i].location,
596 		    sizeof(p->files[i].location));
597 		io_read_buf(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
598 	}
599 
600 	return p;
601 }
602 
603 /*
604  * Compare the thisupdate time of two mft files.
605  */
606 int
mft_compare_issued(const struct mft * a,const struct mft * b)607 mft_compare_issued(const struct mft *a, const struct mft *b)
608 {
609 	if (a->thisupdate > b->thisupdate)
610 		return 1;
611 	if (a->thisupdate < b->thisupdate)
612 		return -1;
613 	return 0;
614 }
615 
616 /*
617  * Compare the manifestNumber of two mft files.
618  */
619 int
mft_compare_seqnum(const struct mft * a,const struct mft * b)620 mft_compare_seqnum(const struct mft *a, const struct mft *b)
621 {
622 	int r;
623 
624 	r = strlen(a->seqnum) - strlen(b->seqnum);
625 	if (r > 0)	/* seqnum in a is longer -> higher */
626 		return 1;
627 	if (r < 0)	/* seqnum in a is shorter -> smaller */
628 		return -1;
629 
630 	r = strcmp(a->seqnum, b->seqnum);
631 	if (r > 0)	/* a is greater, prefer a */
632 		return 1;
633 	if (r < 0)	/* b is greater, prefer b */
634 		return -1;
635 
636 	return 0;
637 }
638 
639 /*
640  * Test if there is a gap in the sequence numbers of two MFTs.
641  * Return 1 if a gap is detected.
642  */
643 int
mft_seqnum_gap_present(const struct mft * a,const struct mft * b)644 mft_seqnum_gap_present(const struct mft *a, const struct mft *b)
645 {
646 	BIGNUM *diff, *seqnum_a, *seqnum_b;
647 	int ret = 0;
648 
649 	BN_CTX_start(bn_ctx);
650 	if ((diff = BN_CTX_get(bn_ctx)) == NULL ||
651 	    (seqnum_a = BN_CTX_get(bn_ctx)) == NULL ||
652 	    (seqnum_b = BN_CTX_get(bn_ctx)) == NULL)
653 		errx(1, "BN_CTX_get");
654 
655 	if (!BN_hex2bn(&seqnum_a, a->seqnum))
656 		errx(1, "BN_hex2bn");
657 
658 	if (!BN_hex2bn(&seqnum_b, b->seqnum))
659 		errx(1, "BN_hex2bn");
660 
661 	if (!BN_sub(diff, seqnum_a, seqnum_b))
662 		errx(1, "BN_sub");
663 
664 	ret = !BN_is_one(diff);
665 
666 	BN_CTX_end(bn_ctx);
667 
668 	return ret;
669 }
670