]>
Commit | Line | Data |
---|---|---|
62dcd2aa DDO |
1 | /* |
2 | * Copyright 2018-2020 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * Copyright Siemens AG 2018-2020 | |
4 | * | |
5 | * Licensed under the Apache License 2.0 (the "License"). You may not use | |
6 | * this file except in compliance with the License. You can obtain a copy | |
7 | * in the file LICENSE in the source distribution or atf | |
8 | * https://www.openssl.org/source/license.html | |
9 | */ | |
10 | ||
11 | #include "apps.h" | |
12 | #include "cmp_mock_srv.h" | |
13 | ||
14 | #include <openssl/cmp.h> | |
15 | #include <openssl/err.h> | |
16 | #include <openssl/cmperr.h> | |
17 | ||
18 | /* the context for the CMP mock server */ | |
19 | typedef struct | |
20 | { | |
21 | X509 *certOut; /* certificate to be returned in cp/ip/kup msg */ | |
22 | STACK_OF(X509) *chainOut; /* chain of certOut to add to extraCerts field */ | |
23 | STACK_OF(X509) *caPubsOut; /* certs to return in caPubs field of ip msg */ | |
24 | OSSL_CMP_PKISI *statusOut; /* status for ip/cp/kup/rp msg unless polling */ | |
25 | int sendError; /* send error response also on valid requests */ | |
26 | OSSL_CMP_MSG *certReq; /* ir/cr/p10cr/kur remembered while polling */ | |
27 | int certReqId; /* id of last ir/cr/kur, used for polling */ | |
28 | int pollCount; /* number of polls before actual cert response */ | |
29 | int checkAfterTime; /* time the client should wait between polling */ | |
30 | } mock_srv_ctx; | |
31 | ||
32 | ||
33 | static void mock_srv_ctx_free(mock_srv_ctx *ctx) | |
34 | { | |
35 | if (ctx == NULL) | |
36 | return; | |
37 | ||
38 | OSSL_CMP_PKISI_free(ctx->statusOut); | |
39 | X509_free(ctx->certOut); | |
40 | sk_X509_pop_free(ctx->chainOut, X509_free); | |
41 | sk_X509_pop_free(ctx->caPubsOut, X509_free); | |
42 | OSSL_CMP_MSG_free(ctx->certReq); | |
43 | OPENSSL_free(ctx); | |
44 | } | |
45 | ||
46 | static mock_srv_ctx *mock_srv_ctx_new(void) | |
47 | { | |
48 | mock_srv_ctx *ctx = OPENSSL_zalloc(sizeof(mock_srv_ctx)); | |
49 | ||
50 | if (ctx == NULL) | |
51 | goto err; | |
52 | ||
53 | if ((ctx->statusOut = OSSL_CMP_PKISI_new()) == NULL) | |
54 | goto err; | |
55 | ||
56 | ctx->certReqId = -1; | |
57 | ||
58 | /* all other elements are initialized to 0 or NULL, respectively */ | |
59 | return ctx; | |
60 | err: | |
61 | mock_srv_ctx_free(ctx); | |
62 | return NULL; | |
63 | } | |
64 | ||
65 | int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert) | |
66 | { | |
67 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
68 | ||
69 | if (ctx == NULL) { | |
70 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
71 | return 0; | |
72 | } | |
73 | if (cert == NULL || X509_up_ref(cert)) { | |
74 | X509_free(ctx->certOut); | |
75 | ctx->certOut = cert; | |
76 | return 1; | |
77 | } | |
78 | return 0; | |
79 | } | |
80 | ||
81 | int ossl_cmp_mock_srv_set1_chainOut(OSSL_CMP_SRV_CTX *srv_ctx, | |
82 | STACK_OF(X509) *chain) | |
83 | { | |
84 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
85 | STACK_OF(X509) *chain_copy = NULL; | |
86 | ||
87 | if (ctx == NULL) { | |
88 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
89 | return 0; | |
90 | } | |
91 | if (chain != NULL && (chain_copy = X509_chain_up_ref(chain)) == NULL) | |
92 | return 0; | |
93 | sk_X509_pop_free(ctx->chainOut, X509_free); | |
94 | ctx->chainOut = chain_copy; | |
95 | return 1; | |
96 | } | |
97 | ||
98 | int ossl_cmp_mock_srv_set1_caPubsOut(OSSL_CMP_SRV_CTX *srv_ctx, | |
99 | STACK_OF(X509) *caPubs) | |
100 | { | |
101 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
102 | STACK_OF(X509) *caPubs_copy = NULL; | |
103 | ||
104 | if (ctx == NULL) { | |
105 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
106 | return 0; | |
107 | } | |
108 | if (caPubs != NULL && (caPubs_copy = X509_chain_up_ref(caPubs)) == NULL) | |
109 | return 0; | |
110 | sk_X509_pop_free(ctx->caPubsOut, X509_free); | |
111 | ctx->caPubsOut = caPubs_copy; | |
112 | return 1; | |
113 | } | |
114 | ||
115 | int ossl_cmp_mock_srv_set_statusInfo(OSSL_CMP_SRV_CTX *srv_ctx, int status, | |
116 | int fail_info, const char *text) | |
117 | { | |
118 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
119 | OSSL_CMP_PKISI *si; | |
120 | ||
121 | if (ctx == NULL) { | |
122 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
123 | return 0; | |
124 | } | |
125 | if ((si = OSSL_CMP_STATUSINFO_new(status, fail_info, text)) == NULL) | |
126 | return 0; | |
127 | OSSL_CMP_PKISI_free(ctx->statusOut); | |
128 | ctx->statusOut = si; | |
129 | return 1; | |
130 | } | |
131 | ||
132 | int ossl_cmp_mock_srv_set_send_error(OSSL_CMP_SRV_CTX *srv_ctx, int val) | |
133 | { | |
134 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
135 | ||
136 | if (ctx == NULL) { | |
137 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
138 | return 0; | |
139 | } | |
140 | ctx->sendError = val != 0; | |
141 | return 1; | |
142 | } | |
143 | ||
144 | int ossl_cmp_mock_srv_set_pollCount(OSSL_CMP_SRV_CTX *srv_ctx, int count) | |
145 | { | |
146 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
147 | ||
148 | if (ctx == NULL) { | |
149 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
150 | return 0; | |
151 | } | |
152 | if (count < 0) { | |
153 | CMPerr(0, CMP_R_INVALID_ARGS); | |
154 | return 0; | |
155 | } | |
156 | ctx->pollCount = count; | |
157 | return 1; | |
158 | } | |
159 | ||
160 | int ossl_cmp_mock_srv_set_checkAfterTime(OSSL_CMP_SRV_CTX *srv_ctx, int sec) | |
161 | { | |
162 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
163 | ||
164 | if (ctx == NULL) { | |
165 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
166 | return 0; | |
167 | } | |
168 | ctx->checkAfterTime = sec; | |
169 | return 1; | |
170 | } | |
171 | ||
172 | static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx, | |
173 | const OSSL_CMP_MSG *cert_req, | |
174 | int certReqId, | |
175 | const OSSL_CRMF_MSG *crm, | |
176 | const X509_REQ *p10cr, | |
177 | X509 **certOut, | |
178 | STACK_OF(X509) **chainOut, | |
179 | STACK_OF(X509) **caPubs) | |
180 | { | |
181 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
182 | OSSL_CMP_PKISI *si = NULL; | |
183 | ||
184 | if (ctx == NULL || cert_req == NULL | |
185 | || certOut == NULL || chainOut == NULL || caPubs == NULL) { | |
186 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
187 | return NULL; | |
188 | } | |
189 | if (ctx->sendError) { | |
7e765f46 | 190 | CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE); |
62dcd2aa DDO |
191 | return NULL; |
192 | } | |
193 | ||
194 | *certOut = NULL; | |
195 | *chainOut = NULL; | |
196 | *caPubs = NULL; | |
197 | ctx->certReqId = certReqId; | |
198 | if (ctx->pollCount > 0) { | |
199 | ctx->pollCount--; | |
200 | OSSL_CMP_MSG_free(ctx->certReq); | |
201 | if ((ctx->certReq = OSSL_CMP_MSG_dup(cert_req)) == NULL) | |
202 | return NULL; | |
203 | return OSSL_CMP_STATUSINFO_new(OSSL_CMP_PKISTATUS_waiting, 0, NULL); | |
204 | } | |
205 | if (ctx->certOut != NULL | |
206 | && (*certOut = X509_dup(ctx->certOut)) == NULL) | |
207 | goto err; | |
208 | if (ctx->chainOut != NULL | |
209 | && (*chainOut = X509_chain_up_ref(ctx->chainOut)) == NULL) | |
210 | goto err; | |
211 | if (ctx->caPubsOut != NULL | |
212 | && (*caPubs = X509_chain_up_ref(ctx->caPubsOut)) == NULL) | |
213 | goto err; | |
214 | if (ctx->statusOut != NULL | |
215 | && (si = OSSL_CMP_PKISI_dup(ctx->statusOut)) == NULL) | |
216 | goto err; | |
217 | return si; | |
218 | ||
219 | err: | |
220 | X509_free(*certOut); | |
221 | *certOut = NULL; | |
222 | sk_X509_pop_free(*chainOut, X509_free); | |
223 | *chainOut = NULL; | |
224 | sk_X509_pop_free(*caPubs, X509_free); | |
225 | *caPubs = NULL; | |
226 | return NULL; | |
227 | } | |
228 | ||
229 | static OSSL_CMP_PKISI *process_rr(OSSL_CMP_SRV_CTX *srv_ctx, | |
230 | const OSSL_CMP_MSG *rr, | |
231 | const X509_NAME *issuer, | |
232 | const ASN1_INTEGER *serial) | |
233 | { | |
234 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
235 | ||
236 | if (ctx == NULL || rr == NULL || issuer == NULL || serial == NULL) { | |
237 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
238 | return NULL; | |
239 | } | |
240 | if (ctx->sendError || ctx->certOut == NULL) { | |
7e765f46 | 241 | CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE); |
62dcd2aa DDO |
242 | return NULL; |
243 | } | |
244 | ||
245 | /* accept revocation only for the certificate we sent in ir/cr/kur */ | |
246 | if (X509_NAME_cmp(issuer, X509_get_issuer_name(ctx->certOut)) != 0 | |
247 | || ASN1_INTEGER_cmp(serial, | |
248 | X509_get0_serialNumber(ctx->certOut)) != 0) { | |
249 | CMPerr(0, CMP_R_REQUEST_NOT_ACCEPTED); | |
250 | return NULL; | |
251 | } | |
252 | return OSSL_CMP_PKISI_dup(ctx->statusOut); | |
253 | } | |
254 | ||
255 | static int process_genm(OSSL_CMP_SRV_CTX *srv_ctx, | |
256 | const OSSL_CMP_MSG *genm, | |
257 | const STACK_OF(OSSL_CMP_ITAV) *in, | |
258 | STACK_OF(OSSL_CMP_ITAV) **out) | |
259 | { | |
260 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
261 | ||
262 | if (ctx == NULL || genm == NULL || in == NULL || out == NULL) { | |
263 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
264 | return 0; | |
265 | } | |
266 | if (ctx->sendError) { | |
7e765f46 | 267 | CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE); |
62dcd2aa DDO |
268 | return 0; |
269 | } | |
270 | ||
271 | *out = sk_OSSL_CMP_ITAV_deep_copy(in, OSSL_CMP_ITAV_dup, | |
272 | OSSL_CMP_ITAV_free); | |
273 | return *out != NULL; | |
274 | } | |
275 | ||
276 | static void process_error(OSSL_CMP_SRV_CTX *srv_ctx, const OSSL_CMP_MSG *error, | |
277 | const OSSL_CMP_PKISI *statusInfo, | |
278 | const ASN1_INTEGER *errorCode, | |
279 | const OSSL_CMP_PKIFREETEXT *errorDetails) | |
280 | { | |
281 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
282 | char buf[OSSL_CMP_PKISI_BUFLEN]; | |
283 | char *sibuf; | |
284 | int i; | |
285 | ||
286 | if (ctx == NULL || error == NULL) { | |
287 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
288 | return; | |
289 | } | |
290 | ||
291 | BIO_printf(bio_err, "mock server received error:\n"); | |
292 | ||
293 | if (statusInfo == NULL) { | |
294 | BIO_printf(bio_err, "pkiStatusInfo absent\n"); | |
295 | } else { | |
296 | sibuf = OSSL_CMP_snprint_PKIStatusInfo(statusInfo, buf, sizeof(buf)); | |
297 | BIO_printf(bio_err, "pkiStatusInfo: %s\n", | |
298 | sibuf != NULL ? sibuf: "<invalid>"); | |
299 | } | |
300 | ||
301 | if (errorCode == NULL) | |
302 | BIO_printf(bio_err, "errorCode absent\n"); | |
303 | else | |
304 | BIO_printf(bio_err, "errorCode: %ld\n", ASN1_INTEGER_get(errorCode)); | |
305 | ||
306 | if (sk_ASN1_UTF8STRING_num(errorDetails) <= 0) { | |
307 | BIO_printf(bio_err, "errorDetails absent\n"); | |
308 | } else { | |
7e765f46 | 309 | /* TODO could use sk_ASN1_UTF8STRING2text() if exported */ |
62dcd2aa DDO |
310 | BIO_printf(bio_err, "errorDetails: "); |
311 | for (i = 0; i < sk_ASN1_UTF8STRING_num(errorDetails); i++) { | |
312 | if (i > 0) | |
313 | BIO_printf(bio_err, ", "); | |
314 | BIO_printf(bio_err, "\""); | |
315 | ASN1_STRING_print(bio_err, | |
316 | sk_ASN1_UTF8STRING_value(errorDetails, i)); | |
317 | BIO_printf(bio_err, "\""); | |
318 | } | |
319 | BIO_printf(bio_err, "\n"); | |
320 | } | |
321 | } | |
322 | ||
323 | static int process_certConf(OSSL_CMP_SRV_CTX *srv_ctx, | |
324 | const OSSL_CMP_MSG *certConf, int certReqId, | |
325 | const ASN1_OCTET_STRING *certHash, | |
326 | const OSSL_CMP_PKISI *si) | |
327 | { | |
328 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
329 | ASN1_OCTET_STRING *digest; | |
330 | ||
331 | if (ctx == NULL || certConf == NULL || certHash == NULL) { | |
332 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
333 | return 0; | |
334 | } | |
335 | if (ctx->sendError || ctx->certOut == NULL) { | |
7e765f46 | 336 | CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE); |
62dcd2aa DDO |
337 | return 0; |
338 | } | |
339 | ||
340 | if (certReqId != ctx->certReqId) { | |
341 | /* in case of error, invalid reqId -1 */ | |
342 | CMPerr(0, CMP_R_BAD_REQUEST_ID); | |
343 | return 0; | |
344 | } | |
345 | ||
44387c90 | 346 | if ((digest = X509_digest_sig(ctx->certOut)) == NULL) |
62dcd2aa DDO |
347 | return 0; |
348 | if (ASN1_OCTET_STRING_cmp(certHash, digest) != 0) { | |
349 | ASN1_OCTET_STRING_free(digest); | |
350 | CMPerr(0, CMP_R_CERTHASH_UNMATCHED); | |
351 | return 0; | |
352 | } | |
353 | ASN1_OCTET_STRING_free(digest); | |
354 | return 1; | |
355 | } | |
356 | ||
357 | static int process_pollReq(OSSL_CMP_SRV_CTX *srv_ctx, | |
358 | const OSSL_CMP_MSG *pollReq, int certReqId, | |
359 | OSSL_CMP_MSG **certReq, int64_t *check_after) | |
360 | { | |
361 | mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx); | |
362 | ||
363 | if (ctx == NULL || pollReq == NULL | |
364 | || certReq == NULL || check_after == NULL) { | |
365 | CMPerr(0, CMP_R_NULL_ARGUMENT); | |
366 | return 0; | |
367 | } | |
368 | if (ctx->sendError || ctx->certReq == NULL) { | |
369 | *certReq = NULL; | |
7e765f46 | 370 | CMPerr(0, CMP_R_ERROR_PROCESSING_MESSAGE); |
62dcd2aa DDO |
371 | return 0; |
372 | } | |
373 | ||
374 | if (ctx->pollCount == 0) { | |
375 | *certReq = ctx->certReq; | |
376 | ctx->certReq = NULL; | |
377 | *check_after = 0; | |
378 | } else { | |
379 | ctx->pollCount--; | |
380 | *certReq = NULL; | |
381 | *check_after = ctx->checkAfterTime; | |
382 | } | |
383 | return 1; | |
384 | } | |
385 | ||
386 | OSSL_CMP_SRV_CTX *ossl_cmp_mock_srv_new(void) | |
387 | { | |
388 | OSSL_CMP_SRV_CTX *srv_ctx = OSSL_CMP_SRV_CTX_new(); | |
389 | mock_srv_ctx *ctx = mock_srv_ctx_new(); | |
390 | ||
391 | if (srv_ctx != NULL && ctx != NULL | |
392 | && OSSL_CMP_SRV_CTX_init(srv_ctx, ctx, process_cert_request, | |
393 | process_rr, process_genm, process_error, | |
394 | process_certConf, process_pollReq)) | |
395 | return srv_ctx; | |
396 | ||
397 | mock_srv_ctx_free(ctx); | |
398 | OSSL_CMP_SRV_CTX_free(srv_ctx); | |
399 | return NULL; | |
400 | } | |
401 | ||
402 | void ossl_cmp_mock_srv_free(OSSL_CMP_SRV_CTX *srv_ctx) | |
403 | { | |
404 | if (srv_ctx != NULL) | |
405 | mock_srv_ctx_free(OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx)); | |
406 | OSSL_CMP_SRV_CTX_free(srv_ctx); | |
407 | } |