1 #include "manifest.h"
2 
3 #include <errno.h>
4 
5 #include "algorithm.h"
6 #include "common.h"
7 #include "log.h"
8 #include "thread_var.h"
9 #include "asn1/decode.h"
10 #include "asn1/oid.h"
11 #include "asn1/asn1c/GeneralizedTime.h"
12 #include "asn1/asn1c/Manifest.h"
13 #include "crypto/hash.h"
14 #include "object/certificate.h"
15 #include "object/crl.h"
16 #include "object/roa.h"
17 #include "object/signed_object.h"
18 
19 static int
decode_manifest(struct signed_object * sobj,struct Manifest ** result)20 decode_manifest(struct signed_object *sobj, struct Manifest **result)
21 {
22 	return asn1_decode_octet_string(
23 		sobj->sdata.decoded->encapContentInfo.eContent,
24 		&asn_DEF_Manifest,
25 		(void **) result,
26 		true,
27 		false
28 	);
29 }
30 
31 static int
validate_dates(GeneralizedTime_t * this,GeneralizedTime_t * next)32 validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next)
33 {
34 #define TM_FMT "%02d/%02d/%02d %02d:%02d:%02d"
35 #define TM_ARGS(tm)							\
36 	tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,			\
37 	tm.tm_hour, tm.tm_min, tm.tm_sec
38 
39 	time_t thisUpdate;
40 	time_t nextUpdate;
41 	time_t now;
42 	struct tm thisUpdate_tm;
43 	struct tm nextUpdate_tm;
44 	int error;
45 
46 	/*
47 	 * BTW: We only need the tm variables for error messages, which are
48 	 * rarely needed.
49 	 * So maybe we could get a small performance boost by postponing the
50 	 * calls to localtime_r().
51 	 */
52 	thisUpdate = asn_GT2time(this, &thisUpdate_tm, false);
53 	nextUpdate = asn_GT2time(next, &nextUpdate_tm, false);
54 
55 	if (difftime(thisUpdate, nextUpdate) > 0) {
56 		return pr_val_err(
57 		    "Manifest's thisUpdate (" TM_FMT ") > nextUpdate ("
58 		        TM_FMT ").",
59 		    TM_ARGS(thisUpdate_tm),
60 		    TM_ARGS(nextUpdate_tm));
61 	}
62 
63 	now = 0;
64 	error = get_current_time(&now);
65 	if (error)
66 		return error;
67 
68 	if (difftime(now, thisUpdate) < 0) {
69 		return pr_val_err(
70 		    "Manifest is not valid yet. (thisUpdate: " TM_FMT ")",
71 		    TM_ARGS(thisUpdate_tm));
72 	}
73 	if (difftime(now, nextUpdate) > 0) {
74 		return incidence(INID_MFT_STALE,
75 		    "Manifest is stale. (nextUpdate: " TM_FMT ")",
76 		    TM_ARGS(nextUpdate_tm));
77 	}
78 
79 	return 0;
80 
81 #undef TM_FMT
82 #undef TM_ARGS
83 }
84 
85 static int
validate_manifest(struct Manifest * manifest)86 validate_manifest(struct Manifest *manifest)
87 {
88 	unsigned long version;
89 	int error;
90 
91 	/* rfc6486#section-4.2.1 */
92 
93 	/*
94 	 * BTW:
95 	 *
96 	 * "If a "one-time-use" EE certificate is employed to verify a manifest,
97 	 * the EE certificate MUST have a validity period that coincides with
98 	 * the interval from thisUpdate to nextUpdate, to prevent needless
99 	 * growth of the CA's CRL."
100 	 *
101 	 * "If a "sequential-use" EE certificate is employed to verify a
102 	 * manifest, the EE certificate's validity period needs to be no shorter
103 	 * than the nextUpdate time of the current manifest."
104 	 *
105 	 * It would appear that there's no way to tell whether an EE certificate
106 	 * is "one-time-use" or "sequential-use," so we have no way to validate
107 	 * this.
108 	 */
109 
110 	/* rfc6486#section-4.4.2 */
111 	if (manifest->version != NULL) {
112 		error = asn_INTEGER2ulong(manifest->version, &version);
113 		if (error) {
114 			if (errno)
115 				pr_val_errno(errno, "Error casting manifest version");
116 			return pr_val_err("The manifest version isn't a valid unsigned long");
117 		}
118 		if (version != 0)
119 			return -EINVAL;
120 	}
121 
122 	/*
123 	 * "Manifest verifiers MUST be able to handle number values up to
124 	 * 20 octets."
125 	 */
126 	if (manifest->manifestNumber.size > 20)
127 		return pr_val_err("Manifest number is larger than 20 octets");
128 
129 	/* rfc6486#section-4.4.3 */
130 	error = validate_dates(&manifest->thisUpdate, &manifest->nextUpdate);
131 	if (error)
132 		return error;
133 
134 	/* rfc6486#section-4.2.1.fileHashAlg */
135 	/*
136 	 * Um, RFC 7935 does not declare a hash algorithm specifically intended
137 	 * for manifest hashes. But all the hashes it declares are SHA256, so
138 	 * I guess we'll just default to that.
139 	 * I'm going with the signed object hash function, since it appears to
140 	 * be the closest match.
141 	 */
142 	error = validate_cms_hashing_algorithm_oid(&manifest->fileHashAlg,
143 	    "manifest file");
144 	if (error)
145 		return error;
146 
147 	/* The file hashes will be validated during the traversal. */
148 
149 	return 0;
150 }
151 
152 static int
build_rpp(struct Manifest * mft,struct rpki_uri * mft_uri,bool rrdp_workspace,struct rpp ** pp)153 build_rpp(struct Manifest *mft, struct rpki_uri *mft_uri, bool rrdp_workspace,
154     struct rpp **pp)
155 {
156 	int i;
157 	struct FileAndHash *fah;
158 	struct rpki_uri *uri;
159 	int error;
160 
161 	*pp = rpp_create();
162 	if (*pp == NULL)
163 		return pr_enomem();
164 
165 	for (i = 0; i < mft->fileList.list.count; i++) {
166 		fah = mft->fileList.list.array[i];
167 
168 		error = uri_create_mft(&uri, mft_uri, &fah->file,
169 		    rrdp_workspace);
170 		if (error == ESKIP)
171 			continue;
172 		/*
173 		 * Not handling ENOTRSYNC is fine because the manifest URL
174 		 * should have been RSYNC. Something went wrong if an RSYNC URL
175 		 * plus a relative path is not RSYNC.
176 		 */
177 		if (error)
178 			goto fail;
179 
180 		/*
181 		 * Expect:
182 		 * - Negative value: an error not to be ignored, the whole
183 		 *   manifest will be discarded.
184 		 * - Zero value: hash at manifest matches file's hash, or it
185 		 *   doesn't match its hash but there's an incidence to ignore
186 		 *   such error.
187 		 * - Positive value: file doesn't exist and keep validating
188 		 *   manifest.
189 		 */
190 		error = hash_validate_mft_file("sha256", uri, &fah->hash);
191 		if (error < 0) {
192 			uri_refput(uri);
193 			goto fail;
194 		}
195 		if (error > 0) {
196 			uri_refput(uri);
197 			continue;
198 		}
199 
200 		if (uri_has_extension(uri, ".cer"))
201 			error = rpp_add_cert(*pp, uri);
202 		else if (uri_has_extension(uri, ".roa"))
203 			error = rpp_add_roa(*pp, uri);
204 		else if (uri_has_extension(uri, ".crl"))
205 			error = rpp_add_crl(*pp, uri);
206 		else if (uri_has_extension(uri, ".gbr"))
207 			error = rpp_add_ghostbusters(*pp, uri);
208 		else
209 			uri_refput(uri); /* ignore it. */
210 
211 		if (error) {
212 			uri_refput(uri);
213 			goto fail;
214 		} /* Otherwise ownership was transferred to @pp. */
215 	}
216 
217 	/* rfc6486#section-7 */
218 	if (rpp_get_crl(*pp) == NULL) {
219 		error = pr_val_err("Manifest lacks a CRL.");
220 		goto fail;
221 	}
222 
223 	return 0;
224 
225 fail:
226 	rpp_refput(*pp);
227 	return error;
228 }
229 
230 /**
231  * Validates the manifest pointed by @uri, returns the RPP described by it in
232  * @pp. If @rrdp_workspace is true, use the local RRDP repository.
233  */
234 int
handle_manifest(struct rpki_uri * uri,bool rrdp_workspace,struct rpp ** pp)235 handle_manifest(struct rpki_uri *uri, bool rrdp_workspace, struct rpp **pp)
236 {
237 	static OID oid = OID_MANIFEST;
238 	struct oid_arcs arcs = OID2ARCS("manifest", oid);
239 	struct signed_object sobj;
240 	struct signed_object_args sobj_args;
241 	struct Manifest *mft;
242 	STACK_OF(X509_CRL) *crl;
243 	int error;
244 
245 	/* Prepare */
246 	pr_val_debug("Manifest '%s' {", uri_val_get_printable(uri));
247 	fnstack_push_uri(uri);
248 
249 	/* Decode */
250 	error = signed_object_decode(&sobj, uri);
251 	if (error)
252 		goto revert_log;
253 	error = decode_manifest(&sobj, &mft);
254 	if (error)
255 		goto revert_sobj;
256 
257 	/* Initialize out parameter (@pp) */
258 	error = build_rpp(mft, uri, rrdp_workspace, pp);
259 	if (error)
260 		goto revert_manifest;
261 
262 	/* Prepare validation arguments */
263 	error = rpp_crl(*pp, &crl);
264 	if (error)
265 		goto revert_rpp;
266 	error = signed_object_args_init(&sobj_args, uri, crl, false);
267 	if (error)
268 		goto revert_rpp;
269 
270 	/* Validate everything */
271 	error = signed_object_validate(&sobj, &arcs, &sobj_args);
272 	if (error)
273 		goto revert_args;
274 	error = validate_manifest(mft);
275 	if (error)
276 		goto revert_args;
277 	error = refs_validate_ee(&sobj_args.refs, *pp, uri);
278 	if (error)
279 		goto revert_args;
280 
281 	/* Success */
282 	signed_object_args_cleanup(&sobj_args);
283 	goto revert_manifest;
284 
285 revert_args:
286 	signed_object_args_cleanup(&sobj_args);
287 revert_rpp:
288 	rpp_refput(*pp);
289 revert_manifest:
290 	ASN_STRUCT_FREE(asn_DEF_Manifest, mft);
291 revert_sobj:
292 	signed_object_cleanup(&sobj);
293 revert_log:
294 	pr_val_debug("}");
295 	fnstack_pop();
296 	return error;
297 }
298