1 | /*
|
---|
2 | * Copyright 2007-2023 The OpenSSL Project Authors. All Rights Reserved.
|
---|
3 | * Copyright Nokia 2007-2020
|
---|
4 | * Copyright Siemens AG 2015-2020
|
---|
5 | *
|
---|
6 | * Licensed under the Apache License 2.0 (the "License"). You may not use
|
---|
7 | * this file except in compliance with the License. You can obtain a copy
|
---|
8 | * in the file LICENSE in the source distribution or at
|
---|
9 | * https://www.openssl.org/source/license.html
|
---|
10 | */
|
---|
11 |
|
---|
12 | /* CMP functions for PKIMessage checking */
|
---|
13 |
|
---|
14 | #include "cmp_local.h"
|
---|
15 | #include <openssl/cmp_util.h>
|
---|
16 |
|
---|
17 | /* explicit #includes not strictly needed since implied by the above: */
|
---|
18 | #include <openssl/asn1t.h>
|
---|
19 | #include <openssl/cmp.h>
|
---|
20 | #include <openssl/crmf.h>
|
---|
21 | #include <openssl/err.h>
|
---|
22 | #include <openssl/x509.h>
|
---|
23 |
|
---|
24 | /* Verify a message protected by signature according to RFC section 5.1.3.3 */
|
---|
25 | static int verify_signature(const OSSL_CMP_CTX *cmp_ctx,
|
---|
26 | const OSSL_CMP_MSG *msg, X509 *cert)
|
---|
27 | {
|
---|
28 | OSSL_CMP_PROTECTEDPART prot_part;
|
---|
29 | EVP_PKEY *pubkey = NULL;
|
---|
30 | BIO *bio;
|
---|
31 | int res = 0;
|
---|
32 |
|
---|
33 | if (!ossl_assert(cmp_ctx != NULL && msg != NULL && cert != NULL))
|
---|
34 | return 0;
|
---|
35 |
|
---|
36 | bio = BIO_new(BIO_s_mem()); /* may be NULL */
|
---|
37 |
|
---|
38 | /* verify that keyUsage, if present, contains digitalSignature */
|
---|
39 | if (!cmp_ctx->ignore_keyusage
|
---|
40 | && (X509_get_key_usage(cert) & X509v3_KU_DIGITAL_SIGNATURE) == 0) {
|
---|
41 | ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE);
|
---|
42 | goto sig_err;
|
---|
43 | }
|
---|
44 |
|
---|
45 | pubkey = X509_get_pubkey(cert);
|
---|
46 | if (pubkey == NULL) {
|
---|
47 | ERR_raise(ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_PUBKEY);
|
---|
48 | goto sig_err;
|
---|
49 | }
|
---|
50 |
|
---|
51 | prot_part.header = msg->header;
|
---|
52 | prot_part.body = msg->body;
|
---|
53 |
|
---|
54 | if (ASN1_item_verify_ex(ASN1_ITEM_rptr(OSSL_CMP_PROTECTEDPART),
|
---|
55 | msg->header->protectionAlg, msg->protection,
|
---|
56 | &prot_part, NULL, pubkey, cmp_ctx->libctx,
|
---|
57 | cmp_ctx->propq) > 0) {
|
---|
58 | res = 1;
|
---|
59 | goto end;
|
---|
60 | }
|
---|
61 |
|
---|
62 | sig_err:
|
---|
63 | res = ossl_x509_print_ex_brief(bio, cert, X509_FLAG_NO_EXTENSIONS);
|
---|
64 | ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_SIGNATURE);
|
---|
65 | if (res)
|
---|
66 | ERR_add_error_mem_bio("\n", bio);
|
---|
67 | res = 0;
|
---|
68 |
|
---|
69 | end:
|
---|
70 | EVP_PKEY_free(pubkey);
|
---|
71 | BIO_free(bio);
|
---|
72 |
|
---|
73 | return res;
|
---|
74 | }
|
---|
75 |
|
---|
76 | /* Verify a message protected with PBMAC */
|
---|
77 | static int verify_PBMAC(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
|
---|
78 | {
|
---|
79 | ASN1_BIT_STRING *protection = NULL;
|
---|
80 | int valid = 0;
|
---|
81 |
|
---|
82 | /* generate expected protection for the message */
|
---|
83 | if ((protection = ossl_cmp_calc_protection(ctx, msg)) == NULL)
|
---|
84 | return 0; /* failed to generate protection string! */
|
---|
85 |
|
---|
86 | valid = msg->protection != NULL && msg->protection->length >= 0
|
---|
87 | && msg->protection->type == protection->type
|
---|
88 | && msg->protection->length == protection->length
|
---|
89 | && CRYPTO_memcmp(msg->protection->data, protection->data,
|
---|
90 | protection->length) == 0;
|
---|
91 | ASN1_BIT_STRING_free(protection);
|
---|
92 | if (!valid)
|
---|
93 | ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_PBM_VALUE);
|
---|
94 |
|
---|
95 | return valid;
|
---|
96 | }
|
---|
97 |
|
---|
98 | /*-
|
---|
99 | * Attempt to validate certificate and path using any given store with trusted
|
---|
100 | * certs (possibly including CRLs and a cert verification callback function)
|
---|
101 | * and non-trusted intermediate certs from the given ctx.
|
---|
102 | *
|
---|
103 | * Returns 1 on successful validation and 0 otherwise.
|
---|
104 | */
|
---|
105 | int OSSL_CMP_validate_cert_path(const OSSL_CMP_CTX *ctx,
|
---|
106 | X509_STORE *trusted_store, X509 *cert)
|
---|
107 | {
|
---|
108 | int valid = 0;
|
---|
109 | X509_STORE_CTX *csc = NULL;
|
---|
110 | int err;
|
---|
111 |
|
---|
112 | if (ctx == NULL || cert == NULL) {
|
---|
113 | ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
|
---|
114 | return 0;
|
---|
115 | }
|
---|
116 |
|
---|
117 | if (trusted_store == NULL) {
|
---|
118 | ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_TRUST_STORE);
|
---|
119 | return 0;
|
---|
120 | }
|
---|
121 |
|
---|
122 | if ((csc = X509_STORE_CTX_new_ex(ctx->libctx, ctx->propq)) == NULL
|
---|
123 | || !X509_STORE_CTX_init(csc, trusted_store,
|
---|
124 | cert, ctx->untrusted))
|
---|
125 | goto err;
|
---|
126 |
|
---|
127 | valid = X509_verify_cert(csc) > 0;
|
---|
128 |
|
---|
129 | /* make sure suitable error is queued even if callback did not do */
|
---|
130 | err = ERR_peek_last_error();
|
---|
131 | if (!valid && ERR_GET_REASON(err) != CMP_R_POTENTIALLY_INVALID_CERTIFICATE)
|
---|
132 | ERR_raise(ERR_LIB_CMP, CMP_R_POTENTIALLY_INVALID_CERTIFICATE);
|
---|
133 |
|
---|
134 | err:
|
---|
135 | /* directly output any fresh errors, needed for check_msg_find_cert() */
|
---|
136 | OSSL_CMP_CTX_print_errors(ctx);
|
---|
137 | X509_STORE_CTX_free(csc);
|
---|
138 | return valid;
|
---|
139 | }
|
---|
140 |
|
---|
141 | /* Return 0 if expect_name != NULL and there is no matching actual_name */
|
---|
142 | static int check_name(const OSSL_CMP_CTX *ctx, int log_success,
|
---|
143 | const char *actual_desc, const X509_NAME *actual_name,
|
---|
144 | const char *expect_desc, const X509_NAME *expect_name)
|
---|
145 | {
|
---|
146 | char *str;
|
---|
147 |
|
---|
148 | if (expect_name == NULL)
|
---|
149 | return 1; /* no expectation, thus trivially fulfilled */
|
---|
150 |
|
---|
151 | /* make sure that a matching name is there */
|
---|
152 | if (actual_name == NULL) {
|
---|
153 | ossl_cmp_log1(WARN, ctx, "missing %s", actual_desc);
|
---|
154 | return 0;
|
---|
155 | }
|
---|
156 | str = X509_NAME_oneline(actual_name, NULL, 0);
|
---|
157 | if (X509_NAME_cmp(actual_name, expect_name) == 0) {
|
---|
158 | if (log_success && str != NULL)
|
---|
159 | ossl_cmp_log2(INFO, ctx, " subject matches %s: %s", expect_desc,
|
---|
160 | str);
|
---|
161 | OPENSSL_free(str);
|
---|
162 | return 1;
|
---|
163 | }
|
---|
164 |
|
---|
165 | if (str != NULL)
|
---|
166 | ossl_cmp_log2(INFO, ctx, " actual name in %s = %s", actual_desc, str);
|
---|
167 | OPENSSL_free(str);
|
---|
168 | if ((str = X509_NAME_oneline(expect_name, NULL, 0)) != NULL)
|
---|
169 | ossl_cmp_log2(INFO, ctx, " does not match %s = %s", expect_desc, str);
|
---|
170 | OPENSSL_free(str);
|
---|
171 | return 0;
|
---|
172 | }
|
---|
173 |
|
---|
174 | /* Return 0 if skid != NULL and there is no matching subject key ID in cert */
|
---|
175 | static int check_kid(const OSSL_CMP_CTX *ctx,
|
---|
176 | const ASN1_OCTET_STRING *ckid,
|
---|
177 | const ASN1_OCTET_STRING *skid)
|
---|
178 | {
|
---|
179 | char *str;
|
---|
180 |
|
---|
181 | if (skid == NULL)
|
---|
182 | return 1; /* no expectation, thus trivially fulfilled */
|
---|
183 |
|
---|
184 | /* make sure that the expected subject key identifier is there */
|
---|
185 | if (ckid == NULL) {
|
---|
186 | ossl_cmp_warn(ctx, "missing Subject Key Identifier in certificate");
|
---|
187 | return 0;
|
---|
188 | }
|
---|
189 | str = OPENSSL_buf2hexstr(ckid->data, ckid->length);
|
---|
190 | if (ASN1_OCTET_STRING_cmp(ckid, skid) == 0) {
|
---|
191 | if (str != NULL)
|
---|
192 | ossl_cmp_log1(INFO, ctx, " subjectKID matches senderKID: %s", str);
|
---|
193 | OPENSSL_free(str);
|
---|
194 | return 1;
|
---|
195 | }
|
---|
196 |
|
---|
197 | if (str != NULL)
|
---|
198 | ossl_cmp_log1(INFO, ctx, " cert Subject Key Identifier = %s", str);
|
---|
199 | OPENSSL_free(str);
|
---|
200 | if ((str = OPENSSL_buf2hexstr(skid->data, skid->length)) != NULL)
|
---|
201 | ossl_cmp_log1(INFO, ctx, " does not match senderKID = %s", str);
|
---|
202 | OPENSSL_free(str);
|
---|
203 | return 0;
|
---|
204 | }
|
---|
205 |
|
---|
206 | static int already_checked(const X509 *cert,
|
---|
207 | const STACK_OF(X509) *already_checked)
|
---|
208 | {
|
---|
209 | int i;
|
---|
210 |
|
---|
211 | for (i = sk_X509_num(already_checked /* may be NULL */); i > 0; i--)
|
---|
212 | if (X509_cmp(sk_X509_value(already_checked, i - 1), cert) == 0)
|
---|
213 | return 1;
|
---|
214 | return 0;
|
---|
215 | }
|
---|
216 |
|
---|
217 | /*-
|
---|
218 | * Check if the given cert is acceptable as sender cert of the given message.
|
---|
219 | * The subject DN must match, the subject key ID as well if present in the msg,
|
---|
220 | * and the cert must be current (checked if ctx->trusted is not NULL).
|
---|
221 | * Note that cert revocation etc. is checked by OSSL_CMP_validate_cert_path().
|
---|
222 | *
|
---|
223 | * Returns 0 on error or not acceptable, else 1.
|
---|
224 | */
|
---|
225 | static int cert_acceptable(const OSSL_CMP_CTX *ctx,
|
---|
226 | const char *desc1, const char *desc2, X509 *cert,
|
---|
227 | const STACK_OF(X509) *already_checked1,
|
---|
228 | const STACK_OF(X509) *already_checked2,
|
---|
229 | const OSSL_CMP_MSG *msg)
|
---|
230 | {
|
---|
231 | X509_STORE *ts = ctx->trusted;
|
---|
232 | int self_issued = X509_check_issued(cert, cert) == X509_V_OK;
|
---|
233 | char *str;
|
---|
234 | X509_VERIFY_PARAM *vpm = ts != NULL ? X509_STORE_get0_param(ts) : NULL;
|
---|
235 | int time_cmp;
|
---|
236 |
|
---|
237 | ossl_cmp_log3(INFO, ctx, " considering %s%s %s with..",
|
---|
238 | self_issued ? "self-issued ": "", desc1, desc2);
|
---|
239 | if ((str = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0)) != NULL)
|
---|
240 | ossl_cmp_log1(INFO, ctx, " subject = %s", str);
|
---|
241 | OPENSSL_free(str);
|
---|
242 | if (!self_issued) {
|
---|
243 | str = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
|
---|
244 | if (str != NULL)
|
---|
245 | ossl_cmp_log1(INFO, ctx, " issuer = %s", str);
|
---|
246 | OPENSSL_free(str);
|
---|
247 | }
|
---|
248 |
|
---|
249 | if (already_checked(cert, already_checked1)
|
---|
250 | || already_checked(cert, already_checked2)) {
|
---|
251 | ossl_cmp_info(ctx, " cert has already been checked");
|
---|
252 | return 0;
|
---|
253 | }
|
---|
254 |
|
---|
255 | time_cmp = X509_cmp_timeframe(vpm, X509_get0_notBefore(cert),
|
---|
256 | X509_get0_notAfter(cert));
|
---|
257 | if (time_cmp != 0) {
|
---|
258 | ossl_cmp_warn(ctx, time_cmp > 0 ? "cert has expired"
|
---|
259 | : "cert is not yet valid");
|
---|
260 | return 0;
|
---|
261 | }
|
---|
262 |
|
---|
263 | if (!check_name(ctx, 1,
|
---|
264 | "cert subject", X509_get_subject_name(cert),
|
---|
265 | "sender field", msg->header->sender->d.directoryName))
|
---|
266 | return 0;
|
---|
267 |
|
---|
268 | if (!check_kid(ctx, X509_get0_subject_key_id(cert), msg->header->senderKID))
|
---|
269 | return 0;
|
---|
270 | /* prevent misleading error later in case x509v3_cache_extensions() fails */
|
---|
271 | if (!ossl_x509v3_cache_extensions(cert)) {
|
---|
272 | ossl_cmp_warn(ctx, "cert appears to be invalid");
|
---|
273 | return 0;
|
---|
274 | }
|
---|
275 | if (!verify_signature(ctx, msg, cert)) {
|
---|
276 | ossl_cmp_warn(ctx, "msg signature verification failed");
|
---|
277 | return 0;
|
---|
278 | }
|
---|
279 | /* acceptable also if there is no senderKID in msg header */
|
---|
280 | ossl_cmp_info(ctx, " cert seems acceptable");
|
---|
281 | return 1;
|
---|
282 | }
|
---|
283 |
|
---|
284 | static int check_cert_path(const OSSL_CMP_CTX *ctx, X509_STORE *store,
|
---|
285 | X509 *scrt)
|
---|
286 | {
|
---|
287 | if (OSSL_CMP_validate_cert_path(ctx, store, scrt))
|
---|
288 | return 1;
|
---|
289 |
|
---|
290 | ossl_cmp_warn(ctx,
|
---|
291 | "msg signature validates but cert path validation failed");
|
---|
292 | return 0;
|
---|
293 | }
|
---|
294 |
|
---|
295 | /*
|
---|
296 | * Exceptional handling for 3GPP TS 33.310 [3G/LTE Network Domain Security
|
---|
297 | * (NDS); Authentication Framework (AF)], only to use for IP messages
|
---|
298 | * and if the ctx option is explicitly set: use self-issued certificates
|
---|
299 | * from extraCerts as trust anchor to validate sender cert -
|
---|
300 | * provided it also can validate the newly enrolled certificate
|
---|
301 | */
|
---|
302 | static int check_cert_path_3gpp(const OSSL_CMP_CTX *ctx,
|
---|
303 | const OSSL_CMP_MSG *msg, X509 *scrt)
|
---|
304 | {
|
---|
305 | int valid = 0;
|
---|
306 | X509_STORE *store;
|
---|
307 |
|
---|
308 | if (!ctx->permitTAInExtraCertsForIR)
|
---|
309 | return 0;
|
---|
310 |
|
---|
311 | if ((store = X509_STORE_new()) == NULL
|
---|
312 | || !ossl_cmp_X509_STORE_add1_certs(store, msg->extraCerts,
|
---|
313 | 1 /* self-issued only */))
|
---|
314 | goto err;
|
---|
315 |
|
---|
316 | /* store does not include CRLs */
|
---|
317 | valid = OSSL_CMP_validate_cert_path(ctx, store, scrt);
|
---|
318 | if (!valid) {
|
---|
319 | ossl_cmp_warn(ctx,
|
---|
320 | "also exceptional 3GPP mode cert path validation failed");
|
---|
321 | } else {
|
---|
322 | /*
|
---|
323 | * verify that the newly enrolled certificate (which assumed rid ==
|
---|
324 | * OSSL_CMP_CERTREQID) can also be validated with the same trusted store
|
---|
325 | */
|
---|
326 | OSSL_CMP_CERTRESPONSE *crep =
|
---|
327 | ossl_cmp_certrepmessage_get0_certresponse(msg->body->value.ip,
|
---|
328 | OSSL_CMP_CERTREQID);
|
---|
329 | X509 *newcrt = ossl_cmp_certresponse_get1_cert(ctx, crep);
|
---|
330 |
|
---|
331 | /*
|
---|
332 | * maybe better use get_cert_status() from cmp_client.c, which catches
|
---|
333 | * errors
|
---|
334 | */
|
---|
335 | valid = OSSL_CMP_validate_cert_path(ctx, store, newcrt);
|
---|
336 | X509_free(newcrt);
|
---|
337 | }
|
---|
338 |
|
---|
339 | err:
|
---|
340 | X509_STORE_free(store);
|
---|
341 | return valid;
|
---|
342 | }
|
---|
343 |
|
---|
344 | static int check_msg_given_cert(const OSSL_CMP_CTX *ctx, X509 *cert,
|
---|
345 | const OSSL_CMP_MSG *msg)
|
---|
346 | {
|
---|
347 | return cert_acceptable(ctx, "previously validated", "sender cert",
|
---|
348 | cert, NULL, NULL, msg)
|
---|
349 | && (check_cert_path(ctx, ctx->trusted, cert)
|
---|
350 | || check_cert_path_3gpp(ctx, msg, cert));
|
---|
351 | }
|
---|
352 |
|
---|
353 | /*-
|
---|
354 | * Try all certs in given list for verifying msg, normally or in 3GPP mode.
|
---|
355 | * If already_checked1 == NULL then certs are assumed to be the msg->extraCerts.
|
---|
356 | * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
|
---|
357 | */
|
---|
358 | static int check_msg_with_certs(OSSL_CMP_CTX *ctx, const STACK_OF(X509) *certs,
|
---|
359 | const char *desc,
|
---|
360 | const STACK_OF(X509) *already_checked1,
|
---|
361 | const STACK_OF(X509) *already_checked2,
|
---|
362 | const OSSL_CMP_MSG *msg, int mode_3gpp)
|
---|
363 | {
|
---|
364 | int in_extraCerts = already_checked1 == NULL;
|
---|
365 | int n_acceptable_certs = 0;
|
---|
366 | int i;
|
---|
367 |
|
---|
368 | if (sk_X509_num(certs) <= 0) {
|
---|
369 | ossl_cmp_log1(WARN, ctx, "no %s", desc);
|
---|
370 | return 0;
|
---|
371 | }
|
---|
372 |
|
---|
373 | for (i = 0; i < sk_X509_num(certs); i++) { /* certs may be NULL */
|
---|
374 | X509 *cert = sk_X509_value(certs, i);
|
---|
375 |
|
---|
376 | if (!ossl_assert(cert != NULL))
|
---|
377 | return 0;
|
---|
378 | if (!cert_acceptable(ctx, "cert from", desc, cert,
|
---|
379 | already_checked1, already_checked2, msg))
|
---|
380 | continue;
|
---|
381 | n_acceptable_certs++;
|
---|
382 | if (mode_3gpp ? check_cert_path_3gpp(ctx, msg, cert)
|
---|
383 | : check_cert_path(ctx, ctx->trusted, cert)) {
|
---|
384 | /* store successful sender cert for further msgs in transaction */
|
---|
385 | if (!X509_up_ref(cert))
|
---|
386 | return 0;
|
---|
387 | if (!ossl_cmp_ctx_set0_validatedSrvCert(ctx, cert)) {
|
---|
388 | X509_free(cert);
|
---|
389 | return 0;
|
---|
390 | }
|
---|
391 | return 1;
|
---|
392 | }
|
---|
393 | }
|
---|
394 | if (in_extraCerts && n_acceptable_certs == 0)
|
---|
395 | ossl_cmp_warn(ctx, "no acceptable cert in extraCerts");
|
---|
396 | return 0;
|
---|
397 | }
|
---|
398 |
|
---|
399 | /*-
|
---|
400 | * Verify msg trying first ctx->untrusted, which should include extraCerts
|
---|
401 | * at its front, then trying the trusted certs in truststore (if any) of ctx.
|
---|
402 | * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
|
---|
403 | */
|
---|
404 | static int check_msg_all_certs(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
|
---|
405 | int mode_3gpp)
|
---|
406 | {
|
---|
407 | int ret = 0;
|
---|
408 |
|
---|
409 | if (mode_3gpp
|
---|
410 | && ((!ctx->permitTAInExtraCertsForIR
|
---|
411 | || OSSL_CMP_MSG_get_bodytype(msg) != OSSL_CMP_PKIBODY_IP)))
|
---|
412 | return 0;
|
---|
413 |
|
---|
414 | ossl_cmp_info(ctx,
|
---|
415 | mode_3gpp ? "normal mode failed; trying now 3GPP mode trusting extraCerts"
|
---|
416 | : "trying first normal mode using trust store");
|
---|
417 | if (check_msg_with_certs(ctx, msg->extraCerts, "extraCerts",
|
---|
418 | NULL, NULL, msg, mode_3gpp))
|
---|
419 | return 1;
|
---|
420 | if (check_msg_with_certs(ctx, ctx->untrusted, "untrusted certs",
|
---|
421 | msg->extraCerts, NULL, msg, mode_3gpp))
|
---|
422 | return 1;
|
---|
423 |
|
---|
424 | if (ctx->trusted == NULL) {
|
---|
425 | ossl_cmp_warn(ctx, mode_3gpp ? "no self-issued extraCerts"
|
---|
426 | : "no trusted store");
|
---|
427 | } else {
|
---|
428 | STACK_OF(X509) *trusted = X509_STORE_get1_all_certs(ctx->trusted);
|
---|
429 | ret = check_msg_with_certs(ctx, trusted,
|
---|
430 | mode_3gpp ? "self-issued extraCerts"
|
---|
431 | : "certs in trusted store",
|
---|
432 | msg->extraCerts, ctx->untrusted,
|
---|
433 | msg, mode_3gpp);
|
---|
434 | sk_X509_pop_free(trusted, X509_free);
|
---|
435 | }
|
---|
436 | return ret;
|
---|
437 | }
|
---|
438 |
|
---|
439 | static int no_log_cb(const char *func, const char *file, int line,
|
---|
440 | OSSL_CMP_severity level, const char *msg)
|
---|
441 | {
|
---|
442 | return 1;
|
---|
443 | }
|
---|
444 |
|
---|
445 | /*-
|
---|
446 | * Verify message signature with any acceptable and valid candidate cert.
|
---|
447 | * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
|
---|
448 | */
|
---|
449 | static int check_msg_find_cert(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
|
---|
450 | {
|
---|
451 | X509 *scrt = ctx->validatedSrvCert; /* previous successful sender cert */
|
---|
452 | GENERAL_NAME *sender = msg->header->sender;
|
---|
453 | char *sname = NULL;
|
---|
454 | char *skid_str = NULL;
|
---|
455 | const ASN1_OCTET_STRING *skid = msg->header->senderKID;
|
---|
456 | OSSL_CMP_log_cb_t backup_log_cb = ctx->log_cb;
|
---|
457 | int res = 0;
|
---|
458 |
|
---|
459 | if (sender == NULL || msg->body == NULL)
|
---|
460 | return 0; /* other NULL cases already have been checked */
|
---|
461 | if (sender->type != GEN_DIRNAME) {
|
---|
462 | ERR_raise(ERR_LIB_CMP, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED);
|
---|
463 | return 0;
|
---|
464 | }
|
---|
465 |
|
---|
466 | /* dump any hitherto errors to avoid confusion when printing further ones */
|
---|
467 | OSSL_CMP_CTX_print_errors(ctx);
|
---|
468 |
|
---|
469 | /* enable clearing irrelevant errors in attempts to validate sender certs */
|
---|
470 | (void)ERR_set_mark();
|
---|
471 | ctx->log_cb = no_log_cb; /* temporarily disable logging */
|
---|
472 |
|
---|
473 | /*
|
---|
474 | * try first cached scrt, used successfully earlier in same transaction,
|
---|
475 | * for validating this and any further msgs where extraCerts may be left out
|
---|
476 | */
|
---|
477 | if (scrt != NULL) {
|
---|
478 | if (check_msg_given_cert(ctx, scrt, msg)) {
|
---|
479 | ctx->log_cb = backup_log_cb;
|
---|
480 | (void)ERR_pop_to_mark();
|
---|
481 | return 1;
|
---|
482 | }
|
---|
483 | /* cached sender cert has shown to be no more successfully usable */
|
---|
484 | (void)ossl_cmp_ctx_set0_validatedSrvCert(ctx, NULL);
|
---|
485 | /* re-do the above check (just) for adding diagnostic information */
|
---|
486 | ossl_cmp_info(ctx,
|
---|
487 | "trying to verify msg signature with previously validated cert");
|
---|
488 | (void)check_msg_given_cert(ctx, scrt, msg);
|
---|
489 | }
|
---|
490 |
|
---|
491 | res = check_msg_all_certs(ctx, msg, 0 /* using ctx->trusted */)
|
---|
492 | || check_msg_all_certs(ctx, msg, 1 /* 3gpp */);
|
---|
493 | ctx->log_cb = backup_log_cb;
|
---|
494 | if (res) {
|
---|
495 | /* discard any diagnostic information on trying to use certs */
|
---|
496 | (void)ERR_pop_to_mark();
|
---|
497 | goto end;
|
---|
498 | }
|
---|
499 | /* failed finding a sender cert that verifies the message signature */
|
---|
500 | (void)ERR_clear_last_mark();
|
---|
501 |
|
---|
502 | sname = X509_NAME_oneline(sender->d.directoryName, NULL, 0);
|
---|
503 | skid_str = skid == NULL ? NULL
|
---|
504 | : OPENSSL_buf2hexstr(skid->data, skid->length);
|
---|
505 | if (ctx->log_cb != NULL) {
|
---|
506 | ossl_cmp_info(ctx, "trying to verify msg signature with a valid cert that..");
|
---|
507 | if (sname != NULL)
|
---|
508 | ossl_cmp_log1(INFO, ctx, "matches msg sender = %s", sname);
|
---|
509 | if (skid_str != NULL)
|
---|
510 | ossl_cmp_log1(INFO, ctx, "matches msg senderKID = %s", skid_str);
|
---|
511 | else
|
---|
512 | ossl_cmp_info(ctx, "while msg header does not contain senderKID");
|
---|
513 | /* re-do the above checks (just) for adding diagnostic information */
|
---|
514 | (void)check_msg_all_certs(ctx, msg, 0 /* using ctx->trusted */);
|
---|
515 | (void)check_msg_all_certs(ctx, msg, 1 /* 3gpp */);
|
---|
516 | }
|
---|
517 |
|
---|
518 | ERR_raise(ERR_LIB_CMP, CMP_R_NO_SUITABLE_SENDER_CERT);
|
---|
519 | if (sname != NULL) {
|
---|
520 | ERR_add_error_txt(NULL, "for msg sender name = ");
|
---|
521 | ERR_add_error_txt(NULL, sname);
|
---|
522 | }
|
---|
523 | if (skid_str != NULL) {
|
---|
524 | ERR_add_error_txt(" and ", "for msg senderKID = ");
|
---|
525 | ERR_add_error_txt(NULL, skid_str);
|
---|
526 | }
|
---|
527 |
|
---|
528 | end:
|
---|
529 | OPENSSL_free(sname);
|
---|
530 | OPENSSL_free(skid_str);
|
---|
531 | return res;
|
---|
532 | }
|
---|
533 |
|
---|
534 | /*-
|
---|
535 | * Validate the protection of the given PKIMessage using either password-
|
---|
536 | * based mac (PBM) or a signature algorithm. In the case of signature algorithm,
|
---|
537 | * the sender certificate can have been pinned by providing it in ctx->srvCert,
|
---|
538 | * else it is searched in msg->extraCerts, ctx->untrusted, in ctx->trusted
|
---|
539 | * (in this order) and is path is validated against ctx->trusted.
|
---|
540 | * On success cache the found cert using ossl_cmp_ctx_set0_validatedSrvCert().
|
---|
541 | *
|
---|
542 | * If ctx->permitTAInExtraCertsForIR is true and when validating a CMP IP msg,
|
---|
543 | * the trust anchor for validating the IP msg may be taken from msg->extraCerts
|
---|
544 | * if a self-issued certificate is found there that can be used to
|
---|
545 | * validate the enrolled certificate returned in the IP.
|
---|
546 | * This is according to the need given in 3GPP TS 33.310.
|
---|
547 | *
|
---|
548 | * Returns 1 on success, 0 on error or validation failed.
|
---|
549 | */
|
---|
550 | int OSSL_CMP_validate_msg(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg)
|
---|
551 | {
|
---|
552 | X509 *scrt;
|
---|
553 |
|
---|
554 | ossl_cmp_debug(ctx, "validating CMP message");
|
---|
555 | if (ctx == NULL || msg == NULL
|
---|
556 | || msg->header == NULL || msg->body == NULL) {
|
---|
557 | ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
|
---|
558 | return 0;
|
---|
559 | }
|
---|
560 |
|
---|
561 | if (msg->header->protectionAlg == NULL /* unprotected message */
|
---|
562 | || msg->protection == NULL || msg->protection->data == NULL) {
|
---|
563 | ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PROTECTION);
|
---|
564 | return 0;
|
---|
565 | }
|
---|
566 |
|
---|
567 | switch (ossl_cmp_hdr_get_protection_nid(msg->header)) {
|
---|
568 | /* 5.1.3.1. Shared Secret Information */
|
---|
569 | case NID_id_PasswordBasedMAC:
|
---|
570 | if (ctx->secretValue == NULL) {
|
---|
571 | ossl_cmp_info(ctx, "no secret available for verifying PBM-based CMP message protection");
|
---|
572 | ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_SECRET);
|
---|
573 | return 0;
|
---|
574 | }
|
---|
575 | if (verify_PBMAC(ctx, msg)) {
|
---|
576 | /*
|
---|
577 | * RFC 4210, 5.3.2: 'Note that if the PKI Message Protection is
|
---|
578 | * "shared secret information", then any certificate transported in
|
---|
579 | * the caPubs field may be directly trusted as a root CA
|
---|
580 | * certificate by the initiator.'
|
---|
581 | */
|
---|
582 | switch (OSSL_CMP_MSG_get_bodytype(msg)) {
|
---|
583 | case -1:
|
---|
584 | return 0;
|
---|
585 | case OSSL_CMP_PKIBODY_IP:
|
---|
586 | case OSSL_CMP_PKIBODY_CP:
|
---|
587 | case OSSL_CMP_PKIBODY_KUP:
|
---|
588 | case OSSL_CMP_PKIBODY_CCP:
|
---|
589 | if (ctx->trusted != NULL) {
|
---|
590 | STACK_OF(X509) *certs = msg->body->value.ip->caPubs;
|
---|
591 | /* value.ip is same for cp, kup, and ccp */
|
---|
592 |
|
---|
593 | if (!ossl_cmp_X509_STORE_add1_certs(ctx->trusted, certs, 0))
|
---|
594 | /* adds both self-issued and not self-issued certs */
|
---|
595 | return 0;
|
---|
596 | }
|
---|
597 | break;
|
---|
598 | default:
|
---|
599 | break;
|
---|
600 | }
|
---|
601 | ossl_cmp_debug(ctx,
|
---|
602 | "successfully validated PBM-based CMP message protection");
|
---|
603 | return 1;
|
---|
604 | }
|
---|
605 | ossl_cmp_warn(ctx, "verifying PBM-based CMP message protection failed");
|
---|
606 | break;
|
---|
607 |
|
---|
608 | /*
|
---|
609 | * 5.1.3.2 DH Key Pairs
|
---|
610 | * Not yet supported
|
---|
611 | */
|
---|
612 | case NID_id_DHBasedMac:
|
---|
613 | ERR_raise(ERR_LIB_CMP, CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC);
|
---|
614 | break;
|
---|
615 |
|
---|
616 | /*
|
---|
617 | * 5.1.3.3. Signature
|
---|
618 | */
|
---|
619 | default:
|
---|
620 | scrt = ctx->srvCert;
|
---|
621 | if (scrt == NULL) {
|
---|
622 | if (ctx->trusted == NULL) {
|
---|
623 | ossl_cmp_info(ctx, "no trust store nor pinned server cert available for verifying signature-based CMP message protection");
|
---|
624 | ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_TRUST_ANCHOR);
|
---|
625 | return 0;
|
---|
626 | }
|
---|
627 | if (check_msg_find_cert(ctx, msg))
|
---|
628 | return 1;
|
---|
629 | } else { /* use pinned sender cert */
|
---|
630 | /* use ctx->srvCert for signature check even if not acceptable */
|
---|
631 | if (verify_signature(ctx, msg, scrt)) {
|
---|
632 | ossl_cmp_debug(ctx,
|
---|
633 | "successfully validated signature-based CMP message protection");
|
---|
634 |
|
---|
635 | return 1;
|
---|
636 | }
|
---|
637 | ossl_cmp_warn(ctx, "CMP message signature verification failed");
|
---|
638 | ERR_raise(ERR_LIB_CMP, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG);
|
---|
639 | }
|
---|
640 | break;
|
---|
641 | }
|
---|
642 | return 0;
|
---|
643 | }
|
---|
644 |
|
---|
645 | /*-
|
---|
646 | * Check received message (i.e., response by server or request from client)
|
---|
647 | * Any msg->extraCerts are prepended to ctx->untrusted.
|
---|
648 | *
|
---|
649 | * Ensures that:
|
---|
650 | * its sender is of appropriate type (currently only X509_NAME) and
|
---|
651 | * matches any expected sender or srvCert subject given in the ctx
|
---|
652 | * it has a valid body type
|
---|
653 | * its protection is valid (or invalid/absent, but only if a callback function
|
---|
654 | * is present and yields a positive result using also the supplied argument)
|
---|
655 | * its transaction ID matches the previous transaction ID stored in ctx (if any)
|
---|
656 | * its recipNonce matches the previous senderNonce stored in the ctx (if any)
|
---|
657 | *
|
---|
658 | * If everything is fine:
|
---|
659 | * learns the senderNonce from the received message,
|
---|
660 | * learns the transaction ID if it is not yet in ctx,
|
---|
661 | * and makes any certs in caPubs directly trusted.
|
---|
662 | *
|
---|
663 | * Returns 1 on success, 0 on error.
|
---|
664 | */
|
---|
665 | int ossl_cmp_msg_check_update(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *msg,
|
---|
666 | ossl_cmp_allow_unprotected_cb_t cb, int cb_arg)
|
---|
667 | {
|
---|
668 | OSSL_CMP_PKIHEADER *hdr;
|
---|
669 | const X509_NAME *expected_sender;
|
---|
670 |
|
---|
671 | if (!ossl_assert(ctx != NULL && msg != NULL && msg->header != NULL))
|
---|
672 | return 0;
|
---|
673 | hdr = OSSL_CMP_MSG_get0_header(msg);
|
---|
674 |
|
---|
675 | /* validate sender name of received msg */
|
---|
676 | if (hdr->sender->type != GEN_DIRNAME) {
|
---|
677 | ERR_raise(ERR_LIB_CMP, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED);
|
---|
678 | return 0;
|
---|
679 | }
|
---|
680 | /*
|
---|
681 | * Compare actual sender name of response with expected sender name.
|
---|
682 | * Mitigates risk to accept misused PBM secret
|
---|
683 | * or misused certificate of an unauthorized entity of a trusted hierarchy.
|
---|
684 | */
|
---|
685 | expected_sender = ctx->expected_sender;
|
---|
686 | if (expected_sender == NULL && ctx->srvCert != NULL)
|
---|
687 | expected_sender = X509_get_subject_name(ctx->srvCert);
|
---|
688 | if (!check_name(ctx, 0, "sender DN field", hdr->sender->d.directoryName,
|
---|
689 | "expected sender", expected_sender))
|
---|
690 | return 0;
|
---|
691 | /* Note: if recipient was NULL-DN it could be learned here if needed */
|
---|
692 |
|
---|
693 | if (sk_X509_num(msg->extraCerts) > 10)
|
---|
694 | ossl_cmp_warn(ctx,
|
---|
695 | "received CMP message contains more than 10 extraCerts");
|
---|
696 | /*
|
---|
697 | * Store any provided extraCerts in ctx for use in OSSL_CMP_validate_msg()
|
---|
698 | * and for future use, such that they are available to ctx->certConf_cb and
|
---|
699 | * the peer does not need to send them again in the same transaction.
|
---|
700 | * Note that it does not help validating the message before storing the
|
---|
701 | * extraCerts because they do not belong to the protected msg part anyway.
|
---|
702 | * For efficiency, the extraCerts are prepended so they get used first.
|
---|
703 | */
|
---|
704 | if (!X509_add_certs(ctx->untrusted, msg->extraCerts,
|
---|
705 | /* this allows self-signed certs */
|
---|
706 | X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP
|
---|
707 | | X509_ADD_FLAG_PREPEND))
|
---|
708 | return 0;
|
---|
709 |
|
---|
710 | /* validate message protection */
|
---|
711 | if (hdr->protectionAlg != NULL) {
|
---|
712 | /* detect explicitly permitted exceptions for invalid protection */
|
---|
713 | if (!OSSL_CMP_validate_msg(ctx, msg)
|
---|
714 | && (cb == NULL || (*cb)(ctx, msg, 1, cb_arg) <= 0)) {
|
---|
715 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
716 | ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_PROTECTION);
|
---|
717 | return 0;
|
---|
718 | #endif
|
---|
719 | }
|
---|
720 | } else {
|
---|
721 | /* detect explicitly permitted exceptions for missing protection */
|
---|
722 | if (cb == NULL || (*cb)(ctx, msg, 0, cb_arg) <= 0) {
|
---|
723 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
724 | ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PROTECTION);
|
---|
725 | return 0;
|
---|
726 | #endif
|
---|
727 | }
|
---|
728 | }
|
---|
729 |
|
---|
730 | /* check CMP version number in header */
|
---|
731 | if (ossl_cmp_hdr_get_pvno(hdr) != OSSL_CMP_PVNO) {
|
---|
732 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
733 | ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_PVNO);
|
---|
734 | return 0;
|
---|
735 | #endif
|
---|
736 | }
|
---|
737 |
|
---|
738 | if (OSSL_CMP_MSG_get_bodytype(msg) < 0) {
|
---|
739 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
740 | ERR_raise(ERR_LIB_CMP, CMP_R_PKIBODY_ERROR);
|
---|
741 | return 0;
|
---|
742 | #endif
|
---|
743 | }
|
---|
744 |
|
---|
745 | /* compare received transactionID with the expected one in previous msg */
|
---|
746 | if (ctx->transactionID != NULL
|
---|
747 | && (hdr->transactionID == NULL
|
---|
748 | || ASN1_OCTET_STRING_cmp(ctx->transactionID,
|
---|
749 | hdr->transactionID) != 0)) {
|
---|
750 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
751 | ERR_raise(ERR_LIB_CMP, CMP_R_TRANSACTIONID_UNMATCHED);
|
---|
752 | return 0;
|
---|
753 | #endif
|
---|
754 | }
|
---|
755 |
|
---|
756 | /* compare received nonce with the one we sent */
|
---|
757 | if (ctx->senderNonce != NULL
|
---|
758 | && (msg->header->recipNonce == NULL
|
---|
759 | || ASN1_OCTET_STRING_cmp(ctx->senderNonce,
|
---|
760 | hdr->recipNonce) != 0)) {
|
---|
761 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
762 | ERR_raise(ERR_LIB_CMP, CMP_R_RECIPNONCE_UNMATCHED);
|
---|
763 | return 0;
|
---|
764 | #endif
|
---|
765 | }
|
---|
766 |
|
---|
767 | /* if not yet present, learn transactionID */
|
---|
768 | if (ctx->transactionID == NULL
|
---|
769 | && !OSSL_CMP_CTX_set1_transactionID(ctx, hdr->transactionID))
|
---|
770 | return 0;
|
---|
771 |
|
---|
772 | /*
|
---|
773 | * RFC 4210 section 5.1.1 states: the recipNonce is copied from
|
---|
774 | * the senderNonce of the previous message in the transaction.
|
---|
775 | * --> Store for setting in next message
|
---|
776 | */
|
---|
777 | if (!ossl_cmp_ctx_set1_recipNonce(ctx, hdr->senderNonce))
|
---|
778 | return 0;
|
---|
779 |
|
---|
780 | /*
|
---|
781 | * Store any provided extraCerts in ctx for future use,
|
---|
782 | * such that they are available to ctx->certConf_cb and
|
---|
783 | * the peer does not need to send them again in the same transaction.
|
---|
784 | * For efficiency, the extraCerts are prepended so they get used first.
|
---|
785 | */
|
---|
786 | if (!X509_add_certs(ctx->untrusted, msg->extraCerts,
|
---|
787 | /* this allows self-signed certs */
|
---|
788 | X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP
|
---|
789 | | X509_ADD_FLAG_PREPEND))
|
---|
790 | return 0;
|
---|
791 |
|
---|
792 | if (ossl_cmp_hdr_get_protection_nid(hdr) == NID_id_PasswordBasedMAC) {
|
---|
793 | /*
|
---|
794 | * RFC 4210, 5.3.2: 'Note that if the PKI Message Protection is
|
---|
795 | * "shared secret information", then any certificate transported in
|
---|
796 | * the caPubs field may be directly trusted as a root CA
|
---|
797 | * certificate by the initiator.'
|
---|
798 | */
|
---|
799 | switch (OSSL_CMP_MSG_get_bodytype(msg)) {
|
---|
800 | case OSSL_CMP_PKIBODY_IP:
|
---|
801 | case OSSL_CMP_PKIBODY_CP:
|
---|
802 | case OSSL_CMP_PKIBODY_KUP:
|
---|
803 | case OSSL_CMP_PKIBODY_CCP:
|
---|
804 | if (ctx->trusted != NULL) {
|
---|
805 | STACK_OF(X509) *certs = msg->body->value.ip->caPubs;
|
---|
806 | /* value.ip is same for cp, kup, and ccp */
|
---|
807 |
|
---|
808 | if (!ossl_cmp_X509_STORE_add1_certs(ctx->trusted, certs, 0))
|
---|
809 | /* adds both self-issued and not self-issued certs */
|
---|
810 | return 0;
|
---|
811 | }
|
---|
812 | break;
|
---|
813 | default:
|
---|
814 | break;
|
---|
815 | }
|
---|
816 | }
|
---|
817 | return 1;
|
---|
818 | }
|
---|
819 |
|
---|
820 | int ossl_cmp_verify_popo(const OSSL_CMP_CTX *ctx,
|
---|
821 | const OSSL_CMP_MSG *msg, int acceptRAVerified)
|
---|
822 | {
|
---|
823 | if (!ossl_assert(msg != NULL && msg->body != NULL))
|
---|
824 | return 0;
|
---|
825 | switch (msg->body->type) {
|
---|
826 | case OSSL_CMP_PKIBODY_P10CR:
|
---|
827 | {
|
---|
828 | X509_REQ *req = msg->body->value.p10cr;
|
---|
829 |
|
---|
830 | if (X509_REQ_verify_ex(req, X509_REQ_get0_pubkey(req), ctx->libctx,
|
---|
831 | ctx->propq) <= 0) {
|
---|
832 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
833 | ERR_raise(ERR_LIB_CMP, CMP_R_REQUEST_NOT_ACCEPTED);
|
---|
834 | return 0;
|
---|
835 | #endif
|
---|
836 | }
|
---|
837 | }
|
---|
838 | break;
|
---|
839 | case OSSL_CMP_PKIBODY_IR:
|
---|
840 | case OSSL_CMP_PKIBODY_CR:
|
---|
841 | case OSSL_CMP_PKIBODY_KUR:
|
---|
842 | if (!OSSL_CRMF_MSGS_verify_popo(msg->body->value.ir, OSSL_CMP_CERTREQID,
|
---|
843 | acceptRAVerified,
|
---|
844 | ctx->libctx, ctx->propq)) {
|
---|
845 | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
---|
846 | return 0;
|
---|
847 | #endif
|
---|
848 | }
|
---|
849 | break;
|
---|
850 | default:
|
---|
851 | ERR_raise(ERR_LIB_CMP, CMP_R_PKIBODY_ERROR);
|
---|
852 | return 0;
|
---|
853 | }
|
---|
854 | return 1;
|
---|
855 | }
|
---|