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