diff options
Diffstat (limited to 'src/usr.bin/openssl/ca.c')
-rw-r--r-- | src/usr.bin/openssl/ca.c | 2797 |
1 files changed, 0 insertions, 2797 deletions
diff --git a/src/usr.bin/openssl/ca.c b/src/usr.bin/openssl/ca.c deleted file mode 100644 index b644b746b9..0000000000 --- a/src/usr.bin/openssl/ca.c +++ /dev/null | |||
@@ -1,2797 +0,0 @@ | |||
1 | /* $OpenBSD: ca.c,v 1.62 2025/04/14 08:39:27 tb Exp $ */ | ||
2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This package is an SSL implementation written | ||
6 | * by Eric Young (eay@cryptsoft.com). | ||
7 | * The implementation was written so as to conform with Netscapes SSL. | ||
8 | * | ||
9 | * This library is free for commercial and non-commercial use as long as | ||
10 | * the following conditions are aheared to. The following conditions | ||
11 | * apply to all code found in this distribution, be it the RC4, RSA, | ||
12 | * lhash, DES, etc., code; not just the SSL code. The SSL documentation | ||
13 | * included with this distribution is covered by the same copyright terms | ||
14 | * except that the holder is Tim Hudson (tjh@cryptsoft.com). | ||
15 | * | ||
16 | * Copyright remains Eric Young's, and as such any Copyright notices in | ||
17 | * the code are not to be removed. | ||
18 | * If this package is used in a product, Eric Young should be given attribution | ||
19 | * as the author of the parts of the library used. | ||
20 | * This can be in the form of a textual message at program startup or | ||
21 | * in documentation (online or textual) provided with the package. | ||
22 | * | ||
23 | * Redistribution and use in source and binary forms, with or without | ||
24 | * modification, are permitted provided that the following conditions | ||
25 | * are met: | ||
26 | * 1. Redistributions of source code must retain the copyright | ||
27 | * notice, this list of conditions and the following disclaimer. | ||
28 | * 2. Redistributions in binary form must reproduce the above copyright | ||
29 | * notice, this list of conditions and the following disclaimer in the | ||
30 | * documentation and/or other materials provided with the distribution. | ||
31 | * 3. All advertising materials mentioning features or use of this software | ||
32 | * must display the following acknowledgement: | ||
33 | * "This product includes cryptographic software written by | ||
34 | * Eric Young (eay@cryptsoft.com)" | ||
35 | * The word 'cryptographic' can be left out if the rouines from the library | ||
36 | * being used are not cryptographic related :-). | ||
37 | * 4. If you include any Windows specific code (or a derivative thereof) from | ||
38 | * the apps directory (application code) you must include an acknowledgement: | ||
39 | * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" | ||
40 | * | ||
41 | * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND | ||
42 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
43 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
44 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||
45 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
46 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
47 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
48 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
49 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
50 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
51 | * SUCH DAMAGE. | ||
52 | * | ||
53 | * The licence and distribution terms for any publically available version or | ||
54 | * derivative of this code cannot be changed. i.e. this code cannot simply be | ||
55 | * copied and put under another distribution licence | ||
56 | * [including the GNU Public Licence.] | ||
57 | */ | ||
58 | |||
59 | /* The PPKI stuff has been donated by Jeff Barber <jeffb@issl.atl.hp.com> */ | ||
60 | |||
61 | #include <sys/types.h> | ||
62 | |||
63 | #include <ctype.h> | ||
64 | #include <stdio.h> | ||
65 | #include <stdlib.h> | ||
66 | #include <limits.h> | ||
67 | #include <string.h> | ||
68 | #include <unistd.h> | ||
69 | |||
70 | #include "apps.h" | ||
71 | |||
72 | #include <openssl/bio.h> | ||
73 | #include <openssl/bn.h> | ||
74 | #include <openssl/conf.h> | ||
75 | #include <openssl/err.h> | ||
76 | #include <openssl/evp.h> | ||
77 | #include <openssl/objects.h> | ||
78 | #include <openssl/ocsp.h> | ||
79 | #include <openssl/pem.h> | ||
80 | #include <openssl/txt_db.h> | ||
81 | #include <openssl/x509.h> | ||
82 | #include <openssl/x509v3.h> | ||
83 | |||
84 | #define BASE_SECTION "ca" | ||
85 | |||
86 | #define ENV_DEFAULT_CA "default_ca" | ||
87 | |||
88 | #define STRING_MASK "string_mask" | ||
89 | #define UTF8_IN "utf8" | ||
90 | |||
91 | #define ENV_NEW_CERTS_DIR "new_certs_dir" | ||
92 | #define ENV_CERTIFICATE "certificate" | ||
93 | #define ENV_SERIAL "serial" | ||
94 | #define ENV_CRLNUMBER "crlnumber" | ||
95 | #define ENV_PRIVATE_KEY "private_key" | ||
96 | #define ENV_DEFAULT_DAYS "default_days" | ||
97 | #define ENV_DEFAULT_STARTDATE "default_startdate" | ||
98 | #define ENV_DEFAULT_ENDDATE "default_enddate" | ||
99 | #define ENV_DEFAULT_CRL_DAYS "default_crl_days" | ||
100 | #define ENV_DEFAULT_CRL_HOURS "default_crl_hours" | ||
101 | #define ENV_DEFAULT_MD "default_md" | ||
102 | #define ENV_DEFAULT_EMAIL_DN "email_in_dn" | ||
103 | #define ENV_PRESERVE "preserve" | ||
104 | #define ENV_POLICY "policy" | ||
105 | #define ENV_EXTENSIONS "x509_extensions" | ||
106 | #define ENV_CRLEXT "crl_extensions" | ||
107 | #define ENV_NAMEOPT "name_opt" | ||
108 | #define ENV_CERTOPT "cert_opt" | ||
109 | #define ENV_EXTCOPY "copy_extensions" | ||
110 | #define ENV_UNIQUE_SUBJECT "unique_subject" | ||
111 | |||
112 | #define ENV_DATABASE "database" | ||
113 | |||
114 | /* Additional revocation information types */ | ||
115 | |||
116 | #define REV_NONE 0 /* No addditional information */ | ||
117 | #define REV_CRL_REASON 1 /* Value is CRL reason code */ | ||
118 | #define REV_HOLD 2 /* Value is hold instruction */ | ||
119 | #define REV_KEY_COMPROMISE 3 /* Value is cert key compromise time */ | ||
120 | #define REV_CA_COMPROMISE 4 /* Value is CA key compromise time */ | ||
121 | |||
122 | static void lookup_fail(const char *name, const char *tag); | ||
123 | static int certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509, | ||
124 | const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, | ||
125 | STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, char *subj, | ||
126 | unsigned long chtype, int multirdn, int email_dn, char *startdate, | ||
127 | char *enddate, long days, int batch, char *ext_sect, CONF *conf, | ||
128 | int verbose, unsigned long certopt, unsigned long nameopt, | ||
129 | int default_op, int ext_copy, int selfsign); | ||
130 | static int certify_cert(X509 **xret, char *infile, EVP_PKEY *pkey, | ||
131 | X509 *x509, const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, | ||
132 | STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, char *subj, | ||
133 | unsigned long chtype, int multirdn, int email_dn, char *startdate, | ||
134 | char *enddate, long days, int batch, char *ext_sect, CONF *conf, | ||
135 | int verbose, unsigned long certopt, unsigned long nameopt, int default_op, | ||
136 | int ext_copy); | ||
137 | static int write_new_certificate(BIO *bp, X509 *x, int output_der, | ||
138 | int notext); | ||
139 | static int do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, | ||
140 | const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, | ||
141 | STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, char *subj, | ||
142 | unsigned long chtype, int multirdn, int email_dn, char *startdate, | ||
143 | char *enddate, long days, int batch, int verbose, X509_REQ *req, | ||
144 | char *ext_sect, CONF *conf, unsigned long certopt, unsigned long nameopt, | ||
145 | int default_op, int ext_copy, int selfsign); | ||
146 | static int do_revoke(X509 *x509, CA_DB *db, int ext, char *extval); | ||
147 | static int get_certificate_status(const char *serial, CA_DB *db); | ||
148 | static int do_updatedb(CA_DB *db); | ||
149 | static int check_time_format(const char *str); | ||
150 | char *make_revocation_str(int rev_type, char *rev_arg); | ||
151 | int make_revoked(X509_REVOKED *rev, const char *str); | ||
152 | int old_entry_print(BIO *bp, ASN1_OBJECT *obj, ASN1_STRING *str); | ||
153 | |||
154 | static CONF *conf = NULL; | ||
155 | static CONF *extconf = NULL; | ||
156 | |||
157 | static struct { | ||
158 | int batch; | ||
159 | char *certfile; | ||
160 | unsigned long chtype; | ||
161 | char *configfile; | ||
162 | int create_serial; | ||
163 | char *crl_ext; | ||
164 | long crldays; | ||
165 | long crlhours; | ||
166 | long crlsec; | ||
167 | long days; | ||
168 | int dorevoke; | ||
169 | int doupdatedb; | ||
170 | int email_dn; | ||
171 | char *enddate; | ||
172 | char *extensions; | ||
173 | char *extfile; | ||
174 | int gencrl; | ||
175 | char *infile; | ||
176 | char **infiles; | ||
177 | int infiles_num; | ||
178 | char *key; | ||
179 | char *keyfile; | ||
180 | int keyform; | ||
181 | char *md; | ||
182 | int multirdn; | ||
183 | int notext; | ||
184 | char *outdir; | ||
185 | char *outfile; | ||
186 | char *passargin; | ||
187 | char *policy; | ||
188 | int preserve; | ||
189 | int req; | ||
190 | char *rev_arg; | ||
191 | int rev_type; | ||
192 | char *serial_status; | ||
193 | char *section; | ||
194 | int selfsign; | ||
195 | STACK_OF(OPENSSL_STRING) *sigopts; | ||
196 | char *ss_cert_file; | ||
197 | char *startdate; | ||
198 | char *subj; | ||
199 | int verbose; | ||
200 | } cfg; | ||
201 | |||
202 | static int | ||
203 | ca_opt_chtype_utf8(void) | ||
204 | { | ||
205 | cfg.chtype = MBSTRING_UTF8; | ||
206 | return (0); | ||
207 | } | ||
208 | |||
209 | static int | ||
210 | ca_opt_crl_ca_compromise(char *arg) | ||
211 | { | ||
212 | cfg.rev_arg = arg; | ||
213 | cfg.rev_type = REV_CA_COMPROMISE; | ||
214 | return (0); | ||
215 | } | ||
216 | |||
217 | static int | ||
218 | ca_opt_crl_compromise(char *arg) | ||
219 | { | ||
220 | cfg.rev_arg = arg; | ||
221 | cfg.rev_type = REV_KEY_COMPROMISE; | ||
222 | return (0); | ||
223 | } | ||
224 | |||
225 | static int | ||
226 | ca_opt_crl_hold(char *arg) | ||
227 | { | ||
228 | cfg.rev_arg = arg; | ||
229 | cfg.rev_type = REV_HOLD; | ||
230 | return (0); | ||
231 | } | ||
232 | |||
233 | static int | ||
234 | ca_opt_crl_reason(char *arg) | ||
235 | { | ||
236 | cfg.rev_arg = arg; | ||
237 | cfg.rev_type = REV_CRL_REASON; | ||
238 | return (0); | ||
239 | } | ||
240 | |||
241 | static int | ||
242 | ca_opt_in(char *arg) | ||
243 | { | ||
244 | cfg.infile = arg; | ||
245 | cfg.req = 1; | ||
246 | return (0); | ||
247 | } | ||
248 | |||
249 | static int | ||
250 | ca_opt_infiles(int argc, char **argv, int *argsused) | ||
251 | { | ||
252 | cfg.infiles_num = argc - 1; | ||
253 | if (cfg.infiles_num < 1) | ||
254 | return (1); | ||
255 | cfg.infiles = argv + 1; | ||
256 | cfg.req = 1; | ||
257 | *argsused = argc; | ||
258 | return (0); | ||
259 | } | ||
260 | |||
261 | static int | ||
262 | ca_opt_revoke(char *arg) | ||
263 | { | ||
264 | cfg.infile = arg; | ||
265 | cfg.dorevoke = 1; | ||
266 | return (0); | ||
267 | } | ||
268 | |||
269 | static int | ||
270 | ca_opt_sigopt(char *arg) | ||
271 | { | ||
272 | if (cfg.sigopts == NULL) | ||
273 | cfg.sigopts = sk_OPENSSL_STRING_new_null(); | ||
274 | if (cfg.sigopts == NULL) | ||
275 | return (1); | ||
276 | if (!sk_OPENSSL_STRING_push(cfg.sigopts, arg)) | ||
277 | return (1); | ||
278 | return (0); | ||
279 | } | ||
280 | |||
281 | static int | ||
282 | ca_opt_ss_cert(char *arg) | ||
283 | { | ||
284 | cfg.ss_cert_file = arg; | ||
285 | cfg.req = 1; | ||
286 | return (0); | ||
287 | } | ||
288 | |||
289 | static const struct option ca_options[] = { | ||
290 | { | ||
291 | .name = "batch", | ||
292 | .desc = "Operate in batch mode", | ||
293 | .type = OPTION_FLAG, | ||
294 | .opt.flag = &cfg.batch, | ||
295 | }, | ||
296 | { | ||
297 | .name = "cert", | ||
298 | .argname = "file", | ||
299 | .desc = "File containing the CA certificate", | ||
300 | .type = OPTION_ARG, | ||
301 | .opt.arg = &cfg.certfile, | ||
302 | }, | ||
303 | { | ||
304 | .name = "config", | ||
305 | .argname = "file", | ||
306 | .desc = "Specify an alternative configuration file", | ||
307 | .type = OPTION_ARG, | ||
308 | .opt.arg = &cfg.configfile, | ||
309 | }, | ||
310 | { | ||
311 | .name = "create_serial", | ||
312 | .desc = "If reading serial fails, create a new random serial", | ||
313 | .type = OPTION_FLAG, | ||
314 | .opt.flag = &cfg.create_serial, | ||
315 | }, | ||
316 | { | ||
317 | .name = "crl_CA_compromise", | ||
318 | .argname = "time", | ||
319 | .desc = "Set the compromise time and the revocation reason to\n" | ||
320 | "CACompromise", | ||
321 | .type = OPTION_ARG_FUNC, | ||
322 | .opt.argfunc = ca_opt_crl_ca_compromise, | ||
323 | }, | ||
324 | { | ||
325 | .name = "crl_compromise", | ||
326 | .argname = "time", | ||
327 | .desc = "Set the compromise time and the revocation reason to\n" | ||
328 | "keyCompromise", | ||
329 | .type = OPTION_ARG_FUNC, | ||
330 | .opt.argfunc = ca_opt_crl_compromise, | ||
331 | }, | ||
332 | { | ||
333 | .name = "crl_hold", | ||
334 | .argname = "instruction", | ||
335 | .desc = "Set the hold instruction and the revocation reason to\n" | ||
336 | "certificateHold", | ||
337 | .type = OPTION_ARG_FUNC, | ||
338 | .opt.argfunc = ca_opt_crl_hold, | ||
339 | }, | ||
340 | { | ||
341 | .name = "crl_reason", | ||
342 | .argname = "reason", | ||
343 | .desc = "Revocation reason", | ||
344 | .type = OPTION_ARG_FUNC, | ||
345 | .opt.argfunc = ca_opt_crl_reason, | ||
346 | }, | ||
347 | { | ||
348 | .name = "crldays", | ||
349 | .argname = "days", | ||
350 | .desc = "Number of days before the next CRL is due", | ||
351 | .type = OPTION_ARG_LONG, | ||
352 | .opt.lvalue = &cfg.crldays, | ||
353 | }, | ||
354 | { | ||
355 | .name = "crlexts", | ||
356 | .argname = "section", | ||
357 | .desc = "CRL extension section (override value in config file)", | ||
358 | .type = OPTION_ARG, | ||
359 | .opt.arg = &cfg.crl_ext, | ||
360 | }, | ||
361 | { | ||
362 | .name = "crlhours", | ||
363 | .argname = "hours", | ||
364 | .desc = "Number of hours before the next CRL is due", | ||
365 | .type = OPTION_ARG_LONG, | ||
366 | .opt.lvalue = &cfg.crlhours, | ||
367 | }, | ||
368 | { | ||
369 | .name = "crlsec", | ||
370 | .argname = "seconds", | ||
371 | .desc = "Number of seconds before the next CRL is due", | ||
372 | .type = OPTION_ARG_LONG, | ||
373 | .opt.lvalue = &cfg.crlsec, | ||
374 | }, | ||
375 | { | ||
376 | .name = "days", | ||
377 | .argname = "arg", | ||
378 | .desc = "Number of days to certify the certificate for", | ||
379 | .type = OPTION_ARG_LONG, | ||
380 | .opt.lvalue = &cfg.days, | ||
381 | }, | ||
382 | { | ||
383 | .name = "enddate", | ||
384 | .argname = "YYMMDDHHMMSSZ", | ||
385 | .desc = "Certificate validity notAfter (overrides -days)", | ||
386 | .type = OPTION_ARG, | ||
387 | .opt.arg = &cfg.enddate, | ||
388 | }, | ||
389 | { | ||
390 | .name = "extensions", | ||
391 | .argname = "section", | ||
392 | .desc = "Extension section (override value in config file)", | ||
393 | .type = OPTION_ARG, | ||
394 | .opt.arg = &cfg.extensions, | ||
395 | }, | ||
396 | { | ||
397 | .name = "extfile", | ||
398 | .argname = "file", | ||
399 | .desc = "Configuration file with X509v3 extentions to add", | ||
400 | .type = OPTION_ARG, | ||
401 | .opt.arg = &cfg.extfile, | ||
402 | }, | ||
403 | { | ||
404 | .name = "gencrl", | ||
405 | .desc = "Generate a new CRL", | ||
406 | .type = OPTION_FLAG, | ||
407 | .opt.flag = &cfg.gencrl, | ||
408 | }, | ||
409 | { | ||
410 | .name = "in", | ||
411 | .argname = "file", | ||
412 | .desc = "Input file containing a single certificate request", | ||
413 | .type = OPTION_ARG_FUNC, | ||
414 | .opt.argfunc = ca_opt_in, | ||
415 | }, | ||
416 | { | ||
417 | .name = "infiles", | ||
418 | .argname = "...", | ||
419 | .desc = "The last argument, certificate requests to process", | ||
420 | .type = OPTION_ARGV_FUNC, | ||
421 | .opt.argvfunc = ca_opt_infiles, | ||
422 | }, | ||
423 | { | ||
424 | .name = "key", | ||
425 | .argname = "password", | ||
426 | .desc = "Key to decode the private key if it is encrypted", | ||
427 | .type = OPTION_ARG, | ||
428 | .opt.arg = &cfg.key, | ||
429 | }, | ||
430 | { | ||
431 | .name = "keyfile", | ||
432 | .argname = "file", | ||
433 | .desc = "Private key file", | ||
434 | .type = OPTION_ARG, | ||
435 | .opt.arg = &cfg.keyfile, | ||
436 | }, | ||
437 | { | ||
438 | .name = "keyform", | ||
439 | .argname = "fmt", | ||
440 | .desc = "Private key file format (DER or PEM (default))", | ||
441 | .type = OPTION_ARG_FORMAT, | ||
442 | .opt.value = &cfg.keyform, | ||
443 | }, | ||
444 | { | ||
445 | .name = "md", | ||
446 | .argname = "alg", | ||
447 | .desc = "Message digest to use", | ||
448 | .type = OPTION_ARG, | ||
449 | .opt.arg = &cfg.md, | ||
450 | }, | ||
451 | { | ||
452 | .name = "multivalue-rdn", | ||
453 | .desc = "Enable support for multivalued RDNs", | ||
454 | .type = OPTION_FLAG, | ||
455 | .opt.flag = &cfg.multirdn, | ||
456 | }, | ||
457 | { | ||
458 | .name = "name", | ||
459 | .argname = "section", | ||
460 | .desc = "Specifies the configuration file section to use", | ||
461 | .type = OPTION_ARG, | ||
462 | .opt.arg = &cfg.section, | ||
463 | }, | ||
464 | { | ||
465 | .name = "noemailDN", | ||
466 | .desc = "Do not add the EMAIL field to the DN", | ||
467 | .type = OPTION_VALUE, | ||
468 | .opt.value = &cfg.email_dn, | ||
469 | .value = 0, | ||
470 | }, | ||
471 | { | ||
472 | .name = "notext", | ||
473 | .desc = "Do not print the generated certificate", | ||
474 | .type = OPTION_FLAG, | ||
475 | .opt.flag = &cfg.notext, | ||
476 | }, | ||
477 | { | ||
478 | .name = "out", | ||
479 | .argname = "file", | ||
480 | .desc = "Output file (default stdout)", | ||
481 | .type = OPTION_ARG, | ||
482 | .opt.arg = &cfg.outfile, | ||
483 | }, | ||
484 | { | ||
485 | .name = "outdir", | ||
486 | .argname = "directory", | ||
487 | .desc = " Directory to output certificates to", | ||
488 | .type = OPTION_ARG, | ||
489 | .opt.arg = &cfg.outdir, | ||
490 | }, | ||
491 | { | ||
492 | .name = "passin", | ||
493 | .argname = "src", | ||
494 | .desc = "Private key input password source", | ||
495 | .type = OPTION_ARG, | ||
496 | .opt.arg = &cfg.passargin, | ||
497 | }, | ||
498 | { | ||
499 | .name = "policy", | ||
500 | .argname = "name", | ||
501 | .desc = "The CA 'policy' to support", | ||
502 | .type = OPTION_ARG, | ||
503 | .opt.arg = &cfg.policy, | ||
504 | }, | ||
505 | { | ||
506 | .name = "preserveDN", | ||
507 | .desc = "Do not re-order the DN", | ||
508 | .type = OPTION_FLAG, | ||
509 | .opt.flag = &cfg.preserve, | ||
510 | }, | ||
511 | { | ||
512 | .name = "revoke", | ||
513 | .argname = "file", | ||
514 | .desc = "Revoke a certificate (given in file)", | ||
515 | .type = OPTION_ARG_FUNC, | ||
516 | .opt.argfunc = ca_opt_revoke, | ||
517 | }, | ||
518 | { | ||
519 | .name = "selfsign", | ||
520 | .desc = "Sign a certificate using the key associated with it", | ||
521 | .type = OPTION_FLAG, | ||
522 | .opt.flag = &cfg.selfsign, | ||
523 | }, | ||
524 | { | ||
525 | .name = "sigopt", | ||
526 | .argname = "nm:v", | ||
527 | .desc = "Signature parameter in nm:v form", | ||
528 | .type = OPTION_ARG_FUNC, | ||
529 | .opt.argfunc = ca_opt_sigopt, | ||
530 | }, | ||
531 | { | ||
532 | .name = "ss_cert", | ||
533 | .argname = "file", | ||
534 | .desc = "File contains a self signed certificate to sign", | ||
535 | .type = OPTION_ARG_FUNC, | ||
536 | .opt.argfunc = ca_opt_ss_cert, | ||
537 | }, | ||
538 | { | ||
539 | .name = "startdate", | ||
540 | .argname = "YYMMDDHHMMSSZ", | ||
541 | .desc = "Certificate validity notBefore", | ||
542 | .type = OPTION_ARG, | ||
543 | .opt.arg = &cfg.startdate, | ||
544 | }, | ||
545 | { | ||
546 | .name = "status", | ||
547 | .argname = "serial", | ||
548 | .desc = "Shows certificate status given the serial number", | ||
549 | .type = OPTION_ARG, | ||
550 | .opt.arg = &cfg.serial_status, | ||
551 | }, | ||
552 | { | ||
553 | .name = "subj", | ||
554 | .argname = "arg", | ||
555 | .desc = "Use arg instead of request's subject", | ||
556 | .type = OPTION_ARG, | ||
557 | .opt.arg = &cfg.subj, | ||
558 | }, | ||
559 | { | ||
560 | .name = "updatedb", | ||
561 | .desc = "Updates db for expired certificates", | ||
562 | .type = OPTION_FLAG, | ||
563 | .opt.flag = &cfg.doupdatedb, | ||
564 | }, | ||
565 | { | ||
566 | .name = "utf8", | ||
567 | .desc = "Input characters are in UTF-8 (default ASCII)", | ||
568 | .type = OPTION_FUNC, | ||
569 | .opt.func = ca_opt_chtype_utf8, | ||
570 | }, | ||
571 | { | ||
572 | .name = "verbose", | ||
573 | .desc = "Verbose output during processing", | ||
574 | .type = OPTION_FLAG, | ||
575 | .opt.flag = &cfg.verbose, | ||
576 | }, | ||
577 | { NULL }, | ||
578 | }; | ||
579 | |||
580 | static void | ||
581 | ca_usage(void) | ||
582 | { | ||
583 | fprintf(stderr, | ||
584 | "usage: ca [-batch] [-cert file] [-config file] [-create_serial]\n" | ||
585 | " [-crl_CA_compromise time] [-crl_compromise time]\n" | ||
586 | " [-crl_hold instruction] [-crl_reason reason] [-crldays days]\n" | ||
587 | " [-crlexts section] [-crlhours hours] [-crlsec seconds]\n" | ||
588 | " [-days arg] [-enddate date] [-extensions section]\n" | ||
589 | " [-extfile file] [-gencrl] [-in file] [-infiles]\n" | ||
590 | " [-key password] [-keyfile file] [-keyform pem | der]\n" | ||
591 | " [-md alg] [-multivalue-rdn] [-name section]\n" | ||
592 | " [-noemailDN] [-notext] [-out file] [-outdir directory]\n" | ||
593 | " [-passin arg] [-policy name] [-preserveDN] [-revoke file]\n" | ||
594 | " [-selfsign] [-sigopt nm:v] [-ss_cert file]\n" | ||
595 | " [-startdate date] [-status serial] [-subj arg] [-updatedb]\n" | ||
596 | " [-utf8] [-verbose]\n\n"); | ||
597 | options_usage(ca_options); | ||
598 | fprintf(stderr, "\n"); | ||
599 | } | ||
600 | |||
601 | int | ||
602 | ca_main(int argc, char **argv) | ||
603 | { | ||
604 | int free_key = 0; | ||
605 | int total = 0; | ||
606 | int total_done = 0; | ||
607 | long errorline = -1; | ||
608 | EVP_PKEY *pkey = NULL; | ||
609 | int output_der = 0; | ||
610 | char *serialfile = NULL; | ||
611 | char *crlnumberfile = NULL; | ||
612 | char *tmp_email_dn = NULL; | ||
613 | BIGNUM *serial = NULL; | ||
614 | BIGNUM *crlnumber = NULL; | ||
615 | unsigned long nameopt = 0, certopt = 0; | ||
616 | int default_op = 1; | ||
617 | int ext_copy = EXT_COPY_NONE; | ||
618 | X509 *x509 = NULL, *x509p = NULL; | ||
619 | X509 *x = NULL; | ||
620 | BIO *in = NULL, *out = NULL, *Sout = NULL, *Cout = NULL; | ||
621 | char *dbfile = NULL; | ||
622 | CA_DB *db = NULL; | ||
623 | X509_CRL *crl = NULL; | ||
624 | X509_REVOKED *r = NULL; | ||
625 | ASN1_TIME *tmptm = NULL; | ||
626 | ASN1_INTEGER *tmpserial; | ||
627 | char *f; | ||
628 | const char *p; | ||
629 | char *const *pp; | ||
630 | int i, j; | ||
631 | const EVP_MD *dgst = NULL; | ||
632 | STACK_OF(CONF_VALUE) *attribs = NULL; | ||
633 | STACK_OF(X509) *cert_sk = NULL; | ||
634 | char *tofree = NULL; | ||
635 | DB_ATTR db_attr; | ||
636 | int default_nid, rv; | ||
637 | int ret = 1; | ||
638 | |||
639 | if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { | ||
640 | perror("pledge"); | ||
641 | exit(1); | ||
642 | } | ||
643 | |||
644 | memset(&cfg, 0, sizeof(cfg)); | ||
645 | cfg.email_dn = 1; | ||
646 | cfg.keyform = FORMAT_PEM; | ||
647 | cfg.chtype = MBSTRING_ASC; | ||
648 | cfg.rev_type = REV_NONE; | ||
649 | |||
650 | conf = NULL; | ||
651 | |||
652 | if (options_parse(argc, argv, ca_options, NULL, NULL) != 0) { | ||
653 | ca_usage(); | ||
654 | goto err; | ||
655 | } | ||
656 | |||
657 | /*****************************************************************/ | ||
658 | tofree = NULL; | ||
659 | if (cfg.configfile == NULL) | ||
660 | cfg.configfile = getenv("OPENSSL_CONF"); | ||
661 | if (cfg.configfile == NULL) { | ||
662 | if ((tofree = make_config_name()) == NULL) { | ||
663 | BIO_printf(bio_err, "error making config file name\n"); | ||
664 | goto err; | ||
665 | } | ||
666 | cfg.configfile = tofree; | ||
667 | } | ||
668 | BIO_printf(bio_err, "Using configuration from %s\n", | ||
669 | cfg.configfile); | ||
670 | conf = NCONF_new(NULL); | ||
671 | if (NCONF_load(conf, cfg.configfile, &errorline) <= 0) { | ||
672 | if (errorline <= 0) | ||
673 | BIO_printf(bio_err, | ||
674 | "error loading the config file '%s'\n", | ||
675 | cfg.configfile); | ||
676 | else | ||
677 | BIO_printf(bio_err, | ||
678 | "error on line %ld of config file '%s'\n", | ||
679 | errorline, cfg.configfile); | ||
680 | goto err; | ||
681 | } | ||
682 | free(tofree); | ||
683 | tofree = NULL; | ||
684 | |||
685 | /* Lets get the config section we are using */ | ||
686 | if (cfg.section == NULL) { | ||
687 | cfg.section = NCONF_get_string(conf, BASE_SECTION, | ||
688 | ENV_DEFAULT_CA); | ||
689 | if (cfg.section == NULL) { | ||
690 | lookup_fail(BASE_SECTION, ENV_DEFAULT_CA); | ||
691 | goto err; | ||
692 | } | ||
693 | } | ||
694 | if (conf != NULL) { | ||
695 | p = NCONF_get_string(conf, NULL, "oid_file"); | ||
696 | if (p == NULL) | ||
697 | ERR_clear_error(); | ||
698 | if (p != NULL) { | ||
699 | BIO *oid_bio; | ||
700 | |||
701 | oid_bio = BIO_new_file(p, "r"); | ||
702 | if (oid_bio == NULL) { | ||
703 | /* | ||
704 | BIO_printf(bio_err, | ||
705 | "problems opening %s for extra oid's\n", p); | ||
706 | ERR_print_errors(bio_err); | ||
707 | */ | ||
708 | ERR_clear_error(); | ||
709 | } else { | ||
710 | OBJ_create_objects(oid_bio); | ||
711 | BIO_free(oid_bio); | ||
712 | } | ||
713 | } | ||
714 | if (!add_oid_section(bio_err, conf)) { | ||
715 | ERR_print_errors(bio_err); | ||
716 | goto err; | ||
717 | } | ||
718 | } | ||
719 | f = NCONF_get_string(conf, cfg.section, STRING_MASK); | ||
720 | if (f == NULL) | ||
721 | ERR_clear_error(); | ||
722 | |||
723 | if (f != NULL && !ASN1_STRING_set_default_mask_asc(f)) { | ||
724 | BIO_printf(bio_err, | ||
725 | "Invalid global string mask setting %s\n", f); | ||
726 | goto err; | ||
727 | } | ||
728 | if (cfg.chtype != MBSTRING_UTF8) { | ||
729 | f = NCONF_get_string(conf, cfg.section, UTF8_IN); | ||
730 | if (f == NULL) | ||
731 | ERR_clear_error(); | ||
732 | else if (strcmp(f, "yes") == 0) | ||
733 | cfg.chtype = MBSTRING_UTF8; | ||
734 | } | ||
735 | db_attr.unique_subject = 1; | ||
736 | p = NCONF_get_string(conf, cfg.section, ENV_UNIQUE_SUBJECT); | ||
737 | if (p != NULL) { | ||
738 | db_attr.unique_subject = parse_yesno(p, 1); | ||
739 | } else | ||
740 | ERR_clear_error(); | ||
741 | |||
742 | in = BIO_new(BIO_s_file()); | ||
743 | out = BIO_new(BIO_s_file()); | ||
744 | Sout = BIO_new(BIO_s_file()); | ||
745 | Cout = BIO_new(BIO_s_file()); | ||
746 | if ((in == NULL) || (out == NULL) || (Sout == NULL) || (Cout == NULL)) { | ||
747 | ERR_print_errors(bio_err); | ||
748 | goto err; | ||
749 | } | ||
750 | /*****************************************************************/ | ||
751 | /* report status of cert with serial number given on command line */ | ||
752 | if (cfg.serial_status) { | ||
753 | if ((dbfile = NCONF_get_string(conf, cfg.section, | ||
754 | ENV_DATABASE)) == NULL) { | ||
755 | lookup_fail(cfg.section, ENV_DATABASE); | ||
756 | goto err; | ||
757 | } | ||
758 | db = load_index(dbfile, &db_attr); | ||
759 | if (db == NULL) | ||
760 | goto err; | ||
761 | |||
762 | if (!index_index(db)) | ||
763 | goto err; | ||
764 | |||
765 | if (get_certificate_status(cfg.serial_status, db) != 1) | ||
766 | BIO_printf(bio_err, "Error verifying serial %s!\n", | ||
767 | cfg.serial_status); | ||
768 | goto err; | ||
769 | } | ||
770 | /*****************************************************************/ | ||
771 | /* we definitely need a private key, so let's get it */ | ||
772 | |||
773 | if ((cfg.keyfile == NULL) && | ||
774 | ((cfg.keyfile = NCONF_get_string(conf, cfg.section, | ||
775 | ENV_PRIVATE_KEY)) == NULL)) { | ||
776 | lookup_fail(cfg.section, ENV_PRIVATE_KEY); | ||
777 | goto err; | ||
778 | } | ||
779 | if (cfg.key == NULL) { | ||
780 | free_key = 1; | ||
781 | if (!app_passwd(bio_err, cfg.passargin, NULL, | ||
782 | &cfg.key, NULL)) { | ||
783 | BIO_printf(bio_err, "Error getting password\n"); | ||
784 | goto err; | ||
785 | } | ||
786 | } | ||
787 | pkey = load_key(bio_err, cfg.keyfile, cfg.keyform, 0, | ||
788 | cfg.key, "CA private key"); | ||
789 | if (cfg.key != NULL) | ||
790 | explicit_bzero(cfg.key, strlen(cfg.key)); | ||
791 | if (pkey == NULL) { | ||
792 | /* load_key() has already printed an appropriate message */ | ||
793 | goto err; | ||
794 | } | ||
795 | /*****************************************************************/ | ||
796 | /* we need a certificate */ | ||
797 | if (!cfg.selfsign || cfg.ss_cert_file != NULL || cfg.gencrl) { | ||
798 | if ((cfg.certfile == NULL) && | ||
799 | ((cfg.certfile = NCONF_get_string(conf, | ||
800 | cfg.section, ENV_CERTIFICATE)) == NULL)) { | ||
801 | lookup_fail(cfg.section, ENV_CERTIFICATE); | ||
802 | goto err; | ||
803 | } | ||
804 | x509 = load_cert(bio_err, cfg.certfile, FORMAT_PEM, NULL, | ||
805 | "CA certificate"); | ||
806 | if (x509 == NULL) | ||
807 | goto err; | ||
808 | |||
809 | if (!X509_check_private_key(x509, pkey)) { | ||
810 | BIO_printf(bio_err, | ||
811 | "CA certificate and CA private key do not match\n"); | ||
812 | goto err; | ||
813 | } | ||
814 | } | ||
815 | if (!cfg.selfsign) | ||
816 | x509p = x509; | ||
817 | |||
818 | f = NCONF_get_string(conf, BASE_SECTION, ENV_PRESERVE); | ||
819 | if (f == NULL) | ||
820 | ERR_clear_error(); | ||
821 | if ((f != NULL) && ((*f == 'y') || (*f == 'Y'))) | ||
822 | cfg.preserve = 1; | ||
823 | |||
824 | f = NCONF_get_string(conf, cfg.section, ENV_NAMEOPT); | ||
825 | |||
826 | if (f != NULL) { | ||
827 | if (!set_name_ex(&nameopt, f)) { | ||
828 | BIO_printf(bio_err, | ||
829 | "Invalid name options: \"%s\"\n", f); | ||
830 | goto err; | ||
831 | } | ||
832 | default_op = 0; | ||
833 | } else | ||
834 | ERR_clear_error(); | ||
835 | |||
836 | f = NCONF_get_string(conf, cfg.section, ENV_CERTOPT); | ||
837 | |||
838 | if (f != NULL) { | ||
839 | if (!set_cert_ex(&certopt, f)) { | ||
840 | BIO_printf(bio_err, | ||
841 | "Invalid certificate options: \"%s\"\n", f); | ||
842 | goto err; | ||
843 | } | ||
844 | default_op = 0; | ||
845 | } else | ||
846 | ERR_clear_error(); | ||
847 | |||
848 | f = NCONF_get_string(conf, cfg.section, ENV_EXTCOPY); | ||
849 | |||
850 | if (f != NULL) { | ||
851 | if (!set_ext_copy(&ext_copy, f)) { | ||
852 | BIO_printf(bio_err, | ||
853 | "Invalid extension copy option: \"%s\"\n", f); | ||
854 | goto err; | ||
855 | } | ||
856 | } else | ||
857 | ERR_clear_error(); | ||
858 | |||
859 | /*****************************************************************/ | ||
860 | /* lookup where to write new certificates */ | ||
861 | if (cfg.outdir == NULL && cfg.req) { | ||
862 | if ((cfg.outdir = NCONF_get_string(conf, | ||
863 | cfg.section, ENV_NEW_CERTS_DIR)) == NULL) { | ||
864 | BIO_printf(bio_err, "output directory %s not defined\n", | ||
865 | ENV_NEW_CERTS_DIR); | ||
866 | goto err; | ||
867 | } | ||
868 | } | ||
869 | /*****************************************************************/ | ||
870 | /* we need to load the database file */ | ||
871 | if ((dbfile = NCONF_get_string(conf, cfg.section, | ||
872 | ENV_DATABASE)) == NULL) { | ||
873 | lookup_fail(cfg.section, ENV_DATABASE); | ||
874 | goto err; | ||
875 | } | ||
876 | db = load_index(dbfile, &db_attr); | ||
877 | if (db == NULL) | ||
878 | goto err; | ||
879 | |||
880 | /* Lets check some fields */ | ||
881 | for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { | ||
882 | pp = sk_OPENSSL_PSTRING_value(db->db->data, i); | ||
883 | if ((pp[DB_type][0] != DB_TYPE_REV) && | ||
884 | (pp[DB_rev_date][0] != '\0')) { | ||
885 | BIO_printf(bio_err, | ||
886 | "entry %d: not revoked yet, but has a revocation date\n", | ||
887 | i + 1); | ||
888 | goto err; | ||
889 | } | ||
890 | if ((pp[DB_type][0] == DB_TYPE_REV) && | ||
891 | !make_revoked(NULL, pp[DB_rev_date])) { | ||
892 | BIO_printf(bio_err, " in entry %d\n", i + 1); | ||
893 | goto err; | ||
894 | } | ||
895 | if (!check_time_format((char *) pp[DB_exp_date])) { | ||
896 | BIO_printf(bio_err, "entry %d: invalid expiry date\n", | ||
897 | i + 1); | ||
898 | goto err; | ||
899 | } | ||
900 | p = pp[DB_serial]; | ||
901 | j = strlen(p); | ||
902 | if (*p == '-') { | ||
903 | p++; | ||
904 | j--; | ||
905 | } | ||
906 | if ((j & 1) || (j < 2)) { | ||
907 | BIO_printf(bio_err, | ||
908 | "entry %d: bad serial number length (%d)\n", | ||
909 | i + 1, j); | ||
910 | goto err; | ||
911 | } | ||
912 | while (*p) { | ||
913 | if (!(((*p >= '0') && (*p <= '9')) || | ||
914 | ((*p >= 'A') && (*p <= 'F')) || | ||
915 | ((*p >= 'a') && (*p <= 'f')))) { | ||
916 | BIO_printf(bio_err, | ||
917 | "entry %d: bad serial number characters, char pos %ld, char is '%c'\n", | ||
918 | i + 1, (long) (p - pp[DB_serial]), *p); | ||
919 | goto err; | ||
920 | } | ||
921 | p++; | ||
922 | } | ||
923 | } | ||
924 | if (cfg.verbose) { | ||
925 | BIO_set_fp(out, stdout, BIO_NOCLOSE | BIO_FP_TEXT); | ||
926 | TXT_DB_write(out, db->db); | ||
927 | BIO_printf(bio_err, "%d entries loaded from the database\n", | ||
928 | sk_OPENSSL_PSTRING_num(db->db->data)); | ||
929 | BIO_printf(bio_err, "generating index\n"); | ||
930 | } | ||
931 | if (!index_index(db)) | ||
932 | goto err; | ||
933 | |||
934 | /*****************************************************************/ | ||
935 | /* Update the db file for expired certificates */ | ||
936 | if (cfg.doupdatedb) { | ||
937 | if (cfg.verbose) | ||
938 | BIO_printf(bio_err, "Updating %s ...\n", dbfile); | ||
939 | |||
940 | i = do_updatedb(db); | ||
941 | if (i == -1) { | ||
942 | BIO_printf(bio_err, "Malloc failure\n"); | ||
943 | goto err; | ||
944 | } else if (i == 0) { | ||
945 | if (cfg.verbose) | ||
946 | BIO_printf(bio_err, | ||
947 | "No entries found to mark expired\n"); | ||
948 | } else { | ||
949 | if (!save_index(dbfile, "new", db)) | ||
950 | goto err; | ||
951 | |||
952 | if (!rotate_index(dbfile, "new", "old")) | ||
953 | goto err; | ||
954 | |||
955 | if (cfg.verbose) | ||
956 | BIO_printf(bio_err, | ||
957 | "Done. %d entries marked as expired\n", i); | ||
958 | } | ||
959 | } | ||
960 | /*****************************************************************/ | ||
961 | /* Read extentions config file */ | ||
962 | if (cfg.extfile != NULL) { | ||
963 | extconf = NCONF_new(NULL); | ||
964 | if (NCONF_load(extconf, cfg.extfile, &errorline) <= 0) { | ||
965 | if (errorline <= 0) | ||
966 | BIO_printf(bio_err, | ||
967 | "ERROR: loading the config file '%s'\n", | ||
968 | cfg.extfile); | ||
969 | else | ||
970 | BIO_printf(bio_err, | ||
971 | "ERROR: on line %ld of config file '%s'\n", | ||
972 | errorline, cfg.extfile); | ||
973 | ret = 1; | ||
974 | goto err; | ||
975 | } | ||
976 | if (cfg.verbose) | ||
977 | BIO_printf(bio_err, | ||
978 | "Successfully loaded extensions file %s\n", | ||
979 | cfg.extfile); | ||
980 | |||
981 | /* We can have sections in the ext file */ | ||
982 | if (cfg.extensions == NULL && | ||
983 | (cfg.extensions = NCONF_get_string(extconf, "default", | ||
984 | "extensions")) == NULL) | ||
985 | cfg.extensions = "default"; | ||
986 | } | ||
987 | /*****************************************************************/ | ||
988 | if (cfg.req || cfg.gencrl) { | ||
989 | if (cfg.outfile != NULL) { | ||
990 | if (BIO_write_filename(Sout, cfg.outfile) <= 0) { | ||
991 | perror(cfg.outfile); | ||
992 | goto err; | ||
993 | } | ||
994 | } else { | ||
995 | BIO_set_fp(Sout, stdout, BIO_NOCLOSE | BIO_FP_TEXT); | ||
996 | } | ||
997 | } | ||
998 | |||
999 | rv = EVP_PKEY_get_default_digest_nid(pkey, &default_nid); | ||
1000 | if (rv == 2 && default_nid == NID_undef) { | ||
1001 | /* The digest is required to be EVP_md_null() (EdDSA). */ | ||
1002 | dgst = EVP_md_null(); | ||
1003 | } else { | ||
1004 | /* Ignore rv unless we need a valid default_nid. */ | ||
1005 | if (cfg.md == NULL) | ||
1006 | cfg.md = NCONF_get_string(conf, cfg.section, | ||
1007 | ENV_DEFAULT_MD); | ||
1008 | if (cfg.md == NULL) { | ||
1009 | lookup_fail(cfg.section, ENV_DEFAULT_MD); | ||
1010 | goto err; | ||
1011 | } | ||
1012 | if (strcmp(cfg.md, "default") == 0) { | ||
1013 | if (rv <= 0) { | ||
1014 | BIO_puts(bio_err, "no default digest\n"); | ||
1015 | goto err; | ||
1016 | } | ||
1017 | cfg.md = (char *)OBJ_nid2sn(default_nid); | ||
1018 | } | ||
1019 | if (cfg.md == NULL) | ||
1020 | goto err; | ||
1021 | if ((dgst = EVP_get_digestbyname(cfg.md)) == NULL) { | ||
1022 | BIO_printf(bio_err, "%s is an unsupported " | ||
1023 | "message digest type\n", cfg.md); | ||
1024 | goto err; | ||
1025 | } | ||
1026 | } | ||
1027 | if (cfg.req) { | ||
1028 | if ((cfg.email_dn == 1) && | ||
1029 | ((tmp_email_dn = NCONF_get_string(conf, cfg.section, | ||
1030 | ENV_DEFAULT_EMAIL_DN)) != NULL)) { | ||
1031 | if (strcmp(tmp_email_dn, "no") == 0) | ||
1032 | cfg.email_dn = 0; | ||
1033 | } | ||
1034 | if (cfg.verbose) | ||
1035 | BIO_printf(bio_err, "message digest is %s\n", | ||
1036 | OBJ_nid2ln(EVP_MD_type(dgst))); | ||
1037 | if ((cfg.policy == NULL) && | ||
1038 | ((cfg.policy = NCONF_get_string(conf, | ||
1039 | cfg.section, ENV_POLICY)) == NULL)) { | ||
1040 | lookup_fail(cfg.section, ENV_POLICY); | ||
1041 | goto err; | ||
1042 | } | ||
1043 | if (cfg.verbose) | ||
1044 | BIO_printf(bio_err, "policy is %s\n", cfg.policy); | ||
1045 | |||
1046 | if ((serialfile = NCONF_get_string(conf, cfg.section, | ||
1047 | ENV_SERIAL)) == NULL) { | ||
1048 | lookup_fail(cfg.section, ENV_SERIAL); | ||
1049 | goto err; | ||
1050 | } | ||
1051 | if (extconf == NULL) { | ||
1052 | /* | ||
1053 | * no '-extfile' option, so we look for extensions in | ||
1054 | * the main configuration file | ||
1055 | */ | ||
1056 | if (cfg.extensions == NULL) { | ||
1057 | cfg.extensions = NCONF_get_string(conf, | ||
1058 | cfg.section, ENV_EXTENSIONS); | ||
1059 | if (cfg.extensions == NULL) | ||
1060 | ERR_clear_error(); | ||
1061 | } | ||
1062 | if (cfg.extensions != NULL) { | ||
1063 | /* Check syntax of file */ | ||
1064 | X509V3_CTX ctx; | ||
1065 | X509V3_set_ctx_test(&ctx); | ||
1066 | X509V3_set_nconf(&ctx, conf); | ||
1067 | if (!X509V3_EXT_add_nconf(conf, &ctx, | ||
1068 | cfg.extensions, NULL)) { | ||
1069 | BIO_printf(bio_err, | ||
1070 | "Error Loading extension section %s\n", | ||
1071 | cfg.extensions); | ||
1072 | ret = 1; | ||
1073 | goto err; | ||
1074 | } | ||
1075 | } | ||
1076 | } | ||
1077 | if (cfg.startdate == NULL) { | ||
1078 | cfg.startdate = NCONF_get_string(conf, | ||
1079 | cfg.section, ENV_DEFAULT_STARTDATE); | ||
1080 | if (cfg.startdate == NULL) | ||
1081 | ERR_clear_error(); | ||
1082 | } | ||
1083 | if (cfg.startdate == NULL) | ||
1084 | cfg.startdate = "today"; | ||
1085 | |||
1086 | if (cfg.enddate == NULL) { | ||
1087 | cfg.enddate = NCONF_get_string(conf, | ||
1088 | cfg.section, ENV_DEFAULT_ENDDATE); | ||
1089 | if (cfg.enddate == NULL) | ||
1090 | ERR_clear_error(); | ||
1091 | } | ||
1092 | if (cfg.days == 0 && cfg.enddate == NULL) { | ||
1093 | if (!NCONF_get_number(conf, cfg.section, | ||
1094 | ENV_DEFAULT_DAYS, &cfg.days)) | ||
1095 | cfg.days = 0; | ||
1096 | } | ||
1097 | if (cfg.enddate == NULL && cfg.days == 0) { | ||
1098 | BIO_printf(bio_err, | ||
1099 | "cannot lookup how many days to certify for\n"); | ||
1100 | goto err; | ||
1101 | } | ||
1102 | if ((serial = load_serial(serialfile, cfg.create_serial, | ||
1103 | NULL)) == NULL) { | ||
1104 | BIO_printf(bio_err, | ||
1105 | "error while loading serial number\n"); | ||
1106 | goto err; | ||
1107 | } | ||
1108 | if (cfg.verbose) { | ||
1109 | if (BN_is_zero(serial)) | ||
1110 | BIO_printf(bio_err, | ||
1111 | "next serial number is 00\n"); | ||
1112 | else { | ||
1113 | if ((f = BN_bn2hex(serial)) == NULL) | ||
1114 | goto err; | ||
1115 | BIO_printf(bio_err, | ||
1116 | "next serial number is %s\n", f); | ||
1117 | free(f); | ||
1118 | } | ||
1119 | } | ||
1120 | if ((attribs = NCONF_get_section(conf, cfg.policy)) == | ||
1121 | NULL) { | ||
1122 | BIO_printf(bio_err, "unable to find 'section' for %s\n", | ||
1123 | cfg.policy); | ||
1124 | goto err; | ||
1125 | } | ||
1126 | if ((cert_sk = sk_X509_new_null()) == NULL) { | ||
1127 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
1128 | goto err; | ||
1129 | } | ||
1130 | if (cfg.ss_cert_file != NULL) { | ||
1131 | total++; | ||
1132 | j = certify_cert(&x, cfg.ss_cert_file, pkey, x509, | ||
1133 | dgst, cfg.sigopts, attribs, db, serial, | ||
1134 | cfg.subj, cfg.chtype, | ||
1135 | cfg.multirdn, cfg.email_dn, | ||
1136 | cfg.startdate, cfg.enddate, | ||
1137 | cfg.days, cfg.batch, | ||
1138 | cfg.extensions, conf, cfg.verbose, | ||
1139 | certopt, nameopt, default_op, ext_copy); | ||
1140 | if (j < 0) | ||
1141 | goto err; | ||
1142 | if (j > 0) { | ||
1143 | total_done++; | ||
1144 | BIO_printf(bio_err, "\n"); | ||
1145 | if (!BN_add_word(serial, 1)) | ||
1146 | goto err; | ||
1147 | if (!sk_X509_push(cert_sk, x)) { | ||
1148 | BIO_printf(bio_err, | ||
1149 | "Memory allocation failure\n"); | ||
1150 | goto err; | ||
1151 | } | ||
1152 | } | ||
1153 | } | ||
1154 | if (cfg.infile != NULL) { | ||
1155 | total++; | ||
1156 | j = certify(&x, cfg.infile, pkey, x509p, dgst, | ||
1157 | cfg.sigopts, attribs, db, serial, | ||
1158 | cfg.subj, cfg.chtype, | ||
1159 | cfg.multirdn, cfg.email_dn, | ||
1160 | cfg.startdate, cfg.enddate, | ||
1161 | cfg.days, cfg.batch, | ||
1162 | cfg.extensions, conf, cfg.verbose, | ||
1163 | certopt, nameopt, default_op, ext_copy, | ||
1164 | cfg.selfsign); | ||
1165 | if (j < 0) | ||
1166 | goto err; | ||
1167 | if (j > 0) { | ||
1168 | total_done++; | ||
1169 | BIO_printf(bio_err, "\n"); | ||
1170 | if (!BN_add_word(serial, 1)) | ||
1171 | goto err; | ||
1172 | if (!sk_X509_push(cert_sk, x)) { | ||
1173 | BIO_printf(bio_err, | ||
1174 | "Memory allocation failure\n"); | ||
1175 | goto err; | ||
1176 | } | ||
1177 | } | ||
1178 | } | ||
1179 | for (i = 0; i < cfg.infiles_num; i++) { | ||
1180 | total++; | ||
1181 | j = certify(&x, cfg.infiles[i], pkey, x509p, dgst, | ||
1182 | cfg.sigopts, attribs, db, serial, | ||
1183 | cfg.subj, cfg.chtype, | ||
1184 | cfg.multirdn, cfg.email_dn, | ||
1185 | cfg.startdate, cfg.enddate, | ||
1186 | cfg.days, cfg.batch, | ||
1187 | cfg.extensions, conf, cfg.verbose, | ||
1188 | certopt, nameopt, default_op, ext_copy, | ||
1189 | cfg.selfsign); | ||
1190 | if (j < 0) | ||
1191 | goto err; | ||
1192 | if (j > 0) { | ||
1193 | total_done++; | ||
1194 | BIO_printf(bio_err, "\n"); | ||
1195 | if (!BN_add_word(serial, 1)) | ||
1196 | goto err; | ||
1197 | if (!sk_X509_push(cert_sk, x)) { | ||
1198 | BIO_printf(bio_err, | ||
1199 | "Memory allocation failure\n"); | ||
1200 | goto err; | ||
1201 | } | ||
1202 | } | ||
1203 | } | ||
1204 | /* | ||
1205 | * we have a stack of newly certified certificates and a data | ||
1206 | * base and serial number that need updating | ||
1207 | */ | ||
1208 | |||
1209 | if (sk_X509_num(cert_sk) > 0) { | ||
1210 | if (!cfg.batch) { | ||
1211 | char answer[10]; | ||
1212 | |||
1213 | BIO_printf(bio_err, | ||
1214 | "\n%d out of %d certificate requests certified, commit? [y/n]", | ||
1215 | total_done, total); | ||
1216 | (void) BIO_flush(bio_err); | ||
1217 | if (fgets(answer, sizeof answer - 1, stdin) == | ||
1218 | NULL) { | ||
1219 | BIO_printf(bio_err, | ||
1220 | "CERTIFICATION CANCELED: I/O error\n"); | ||
1221 | ret = 0; | ||
1222 | goto err; | ||
1223 | } | ||
1224 | if ((answer[0] != 'y') && (answer[0] != 'Y')) { | ||
1225 | BIO_printf(bio_err, | ||
1226 | "CERTIFICATION CANCELED\n"); | ||
1227 | ret = 0; | ||
1228 | goto err; | ||
1229 | } | ||
1230 | } | ||
1231 | BIO_printf(bio_err, | ||
1232 | "Write out database with %d new entries\n", | ||
1233 | sk_X509_num(cert_sk)); | ||
1234 | |||
1235 | if (!save_serial(serialfile, "new", serial, NULL)) | ||
1236 | goto err; | ||
1237 | |||
1238 | if (!save_index(dbfile, "new", db)) | ||
1239 | goto err; | ||
1240 | } | ||
1241 | if (cfg.verbose) | ||
1242 | BIO_printf(bio_err, "writing new certificates\n"); | ||
1243 | for (i = 0; i < sk_X509_num(cert_sk); i++) { | ||
1244 | BIGNUM *bn; | ||
1245 | char *serialstr; | ||
1246 | char pempath[PATH_MAX]; | ||
1247 | int k; | ||
1248 | |||
1249 | x = sk_X509_value(cert_sk, i); | ||
1250 | |||
1251 | if ((bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(x), | ||
1252 | NULL)) == NULL) | ||
1253 | goto err; | ||
1254 | |||
1255 | if (BN_is_zero(bn)) { | ||
1256 | /* For consistency, BN_bn2hex(0) is 0, not 00. */ | ||
1257 | serialstr = strdup("00"); | ||
1258 | } else { | ||
1259 | /* | ||
1260 | * Historical behavior is to ignore the sign | ||
1261 | * that shouldn't be there anyway. | ||
1262 | */ | ||
1263 | BN_set_negative(bn, 0); | ||
1264 | serialstr = BN_bn2hex(bn); | ||
1265 | } | ||
1266 | BN_free(bn); | ||
1267 | |||
1268 | if (serialstr != NULL) { | ||
1269 | k = snprintf(pempath, sizeof(pempath), | ||
1270 | "%s/%s.pem", cfg.outdir, serialstr); | ||
1271 | free(serialstr); | ||
1272 | if (k < 0 || k >= sizeof(pempath)) { | ||
1273 | BIO_printf(bio_err, | ||
1274 | "certificate file name too long\n"); | ||
1275 | goto err; | ||
1276 | } | ||
1277 | } else { | ||
1278 | BIO_printf(bio_err, | ||
1279 | "memory allocation failed\n"); | ||
1280 | goto err; | ||
1281 | } | ||
1282 | if (cfg.verbose) | ||
1283 | BIO_printf(bio_err, "writing %s\n", pempath); | ||
1284 | |||
1285 | if (BIO_write_filename(Cout, pempath) <= 0) { | ||
1286 | perror(pempath); | ||
1287 | goto err; | ||
1288 | } | ||
1289 | if (!write_new_certificate(Cout, x, 0, | ||
1290 | cfg.notext)) | ||
1291 | goto err; | ||
1292 | if (!write_new_certificate(Sout, x, output_der, | ||
1293 | cfg.notext)) | ||
1294 | goto err; | ||
1295 | } | ||
1296 | |||
1297 | if (sk_X509_num(cert_sk)) { | ||
1298 | /* Rename the database and the serial file */ | ||
1299 | if (!rotate_serial(serialfile, "new", "old")) | ||
1300 | goto err; | ||
1301 | |||
1302 | if (!rotate_index(dbfile, "new", "old")) | ||
1303 | goto err; | ||
1304 | |||
1305 | BIO_printf(bio_err, "Data Base Updated\n"); | ||
1306 | } | ||
1307 | } | ||
1308 | /*****************************************************************/ | ||
1309 | if (cfg.gencrl) { | ||
1310 | int crl_v2 = 0; | ||
1311 | if (cfg.crl_ext == NULL) { | ||
1312 | cfg.crl_ext = NCONF_get_string(conf, | ||
1313 | cfg.section, ENV_CRLEXT); | ||
1314 | if (cfg.crl_ext == NULL) | ||
1315 | ERR_clear_error(); | ||
1316 | } | ||
1317 | if (cfg.crl_ext != NULL) { | ||
1318 | /* Check syntax of file */ | ||
1319 | X509V3_CTX ctx; | ||
1320 | X509V3_set_ctx_test(&ctx); | ||
1321 | X509V3_set_nconf(&ctx, conf); | ||
1322 | if (!X509V3_EXT_add_nconf(conf, &ctx, cfg.crl_ext, | ||
1323 | NULL)) { | ||
1324 | BIO_printf(bio_err, | ||
1325 | "Error Loading CRL extension section %s\n", | ||
1326 | cfg.crl_ext); | ||
1327 | ret = 1; | ||
1328 | goto err; | ||
1329 | } | ||
1330 | } | ||
1331 | if ((crlnumberfile = NCONF_get_string(conf, cfg.section, | ||
1332 | ENV_CRLNUMBER)) != NULL) | ||
1333 | if ((crlnumber = load_serial(crlnumberfile, 0, | ||
1334 | NULL)) == NULL) { | ||
1335 | BIO_printf(bio_err, | ||
1336 | "error while loading CRL number\n"); | ||
1337 | goto err; | ||
1338 | } | ||
1339 | if (!cfg.crldays && !cfg.crlhours && | ||
1340 | !cfg.crlsec) { | ||
1341 | if (!NCONF_get_number(conf, cfg.section, | ||
1342 | ENV_DEFAULT_CRL_DAYS, &cfg.crldays)) | ||
1343 | cfg.crldays = 0; | ||
1344 | if (!NCONF_get_number(conf, cfg.section, | ||
1345 | ENV_DEFAULT_CRL_HOURS, &cfg.crlhours)) | ||
1346 | cfg.crlhours = 0; | ||
1347 | ERR_clear_error(); | ||
1348 | } | ||
1349 | if ((cfg.crldays == 0) && (cfg.crlhours == 0) && | ||
1350 | (cfg.crlsec == 0)) { | ||
1351 | BIO_printf(bio_err, | ||
1352 | "cannot lookup how long until the next CRL is issued\n"); | ||
1353 | goto err; | ||
1354 | } | ||
1355 | if (cfg.verbose) | ||
1356 | BIO_printf(bio_err, "making CRL\n"); | ||
1357 | if ((crl = X509_CRL_new()) == NULL) | ||
1358 | goto err; | ||
1359 | if (!X509_CRL_set_issuer_name(crl, X509_get_subject_name(x509))) | ||
1360 | goto err; | ||
1361 | |||
1362 | if ((tmptm = X509_gmtime_adj(NULL, 0)) == NULL) | ||
1363 | goto err; | ||
1364 | if (!X509_CRL_set_lastUpdate(crl, tmptm)) | ||
1365 | goto err; | ||
1366 | if (X509_time_adj_ex(tmptm, cfg.crldays, | ||
1367 | cfg.crlhours * 60 * 60 + cfg.crlsec, NULL) == | ||
1368 | NULL) { | ||
1369 | BIO_puts(bio_err, "error setting CRL nextUpdate\n"); | ||
1370 | goto err; | ||
1371 | } | ||
1372 | if (!X509_CRL_set_nextUpdate(crl, tmptm)) | ||
1373 | goto err; | ||
1374 | ASN1_TIME_free(tmptm); | ||
1375 | tmptm = NULL; | ||
1376 | |||
1377 | for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { | ||
1378 | pp = sk_OPENSSL_PSTRING_value(db->db->data, i); | ||
1379 | if (pp[DB_type][0] == DB_TYPE_REV) { | ||
1380 | if ((r = X509_REVOKED_new()) == NULL) | ||
1381 | goto err; | ||
1382 | j = make_revoked(r, pp[DB_rev_date]); | ||
1383 | if (!j) | ||
1384 | goto err; | ||
1385 | if (j == 2) | ||
1386 | crl_v2 = 1; | ||
1387 | if (!BN_hex2bn(&serial, pp[DB_serial])) | ||
1388 | goto err; | ||
1389 | tmpserial = BN_to_ASN1_INTEGER(serial, NULL); | ||
1390 | BN_free(serial); | ||
1391 | serial = NULL; | ||
1392 | if (tmpserial == NULL) | ||
1393 | goto err; | ||
1394 | if (!X509_REVOKED_set_serialNumber(r, tmpserial)) { | ||
1395 | ASN1_INTEGER_free(tmpserial); | ||
1396 | goto err; | ||
1397 | } | ||
1398 | ASN1_INTEGER_free(tmpserial); | ||
1399 | if (!X509_CRL_add0_revoked(crl, r)) | ||
1400 | goto err; | ||
1401 | r = NULL; | ||
1402 | } | ||
1403 | } | ||
1404 | |||
1405 | /* | ||
1406 | * sort the data so it will be written in serial number order | ||
1407 | */ | ||
1408 | X509_CRL_sort(crl); | ||
1409 | |||
1410 | /* we now have a CRL */ | ||
1411 | if (cfg.verbose) | ||
1412 | BIO_printf(bio_err, "signing CRL\n"); | ||
1413 | |||
1414 | /* Add any extensions asked for */ | ||
1415 | |||
1416 | if (cfg.crl_ext != NULL || crlnumberfile != NULL) { | ||
1417 | X509V3_CTX crlctx; | ||
1418 | X509V3_set_ctx(&crlctx, x509, NULL, NULL, crl, 0); | ||
1419 | X509V3_set_nconf(&crlctx, conf); | ||
1420 | |||
1421 | if (cfg.crl_ext != NULL) | ||
1422 | if (!X509V3_EXT_CRL_add_nconf(conf, &crlctx, | ||
1423 | cfg.crl_ext, crl)) | ||
1424 | goto err; | ||
1425 | if (crlnumberfile != NULL) { | ||
1426 | tmpserial = BN_to_ASN1_INTEGER(crlnumber, NULL); | ||
1427 | if (tmpserial == NULL) | ||
1428 | goto err; | ||
1429 | if (!X509_CRL_add1_ext_i2d(crl, NID_crl_number, | ||
1430 | tmpserial, 0, 0)) { | ||
1431 | ASN1_INTEGER_free(tmpserial); | ||
1432 | goto err; | ||
1433 | } | ||
1434 | ASN1_INTEGER_free(tmpserial); | ||
1435 | crl_v2 = 1; | ||
1436 | if (!BN_add_word(crlnumber, 1)) | ||
1437 | goto err; | ||
1438 | } | ||
1439 | } | ||
1440 | if (cfg.crl_ext != NULL || crl_v2) { | ||
1441 | if (!X509_CRL_set_version(crl, 1)) | ||
1442 | goto err; /* version 2 CRL */ | ||
1443 | } | ||
1444 | if (crlnumberfile != NULL) /* we have a CRL number that | ||
1445 | * need updating */ | ||
1446 | if (!save_serial(crlnumberfile, "new", crlnumber, NULL)) | ||
1447 | goto err; | ||
1448 | |||
1449 | BN_free(crlnumber); | ||
1450 | crlnumber = NULL; | ||
1451 | |||
1452 | if (!do_X509_CRL_sign(bio_err, crl, pkey, dgst, | ||
1453 | cfg.sigopts)) | ||
1454 | goto err; | ||
1455 | |||
1456 | if (!PEM_write_bio_X509_CRL(Sout, crl)) | ||
1457 | goto err; | ||
1458 | |||
1459 | if (crlnumberfile != NULL) /* Rename the crlnumber file */ | ||
1460 | if (!rotate_serial(crlnumberfile, "new", "old")) | ||
1461 | goto err; | ||
1462 | |||
1463 | } | ||
1464 | /*****************************************************************/ | ||
1465 | if (cfg.dorevoke) { | ||
1466 | if (cfg.infile == NULL) { | ||
1467 | BIO_printf(bio_err, "no input files\n"); | ||
1468 | goto err; | ||
1469 | } else { | ||
1470 | X509 *revcert; | ||
1471 | revcert = load_cert(bio_err, cfg.infile, | ||
1472 | FORMAT_PEM, NULL, cfg.infile); | ||
1473 | if (revcert == NULL) | ||
1474 | goto err; | ||
1475 | j = do_revoke(revcert, db, cfg.rev_type, | ||
1476 | cfg.rev_arg); | ||
1477 | if (j <= 0) | ||
1478 | goto err; | ||
1479 | X509_free(revcert); | ||
1480 | |||
1481 | if (!save_index(dbfile, "new", db)) | ||
1482 | goto err; | ||
1483 | |||
1484 | if (!rotate_index(dbfile, "new", "old")) | ||
1485 | goto err; | ||
1486 | |||
1487 | BIO_printf(bio_err, "Data Base Updated\n"); | ||
1488 | } | ||
1489 | } | ||
1490 | /*****************************************************************/ | ||
1491 | ret = 0; | ||
1492 | |||
1493 | err: | ||
1494 | free(tofree); | ||
1495 | |||
1496 | BIO_free_all(Cout); | ||
1497 | BIO_free_all(Sout); | ||
1498 | BIO_free_all(out); | ||
1499 | BIO_free_all(in); | ||
1500 | |||
1501 | sk_X509_pop_free(cert_sk, X509_free); | ||
1502 | |||
1503 | if (ret) | ||
1504 | ERR_print_errors(bio_err); | ||
1505 | if (free_key) | ||
1506 | free(cfg.key); | ||
1507 | BN_free(serial); | ||
1508 | BN_free(crlnumber); | ||
1509 | free_index(db); | ||
1510 | sk_OPENSSL_STRING_free(cfg.sigopts); | ||
1511 | EVP_PKEY_free(pkey); | ||
1512 | X509_free(x509); | ||
1513 | X509_CRL_free(crl); | ||
1514 | X509_REVOKED_free(r); | ||
1515 | ASN1_TIME_free(tmptm); | ||
1516 | NCONF_free(conf); | ||
1517 | NCONF_free(extconf); | ||
1518 | OBJ_cleanup(); | ||
1519 | |||
1520 | return (ret); | ||
1521 | } | ||
1522 | |||
1523 | static void | ||
1524 | lookup_fail(const char *name, const char *tag) | ||
1525 | { | ||
1526 | BIO_printf(bio_err, "variable lookup failed for %s::%s\n", name, tag); | ||
1527 | } | ||
1528 | |||
1529 | static int | ||
1530 | certify(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509, | ||
1531 | const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, | ||
1532 | STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, char *subj, | ||
1533 | unsigned long chtype, int multirdn, int email_dn, char *startdate, | ||
1534 | char *enddate, long days, int batch, char *ext_sect, CONF *lconf, | ||
1535 | int verbose, unsigned long certopt, unsigned long nameopt, int default_op, | ||
1536 | int ext_copy, int selfsign) | ||
1537 | { | ||
1538 | X509_REQ *req = NULL; | ||
1539 | BIO *in = NULL; | ||
1540 | EVP_PKEY *pktmp = NULL; | ||
1541 | int ok = -1, i; | ||
1542 | |||
1543 | in = BIO_new(BIO_s_file()); | ||
1544 | |||
1545 | if (BIO_read_filename(in, infile) <= 0) { | ||
1546 | perror(infile); | ||
1547 | goto err; | ||
1548 | } | ||
1549 | if ((req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL)) == NULL) { | ||
1550 | BIO_printf(bio_err, "Error reading certificate request in %s\n", | ||
1551 | infile); | ||
1552 | goto err; | ||
1553 | } | ||
1554 | if (verbose) { | ||
1555 | if (!X509_REQ_print(bio_err, req)) | ||
1556 | goto err; | ||
1557 | } | ||
1558 | |||
1559 | BIO_printf(bio_err, "Check that the request matches the signature\n"); | ||
1560 | |||
1561 | if (selfsign && !X509_REQ_check_private_key(req, pkey)) { | ||
1562 | BIO_printf(bio_err, | ||
1563 | "Certificate request and CA private key do not match\n"); | ||
1564 | ok = 0; | ||
1565 | goto err; | ||
1566 | } | ||
1567 | if ((pktmp = X509_REQ_get0_pubkey(req)) == NULL) { | ||
1568 | BIO_printf(bio_err, "error unpacking public key\n"); | ||
1569 | goto err; | ||
1570 | } | ||
1571 | i = X509_REQ_verify(req, pktmp); | ||
1572 | if (i < 0) { | ||
1573 | ok = 0; | ||
1574 | BIO_printf(bio_err, "Signature verification problems....\n"); | ||
1575 | goto err; | ||
1576 | } | ||
1577 | if (i == 0) { | ||
1578 | ok = 0; | ||
1579 | BIO_printf(bio_err, | ||
1580 | "Signature did not match the certificate request\n"); | ||
1581 | goto err; | ||
1582 | } else | ||
1583 | BIO_printf(bio_err, "Signature ok\n"); | ||
1584 | |||
1585 | ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, | ||
1586 | subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, | ||
1587 | verbose, req, ext_sect, lconf, certopt, nameopt, default_op, | ||
1588 | ext_copy, selfsign); | ||
1589 | |||
1590 | err: | ||
1591 | X509_REQ_free(req); | ||
1592 | BIO_free(in); | ||
1593 | |||
1594 | return (ok); | ||
1595 | } | ||
1596 | |||
1597 | static int | ||
1598 | certify_cert(X509 **xret, char *infile, EVP_PKEY *pkey, X509 *x509, | ||
1599 | const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, | ||
1600 | STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, char *subj, | ||
1601 | unsigned long chtype, int multirdn, int email_dn, char *startdate, | ||
1602 | char *enddate, long days, int batch, char *ext_sect, CONF *lconf, | ||
1603 | int verbose, unsigned long certopt, unsigned long nameopt, int default_op, | ||
1604 | int ext_copy) | ||
1605 | { | ||
1606 | X509 *req = NULL; | ||
1607 | X509_REQ *rreq = NULL; | ||
1608 | EVP_PKEY *pktmp = NULL; | ||
1609 | int ok = -1, i; | ||
1610 | |||
1611 | if ((req = load_cert(bio_err, infile, FORMAT_PEM, NULL, | ||
1612 | infile)) == NULL) | ||
1613 | goto err; | ||
1614 | if (verbose) { | ||
1615 | if (!X509_print(bio_err, req)) | ||
1616 | goto err; | ||
1617 | } | ||
1618 | |||
1619 | BIO_printf(bio_err, "Check that the request matches the signature\n"); | ||
1620 | |||
1621 | if ((pktmp = X509_get0_pubkey(req)) == NULL) { | ||
1622 | BIO_printf(bio_err, "error unpacking public key\n"); | ||
1623 | goto err; | ||
1624 | } | ||
1625 | i = X509_verify(req, pktmp); | ||
1626 | if (i < 0) { | ||
1627 | ok = 0; | ||
1628 | BIO_printf(bio_err, "Signature verification problems....\n"); | ||
1629 | goto err; | ||
1630 | } | ||
1631 | if (i == 0) { | ||
1632 | ok = 0; | ||
1633 | BIO_printf(bio_err, | ||
1634 | "Signature did not match the certificate\n"); | ||
1635 | goto err; | ||
1636 | } else | ||
1637 | BIO_printf(bio_err, "Signature ok\n"); | ||
1638 | |||
1639 | if ((rreq = X509_to_X509_REQ(req, NULL, EVP_md5())) == NULL) | ||
1640 | goto err; | ||
1641 | |||
1642 | ok = do_body(xret, pkey, x509, dgst, sigopts, policy, db, serial, | ||
1643 | subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, | ||
1644 | verbose, rreq, ext_sect, lconf, certopt, nameopt, default_op, | ||
1645 | ext_copy, 0); | ||
1646 | |||
1647 | err: | ||
1648 | X509_REQ_free(rreq); | ||
1649 | X509_free(req); | ||
1650 | |||
1651 | return (ok); | ||
1652 | } | ||
1653 | |||
1654 | static int | ||
1655 | do_body(X509 **xret, EVP_PKEY *pkey, X509 *x509, const EVP_MD *dgst, | ||
1656 | STACK_OF(OPENSSL_STRING) *sigopts, STACK_OF(CONF_VALUE) *policy, | ||
1657 | CA_DB *db, BIGNUM *serial, char *subj, unsigned long chtype, int multirdn, | ||
1658 | int email_dn, char *startdate, char *enddate, long days, int batch, | ||
1659 | int verbose, X509_REQ *req, char *ext_sect, CONF *lconf, | ||
1660 | unsigned long certopt, unsigned long nameopt, int default_op, | ||
1661 | int ext_copy, int selfsign) | ||
1662 | { | ||
1663 | X509_NAME *name = NULL, *CAname = NULL; | ||
1664 | X509_NAME *subject = NULL, *dn_subject = NULL; | ||
1665 | ASN1_UTCTIME *tm; | ||
1666 | ASN1_STRING *str, *str2; | ||
1667 | ASN1_OBJECT *obj; | ||
1668 | X509 *ret = NULL; | ||
1669 | X509_NAME_ENTRY *ne; | ||
1670 | X509_NAME_ENTRY *tne, *push; | ||
1671 | EVP_PKEY *pktmp; | ||
1672 | int ok = -1, i, j, last; | ||
1673 | const char *p; | ||
1674 | CONF_VALUE *cv; | ||
1675 | OPENSSL_STRING row[DB_NUMBER]; | ||
1676 | OPENSSL_STRING *irow = NULL; | ||
1677 | OPENSSL_STRING *rrow = NULL; | ||
1678 | const STACK_OF(X509_EXTENSION) *exts; | ||
1679 | |||
1680 | *xret = NULL; | ||
1681 | |||
1682 | for (i = 0; i < DB_NUMBER; i++) | ||
1683 | row[i] = NULL; | ||
1684 | |||
1685 | if (subj != NULL) { | ||
1686 | X509_NAME *n = parse_name(subj, chtype, multirdn); | ||
1687 | |||
1688 | if (n == NULL) { | ||
1689 | ERR_print_errors(bio_err); | ||
1690 | goto err; | ||
1691 | } | ||
1692 | if (!X509_REQ_set_subject_name(req, n)) { | ||
1693 | X509_NAME_free(n); | ||
1694 | goto err; | ||
1695 | } | ||
1696 | X509_NAME_free(n); | ||
1697 | } | ||
1698 | if (default_op) | ||
1699 | BIO_printf(bio_err, | ||
1700 | "The Subject's Distinguished Name is as follows\n"); | ||
1701 | |||
1702 | name = X509_REQ_get_subject_name(req); | ||
1703 | for (i = 0; i < X509_NAME_entry_count(name); i++) { | ||
1704 | ne = X509_NAME_get_entry(name, i); | ||
1705 | if (ne == NULL) | ||
1706 | goto err; | ||
1707 | str = X509_NAME_ENTRY_get_data(ne); | ||
1708 | if (str == NULL) | ||
1709 | goto err; | ||
1710 | obj = X509_NAME_ENTRY_get_object(ne); | ||
1711 | if (obj == NULL) | ||
1712 | goto err; | ||
1713 | |||
1714 | /* If no EMAIL is wanted in the subject */ | ||
1715 | if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && (!email_dn)) | ||
1716 | continue; | ||
1717 | |||
1718 | /* check some things */ | ||
1719 | if ((OBJ_obj2nid(obj) == NID_pkcs9_emailAddress) && | ||
1720 | (str->type != V_ASN1_IA5STRING)) { | ||
1721 | BIO_printf(bio_err, | ||
1722 | "\nemailAddress type needs to be of type IA5STRING\n"); | ||
1723 | goto err; | ||
1724 | } | ||
1725 | if ((str->type != V_ASN1_BMPSTRING) && | ||
1726 | (str->type != V_ASN1_UTF8STRING)) { | ||
1727 | j = ASN1_PRINTABLE_type(str->data, str->length); | ||
1728 | if (((j == V_ASN1_T61STRING) && | ||
1729 | (str->type != V_ASN1_T61STRING)) || | ||
1730 | ((j == V_ASN1_IA5STRING) && | ||
1731 | (str->type == V_ASN1_PRINTABLESTRING))) { | ||
1732 | BIO_printf(bio_err, | ||
1733 | "\nThe string contains characters that are illegal for the ASN.1 type\n"); | ||
1734 | goto err; | ||
1735 | } | ||
1736 | } | ||
1737 | if (default_op) | ||
1738 | old_entry_print(bio_err, obj, str); | ||
1739 | } | ||
1740 | |||
1741 | /* Ok, now we check the 'policy' stuff. */ | ||
1742 | if ((subject = X509_NAME_new()) == NULL) { | ||
1743 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
1744 | goto err; | ||
1745 | } | ||
1746 | /* take a copy of the issuer name before we mess with it. */ | ||
1747 | if (selfsign) | ||
1748 | CAname = X509_NAME_dup(name); | ||
1749 | else | ||
1750 | CAname = X509_NAME_dup(X509_get_subject_name(x509)); | ||
1751 | if (CAname == NULL) | ||
1752 | goto err; | ||
1753 | str = str2 = NULL; | ||
1754 | |||
1755 | for (i = 0; i < sk_CONF_VALUE_num(policy); i++) { | ||
1756 | cv = sk_CONF_VALUE_value(policy, i); /* get the object id */ | ||
1757 | if ((j = OBJ_txt2nid(cv->name)) == NID_undef) { | ||
1758 | BIO_printf(bio_err, | ||
1759 | "%s:unknown object type in 'policy' configuration\n", | ||
1760 | cv->name); | ||
1761 | goto err; | ||
1762 | } | ||
1763 | obj = OBJ_nid2obj(j); | ||
1764 | if (obj == NULL) | ||
1765 | goto err; | ||
1766 | |||
1767 | last = -1; | ||
1768 | for (;;) { | ||
1769 | /* lookup the object in the supplied name list */ | ||
1770 | j = X509_NAME_get_index_by_OBJ(name, obj, last); | ||
1771 | if (j < 0) { | ||
1772 | if (last != -1) | ||
1773 | break; | ||
1774 | tne = NULL; | ||
1775 | } else { | ||
1776 | tne = X509_NAME_get_entry(name, j); | ||
1777 | if (tne == NULL) | ||
1778 | goto err; | ||
1779 | } | ||
1780 | last = j; | ||
1781 | |||
1782 | /* depending on the 'policy', decide what to do. */ | ||
1783 | push = NULL; | ||
1784 | if (strcmp(cv->value, "optional") == 0) { | ||
1785 | if (tne != NULL) | ||
1786 | push = tne; | ||
1787 | } else if (strcmp(cv->value, "supplied") == 0) { | ||
1788 | if (tne == NULL) { | ||
1789 | BIO_printf(bio_err, | ||
1790 | "The %s field needed to be supplied and was missing\n", | ||
1791 | cv->name); | ||
1792 | goto err; | ||
1793 | } else | ||
1794 | push = tne; | ||
1795 | } else if (strcmp(cv->value, "match") == 0) { | ||
1796 | int last2; | ||
1797 | |||
1798 | if (tne == NULL) { | ||
1799 | BIO_printf(bio_err, | ||
1800 | "The mandatory %s field was missing\n", | ||
1801 | cv->name); | ||
1802 | goto err; | ||
1803 | } | ||
1804 | last2 = -1; | ||
1805 | |||
1806 | again2: | ||
1807 | j = X509_NAME_get_index_by_OBJ(CAname, obj, | ||
1808 | last2); | ||
1809 | if ((j < 0) && (last2 == -1)) { | ||
1810 | BIO_printf(bio_err, | ||
1811 | "The %s field does not exist in the CA certificate,\nthe 'policy' is misconfigured\n", | ||
1812 | cv->name); | ||
1813 | goto err; | ||
1814 | } | ||
1815 | if (j >= 0) { | ||
1816 | push = X509_NAME_get_entry(CAname, j); | ||
1817 | if (push == NULL) | ||
1818 | goto err; | ||
1819 | str = X509_NAME_ENTRY_get_data(tne); | ||
1820 | if (str == NULL) | ||
1821 | goto err; | ||
1822 | str2 = X509_NAME_ENTRY_get_data(push); | ||
1823 | if (str2 == NULL) | ||
1824 | goto err; | ||
1825 | last2 = j; | ||
1826 | if (ASN1_STRING_cmp(str, str2) != 0) | ||
1827 | goto again2; | ||
1828 | } | ||
1829 | if (j < 0) { | ||
1830 | BIO_printf(bio_err, | ||
1831 | "The %s field needed to be the same in the\nCA certificate (%s) and the request (%s)\n", | ||
1832 | cv->name, ((str2 == NULL) ? | ||
1833 | "NULL" : (char *) str2->data), | ||
1834 | ((str == NULL) ? | ||
1835 | "NULL" : (char *) str->data)); | ||
1836 | goto err; | ||
1837 | } | ||
1838 | } else { | ||
1839 | BIO_printf(bio_err, | ||
1840 | "%s:invalid type in 'policy' configuration\n", | ||
1841 | cv->value); | ||
1842 | goto err; | ||
1843 | } | ||
1844 | |||
1845 | if (push != NULL) { | ||
1846 | if (!X509_NAME_add_entry(subject, push, | ||
1847 | -1, 0)) { | ||
1848 | X509_NAME_ENTRY_free(push); | ||
1849 | BIO_printf(bio_err, | ||
1850 | "Memory allocation failure\n"); | ||
1851 | goto err; | ||
1852 | } | ||
1853 | } | ||
1854 | if (j < 0) | ||
1855 | break; | ||
1856 | } | ||
1857 | } | ||
1858 | |||
1859 | if (cfg.preserve) { | ||
1860 | X509_NAME_free(subject); | ||
1861 | /* subject=X509_NAME_dup(X509_REQ_get_subject_name(req)); */ | ||
1862 | subject = X509_NAME_dup(name); | ||
1863 | if (subject == NULL) | ||
1864 | goto err; | ||
1865 | } | ||
1866 | |||
1867 | /* We are now totally happy, lets make and sign the certificate */ | ||
1868 | if (verbose) | ||
1869 | BIO_printf(bio_err, | ||
1870 | "Everything appears to be ok, creating and signing the certificate\n"); | ||
1871 | |||
1872 | if ((ret = X509_new()) == NULL) | ||
1873 | goto err; | ||
1874 | |||
1875 | #ifdef X509_V3 | ||
1876 | /* Make it an X509 v3 certificate. */ | ||
1877 | if (!X509_set_version(ret, 2)) | ||
1878 | goto err; | ||
1879 | #endif | ||
1880 | if (X509_get_serialNumber(ret) == NULL) | ||
1881 | goto err; | ||
1882 | if (BN_to_ASN1_INTEGER(serial, X509_get_serialNumber(ret)) == NULL) | ||
1883 | goto err; | ||
1884 | if (selfsign) { | ||
1885 | if (!X509_set_issuer_name(ret, subject)) | ||
1886 | goto err; | ||
1887 | } else { | ||
1888 | if (!X509_set_issuer_name(ret, X509_get_subject_name(x509))) | ||
1889 | goto err; | ||
1890 | } | ||
1891 | |||
1892 | if (strcmp(startdate, "today") == 0) { | ||
1893 | if (X509_gmtime_adj(X509_get_notBefore(ret), 0) == NULL) | ||
1894 | goto err; | ||
1895 | } else if (!ASN1_TIME_set_string_X509(X509_get_notBefore(ret), startdate)) { | ||
1896 | BIO_printf(bio_err, "Invalid start date %s\n", startdate); | ||
1897 | goto err; | ||
1898 | } | ||
1899 | |||
1900 | if (enddate == NULL) { | ||
1901 | if (X509_time_adj_ex(X509_get_notAfter(ret), days, 0, | ||
1902 | NULL) == NULL) | ||
1903 | goto err; | ||
1904 | } else if (!ASN1_TIME_set_string_X509(X509_get_notAfter(ret), enddate)) { | ||
1905 | BIO_printf(bio_err, "Invalid end date %s\n", enddate); | ||
1906 | goto err; | ||
1907 | } | ||
1908 | |||
1909 | if (!X509_set_subject_name(ret, subject)) | ||
1910 | goto err; | ||
1911 | |||
1912 | if ((pktmp = X509_REQ_get0_pubkey(req)) == NULL) | ||
1913 | goto err; | ||
1914 | |||
1915 | if (!X509_set_pubkey(ret, pktmp)) | ||
1916 | goto err; | ||
1917 | |||
1918 | /* Lets add the extensions, if there are any */ | ||
1919 | if (ext_sect != NULL) { | ||
1920 | X509V3_CTX ctx; | ||
1921 | |||
1922 | /* Initialize the context structure */ | ||
1923 | if (selfsign) | ||
1924 | X509V3_set_ctx(&ctx, ret, ret, req, NULL, 0); | ||
1925 | else | ||
1926 | X509V3_set_ctx(&ctx, x509, ret, req, NULL, 0); | ||
1927 | |||
1928 | if (extconf != NULL) { | ||
1929 | if (verbose) | ||
1930 | BIO_printf(bio_err, | ||
1931 | "Extra configuration file found\n"); | ||
1932 | |||
1933 | /* Use the extconf configuration db LHASH */ | ||
1934 | X509V3_set_nconf(&ctx, extconf); | ||
1935 | |||
1936 | /* Test the structure (needed?) */ | ||
1937 | /* X509V3_set_ctx_test(&ctx); */ | ||
1938 | |||
1939 | /* Adds exts contained in the configuration file */ | ||
1940 | if (!X509V3_EXT_add_nconf(extconf, &ctx, | ||
1941 | ext_sect, ret)) { | ||
1942 | BIO_printf(bio_err, | ||
1943 | "ERROR: adding extensions in section %s\n", | ||
1944 | ext_sect); | ||
1945 | ERR_print_errors(bio_err); | ||
1946 | goto err; | ||
1947 | } | ||
1948 | if (verbose) | ||
1949 | BIO_printf(bio_err, | ||
1950 | "Successfully added extensions from file.\n"); | ||
1951 | } else if (ext_sect != NULL) { | ||
1952 | /* We found extensions to be set from config file */ | ||
1953 | X509V3_set_nconf(&ctx, lconf); | ||
1954 | |||
1955 | if (!X509V3_EXT_add_nconf(lconf, &ctx, ext_sect, ret)) { | ||
1956 | BIO_printf(bio_err, | ||
1957 | "ERROR: adding extensions in section %s\n", | ||
1958 | ext_sect); | ||
1959 | ERR_print_errors(bio_err); | ||
1960 | goto err; | ||
1961 | } | ||
1962 | if (verbose) | ||
1963 | BIO_printf(bio_err, | ||
1964 | "Successfully added extensions from config\n"); | ||
1965 | } | ||
1966 | } | ||
1967 | |||
1968 | /* Copy extensions from request (if any) */ | ||
1969 | if (!copy_extensions(ret, req, ext_copy)) { | ||
1970 | BIO_printf(bio_err, "ERROR: adding extensions from request\n"); | ||
1971 | ERR_print_errors(bio_err); | ||
1972 | goto err; | ||
1973 | } | ||
1974 | |||
1975 | exts = X509_get0_extensions(ret); | ||
1976 | if (exts != NULL && sk_X509_EXTENSION_num(exts) > 0) { | ||
1977 | /* Make it an X509 v3 certificate. */ | ||
1978 | if (!X509_set_version(ret, 2)) | ||
1979 | goto err; | ||
1980 | } | ||
1981 | |||
1982 | if (verbose) | ||
1983 | BIO_printf(bio_err, | ||
1984 | "The subject name appears to be ok, checking data base for clashes\n"); | ||
1985 | |||
1986 | /* Build the correct Subject if no email is wanted in the subject */ | ||
1987 | if (!email_dn) { | ||
1988 | X509_NAME_ENTRY *tmpne; | ||
1989 | /* | ||
1990 | * Its best to dup the subject DN and then delete any email | ||
1991 | * addresses because this retains its structure. | ||
1992 | */ | ||
1993 | if ((dn_subject = X509_NAME_dup(subject)) == NULL) { | ||
1994 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
1995 | goto err; | ||
1996 | } | ||
1997 | while ((i = X509_NAME_get_index_by_NID(dn_subject, | ||
1998 | NID_pkcs9_emailAddress, -1)) >= 0) { | ||
1999 | tmpne = X509_NAME_get_entry(dn_subject, i); | ||
2000 | if (tmpne == NULL) | ||
2001 | goto err; | ||
2002 | if (X509_NAME_delete_entry(dn_subject, i) == NULL) { | ||
2003 | X509_NAME_ENTRY_free(tmpne); | ||
2004 | goto err; | ||
2005 | } | ||
2006 | X509_NAME_ENTRY_free(tmpne); | ||
2007 | } | ||
2008 | |||
2009 | if (!X509_set_subject_name(ret, dn_subject)) | ||
2010 | goto err; | ||
2011 | |||
2012 | X509_NAME_free(dn_subject); | ||
2013 | dn_subject = NULL; | ||
2014 | } | ||
2015 | |||
2016 | row[DB_name] = X509_NAME_oneline(X509_get_subject_name(ret), NULL, 0); | ||
2017 | if (row[DB_name] == NULL) { | ||
2018 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2019 | goto err; | ||
2020 | } | ||
2021 | |||
2022 | if (BN_is_zero(serial)) | ||
2023 | row[DB_serial] = strdup("00"); | ||
2024 | else | ||
2025 | row[DB_serial] = BN_bn2hex(serial); | ||
2026 | if (row[DB_serial] == NULL) { | ||
2027 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2028 | goto err; | ||
2029 | } | ||
2030 | |||
2031 | if (row[DB_name][0] == '\0') { | ||
2032 | /* | ||
2033 | * An empty subject! We'll use the serial number instead. If | ||
2034 | * unique_subject is in use then we don't want different | ||
2035 | * entries with empty subjects matching each other. | ||
2036 | */ | ||
2037 | free(row[DB_name]); | ||
2038 | row[DB_name] = strdup(row[DB_serial]); | ||
2039 | if (row[DB_name] == NULL) { | ||
2040 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2041 | goto err; | ||
2042 | } | ||
2043 | } | ||
2044 | |||
2045 | if (db->attributes.unique_subject) { | ||
2046 | OPENSSL_STRING *crow = row; | ||
2047 | |||
2048 | rrow = TXT_DB_get_by_index(db->db, DB_name, crow); | ||
2049 | if (rrow != NULL) { | ||
2050 | BIO_printf(bio_err, | ||
2051 | "ERROR:There is already a certificate for %s\n", | ||
2052 | row[DB_name]); | ||
2053 | } | ||
2054 | } | ||
2055 | if (rrow == NULL) { | ||
2056 | rrow = TXT_DB_get_by_index(db->db, DB_serial, row); | ||
2057 | if (rrow != NULL) { | ||
2058 | BIO_printf(bio_err, | ||
2059 | "ERROR:Serial number %s has already been issued,\n", | ||
2060 | row[DB_serial]); | ||
2061 | BIO_printf(bio_err, | ||
2062 | " check the database/serial_file for corruption\n"); | ||
2063 | } | ||
2064 | } | ||
2065 | if (rrow != NULL) { | ||
2066 | BIO_printf(bio_err, | ||
2067 | "The matching entry has the following details\n"); | ||
2068 | if (rrow[DB_type][0] == DB_TYPE_EXP) | ||
2069 | p = "Expired"; | ||
2070 | else if (rrow[DB_type][0] == DB_TYPE_REV) | ||
2071 | p = "Revoked"; | ||
2072 | else if (rrow[DB_type][0] == DB_TYPE_VAL) | ||
2073 | p = "Valid"; | ||
2074 | else | ||
2075 | p = "\ninvalid type, Data base error\n"; | ||
2076 | BIO_printf(bio_err, "Type :%s\n", p); | ||
2077 | if (rrow[DB_type][0] == DB_TYPE_REV) { | ||
2078 | p = rrow[DB_exp_date]; | ||
2079 | if (p == NULL) | ||
2080 | p = "undef"; | ||
2081 | BIO_printf(bio_err, "Was revoked on:%s\n", p); | ||
2082 | } | ||
2083 | p = rrow[DB_exp_date]; | ||
2084 | if (p == NULL) | ||
2085 | p = "undef"; | ||
2086 | BIO_printf(bio_err, "Expires on :%s\n", p); | ||
2087 | p = rrow[DB_serial]; | ||
2088 | if (p == NULL) | ||
2089 | p = "undef"; | ||
2090 | BIO_printf(bio_err, "Serial Number :%s\n", p); | ||
2091 | p = rrow[DB_file]; | ||
2092 | if (p == NULL) | ||
2093 | p = "undef"; | ||
2094 | BIO_printf(bio_err, "File name :%s\n", p); | ||
2095 | p = rrow[DB_name]; | ||
2096 | if (p == NULL) | ||
2097 | p = "undef"; | ||
2098 | BIO_printf(bio_err, "Subject Name :%s\n", p); | ||
2099 | ok = -1; /* This is now a 'bad' error. */ | ||
2100 | goto err; | ||
2101 | } | ||
2102 | |||
2103 | if (!default_op) { | ||
2104 | BIO_printf(bio_err, "Certificate Details:\n"); | ||
2105 | /* | ||
2106 | * Never print signature details because signature not | ||
2107 | * present | ||
2108 | */ | ||
2109 | certopt |= X509_FLAG_NO_SIGDUMP | X509_FLAG_NO_SIGNAME; | ||
2110 | if (!X509_print_ex(bio_err, ret, nameopt, certopt)) | ||
2111 | goto err; | ||
2112 | } | ||
2113 | BIO_printf(bio_err, "Certificate is to be certified until "); | ||
2114 | ASN1_TIME_print(bio_err, X509_get_notAfter(ret)); | ||
2115 | if (days) | ||
2116 | BIO_printf(bio_err, " (%ld days)", days); | ||
2117 | BIO_printf(bio_err, "\n"); | ||
2118 | |||
2119 | if (!batch) { | ||
2120 | char answer[25]; | ||
2121 | |||
2122 | BIO_printf(bio_err, "Sign the certificate? [y/n]:"); | ||
2123 | (void) BIO_flush(bio_err); | ||
2124 | if (!fgets(answer, sizeof(answer) - 1, stdin)) { | ||
2125 | BIO_printf(bio_err, | ||
2126 | "CERTIFICATE WILL NOT BE CERTIFIED: I/O error\n"); | ||
2127 | ok = 0; | ||
2128 | goto err; | ||
2129 | } | ||
2130 | if (!((answer[0] == 'y') || (answer[0] == 'Y'))) { | ||
2131 | BIO_printf(bio_err, | ||
2132 | "CERTIFICATE WILL NOT BE CERTIFIED\n"); | ||
2133 | ok = 0; | ||
2134 | goto err; | ||
2135 | } | ||
2136 | } | ||
2137 | |||
2138 | if ((pktmp = X509_get0_pubkey(ret)) == NULL) | ||
2139 | goto err; | ||
2140 | |||
2141 | if (EVP_PKEY_missing_parameters(pktmp) && | ||
2142 | !EVP_PKEY_missing_parameters(pkey)) { | ||
2143 | if (!EVP_PKEY_copy_parameters(pktmp, pkey)) { | ||
2144 | goto err; | ||
2145 | } | ||
2146 | } | ||
2147 | |||
2148 | if (!do_X509_sign(bio_err, ret, pkey, dgst, sigopts)) | ||
2149 | goto err; | ||
2150 | |||
2151 | /* We now just add it to the database */ | ||
2152 | row[DB_type] = malloc(2); | ||
2153 | |||
2154 | if ((tm = X509_get_notAfter(ret)) == NULL) | ||
2155 | goto err; | ||
2156 | row[DB_exp_date] = strndup(tm->data, tm->length); | ||
2157 | if (row[DB_type] == NULL || row[DB_exp_date] == NULL) { | ||
2158 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2159 | goto err; | ||
2160 | } | ||
2161 | |||
2162 | row[DB_rev_date] = NULL; | ||
2163 | |||
2164 | /* row[DB_serial] done already */ | ||
2165 | row[DB_file] = malloc(8); | ||
2166 | |||
2167 | if ((row[DB_type] == NULL) || (row[DB_file] == NULL) || | ||
2168 | (row[DB_name] == NULL)) { | ||
2169 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2170 | goto err; | ||
2171 | } | ||
2172 | (void) strlcpy(row[DB_file], "unknown", 8); | ||
2173 | row[DB_type][0] = DB_TYPE_VAL; | ||
2174 | row[DB_type][1] = '\0'; | ||
2175 | |||
2176 | if ((irow = reallocarray(NULL, DB_NUMBER + 1, sizeof(char *))) == | ||
2177 | NULL) { | ||
2178 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2179 | goto err; | ||
2180 | } | ||
2181 | for (i = 0; i < DB_NUMBER; i++) { | ||
2182 | irow[i] = row[i]; | ||
2183 | row[i] = NULL; | ||
2184 | } | ||
2185 | irow[DB_NUMBER] = NULL; | ||
2186 | |||
2187 | if (!TXT_DB_insert(db->db, irow)) { | ||
2188 | BIO_printf(bio_err, "failed to update database\n"); | ||
2189 | BIO_printf(bio_err, "TXT_DB error number %ld\n", db->db->error); | ||
2190 | goto err; | ||
2191 | } | ||
2192 | |||
2193 | *xret = ret; | ||
2194 | ret = NULL; | ||
2195 | ok = 1; | ||
2196 | |||
2197 | err: | ||
2198 | for (i = 0; i < DB_NUMBER; i++) | ||
2199 | free(row[i]); | ||
2200 | |||
2201 | X509_NAME_free(CAname); | ||
2202 | X509_NAME_free(subject); | ||
2203 | X509_NAME_free(dn_subject); | ||
2204 | X509_free(ret); | ||
2205 | |||
2206 | return (ok); | ||
2207 | } | ||
2208 | |||
2209 | static int | ||
2210 | write_new_certificate(BIO *bp, X509 *x, int output_der, int notext) | ||
2211 | { | ||
2212 | if (output_der) { | ||
2213 | if (!i2d_X509_bio(bp, x)) | ||
2214 | return (0); | ||
2215 | } | ||
2216 | if (!notext) { | ||
2217 | if (!X509_print(bp, x)) | ||
2218 | return (0); | ||
2219 | } | ||
2220 | |||
2221 | return PEM_write_bio_X509(bp, x); | ||
2222 | } | ||
2223 | |||
2224 | static int | ||
2225 | check_time_format(const char *str) | ||
2226 | { | ||
2227 | return ASN1_TIME_set_string(NULL, str); | ||
2228 | } | ||
2229 | |||
2230 | static int | ||
2231 | do_revoke(X509 *x509, CA_DB *db, int type, char *value) | ||
2232 | { | ||
2233 | ASN1_UTCTIME *tm = NULL; | ||
2234 | char *row[DB_NUMBER], **rrow, **irow; | ||
2235 | char *rev_str = NULL; | ||
2236 | BIGNUM *bn = NULL; | ||
2237 | int ok = -1, i; | ||
2238 | |||
2239 | for (i = 0; i < DB_NUMBER; i++) | ||
2240 | row[i] = NULL; | ||
2241 | row[DB_name] = X509_NAME_oneline(X509_get_subject_name(x509), NULL, 0); | ||
2242 | bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(x509), NULL); | ||
2243 | if (bn == NULL) | ||
2244 | goto err; | ||
2245 | if (BN_is_zero(bn)) | ||
2246 | row[DB_serial] = strdup("00"); | ||
2247 | else | ||
2248 | row[DB_serial] = BN_bn2hex(bn); | ||
2249 | BN_free(bn); | ||
2250 | |||
2251 | if (row[DB_name] != NULL && row[DB_name][0] == '\0') { | ||
2252 | /* | ||
2253 | * Entries with empty Subjects actually use the serial number | ||
2254 | * instead | ||
2255 | */ | ||
2256 | free(row[DB_name]); | ||
2257 | row[DB_name] = strdup(row[DB_serial]); | ||
2258 | if (row[DB_name] == NULL) { | ||
2259 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2260 | goto err; | ||
2261 | } | ||
2262 | } | ||
2263 | |||
2264 | if ((row[DB_name] == NULL) || (row[DB_serial] == NULL)) { | ||
2265 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2266 | goto err; | ||
2267 | } | ||
2268 | /* | ||
2269 | * We have to lookup by serial number because name lookup skips | ||
2270 | * revoked certs | ||
2271 | */ | ||
2272 | rrow = TXT_DB_get_by_index(db->db, DB_serial, row); | ||
2273 | if (rrow == NULL) { | ||
2274 | BIO_printf(bio_err, | ||
2275 | "Adding Entry with serial number %s to DB for %s\n", | ||
2276 | row[DB_serial], row[DB_name]); | ||
2277 | |||
2278 | /* We now just add it to the database */ | ||
2279 | row[DB_type] = malloc(2); | ||
2280 | |||
2281 | if ((tm = X509_get_notAfter(x509)) == NULL) | ||
2282 | goto err; | ||
2283 | row[DB_exp_date] = strndup(tm->data, tm->length); | ||
2284 | if (row[DB_type] == NULL || row[DB_exp_date] == NULL) { | ||
2285 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2286 | goto err; | ||
2287 | } | ||
2288 | |||
2289 | row[DB_rev_date] = NULL; | ||
2290 | |||
2291 | /* row[DB_serial] done already */ | ||
2292 | row[DB_file] = malloc(8); | ||
2293 | |||
2294 | /* row[DB_name] done already */ | ||
2295 | |||
2296 | if ((row[DB_type] == NULL) || (row[DB_file] == NULL)) { | ||
2297 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2298 | goto err; | ||
2299 | } | ||
2300 | (void) strlcpy(row[DB_file], "unknown", 8); | ||
2301 | row[DB_type][0] = DB_TYPE_VAL; | ||
2302 | row[DB_type][1] = '\0'; | ||
2303 | |||
2304 | if ((irow = reallocarray(NULL, sizeof(char *), | ||
2305 | (DB_NUMBER + 1))) == NULL) { | ||
2306 | BIO_printf(bio_err, "Memory allocation failure\n"); | ||
2307 | goto err; | ||
2308 | } | ||
2309 | for (i = 0; i < DB_NUMBER; i++) { | ||
2310 | irow[i] = row[i]; | ||
2311 | row[i] = NULL; | ||
2312 | } | ||
2313 | irow[DB_NUMBER] = NULL; | ||
2314 | |||
2315 | if (!TXT_DB_insert(db->db, irow)) { | ||
2316 | BIO_printf(bio_err, "failed to update database\n"); | ||
2317 | BIO_printf(bio_err, "TXT_DB error number %ld\n", | ||
2318 | db->db->error); | ||
2319 | goto err; | ||
2320 | } | ||
2321 | /* Revoke Certificate */ | ||
2322 | ok = do_revoke(x509, db, type, value); | ||
2323 | |||
2324 | goto err; | ||
2325 | |||
2326 | } else if (index_name_cmp_noconst(row, rrow)) { | ||
2327 | BIO_printf(bio_err, "ERROR:name does not match %s\n", | ||
2328 | row[DB_name]); | ||
2329 | goto err; | ||
2330 | } else if (rrow[DB_type][0] == DB_TYPE_REV) { | ||
2331 | BIO_printf(bio_err, "ERROR:Already revoked, serial number %s\n", | ||
2332 | row[DB_serial]); | ||
2333 | goto err; | ||
2334 | } else { | ||
2335 | BIO_printf(bio_err, "Revoking Certificate %s.\n", | ||
2336 | rrow[DB_serial]); | ||
2337 | rev_str = make_revocation_str(type, value); | ||
2338 | if (rev_str == NULL) { | ||
2339 | BIO_printf(bio_err, "Error in revocation arguments\n"); | ||
2340 | goto err; | ||
2341 | } | ||
2342 | rrow[DB_type][0] = DB_TYPE_REV; | ||
2343 | rrow[DB_type][1] = '\0'; | ||
2344 | rrow[DB_rev_date] = rev_str; | ||
2345 | } | ||
2346 | ok = 1; | ||
2347 | |||
2348 | err: | ||
2349 | for (i = 0; i < DB_NUMBER; i++) | ||
2350 | free(row[i]); | ||
2351 | |||
2352 | return (ok); | ||
2353 | } | ||
2354 | |||
2355 | static int | ||
2356 | get_certificate_status(const char *serial, CA_DB *db) | ||
2357 | { | ||
2358 | char *row[DB_NUMBER], **rrow; | ||
2359 | int ok = -1, i; | ||
2360 | |||
2361 | /* Free Resources */ | ||
2362 | for (i = 0; i < DB_NUMBER; i++) | ||
2363 | row[i] = NULL; | ||
2364 | |||
2365 | /* Malloc needed char spaces */ | ||
2366 | row[DB_serial] = malloc(strlen(serial) + 2); | ||
2367 | if (row[DB_serial] == NULL) { | ||
2368 | BIO_printf(bio_err, "Malloc failure\n"); | ||
2369 | goto err; | ||
2370 | } | ||
2371 | if (strlen(serial) % 2) { | ||
2372 | row[DB_serial][0] = '0'; | ||
2373 | |||
2374 | /* Copy String from serial to row[DB_serial] */ | ||
2375 | memcpy(row[DB_serial] + 1, serial, strlen(serial)); | ||
2376 | row[DB_serial][strlen(serial) + 1] = '\0'; | ||
2377 | } else { | ||
2378 | /* Copy String from serial to row[DB_serial] */ | ||
2379 | memcpy(row[DB_serial], serial, strlen(serial)); | ||
2380 | row[DB_serial][strlen(serial)] = '\0'; | ||
2381 | } | ||
2382 | |||
2383 | /* Make it Upper Case */ | ||
2384 | for (i = 0; row[DB_serial][i] != '\0'; i++) | ||
2385 | row[DB_serial][i] = toupper((unsigned char) row[DB_serial][i]); | ||
2386 | |||
2387 | |||
2388 | ok = 1; | ||
2389 | |||
2390 | /* Search for the certificate */ | ||
2391 | rrow = TXT_DB_get_by_index(db->db, DB_serial, row); | ||
2392 | if (rrow == NULL) { | ||
2393 | BIO_printf(bio_err, "Serial %s not present in db.\n", | ||
2394 | row[DB_serial]); | ||
2395 | ok = -1; | ||
2396 | goto err; | ||
2397 | } else if (rrow[DB_type][0] == DB_TYPE_VAL) { | ||
2398 | BIO_printf(bio_err, "%s=Valid (%c)\n", | ||
2399 | row[DB_serial], rrow[DB_type][0]); | ||
2400 | goto err; | ||
2401 | } else if (rrow[DB_type][0] == DB_TYPE_REV) { | ||
2402 | BIO_printf(bio_err, "%s=Revoked (%c)\n", | ||
2403 | row[DB_serial], rrow[DB_type][0]); | ||
2404 | goto err; | ||
2405 | } else if (rrow[DB_type][0] == DB_TYPE_EXP) { | ||
2406 | BIO_printf(bio_err, "%s=Expired (%c)\n", | ||
2407 | row[DB_serial], rrow[DB_type][0]); | ||
2408 | goto err; | ||
2409 | } else if (rrow[DB_type][0] == DB_TYPE_SUSP) { | ||
2410 | BIO_printf(bio_err, "%s=Suspended (%c)\n", | ||
2411 | row[DB_serial], rrow[DB_type][0]); | ||
2412 | goto err; | ||
2413 | } else { | ||
2414 | BIO_printf(bio_err, "%s=Unknown (%c).\n", | ||
2415 | row[DB_serial], rrow[DB_type][0]); | ||
2416 | ok = -1; | ||
2417 | } | ||
2418 | |||
2419 | err: | ||
2420 | for (i = 0; i < DB_NUMBER; i++) | ||
2421 | free(row[i]); | ||
2422 | |||
2423 | return (ok); | ||
2424 | } | ||
2425 | |||
2426 | static int | ||
2427 | do_updatedb(CA_DB *db) | ||
2428 | { | ||
2429 | ASN1_UTCTIME *a_tm = NULL; | ||
2430 | int i, cnt = 0; | ||
2431 | int db_y2k, a_y2k; /* flags = 1 if y >= 2000 */ | ||
2432 | char **rrow, *a_tm_s = NULL; | ||
2433 | |||
2434 | a_tm = ASN1_UTCTIME_new(); | ||
2435 | if (a_tm == NULL) { | ||
2436 | cnt = -1; | ||
2437 | goto err; | ||
2438 | } | ||
2439 | |||
2440 | /* get actual time and make a string */ | ||
2441 | a_tm = X509_gmtime_adj(a_tm, 0); | ||
2442 | if (a_tm == NULL) { | ||
2443 | cnt = -1; | ||
2444 | goto err; | ||
2445 | } | ||
2446 | a_tm_s = strndup(a_tm->data, a_tm->length); | ||
2447 | if (a_tm_s == NULL) { | ||
2448 | cnt = -1; | ||
2449 | goto err; | ||
2450 | } | ||
2451 | |||
2452 | if (strncmp(a_tm_s, "49", 2) <= 0) | ||
2453 | a_y2k = 1; | ||
2454 | else | ||
2455 | a_y2k = 0; | ||
2456 | |||
2457 | for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) { | ||
2458 | rrow = sk_OPENSSL_PSTRING_value(db->db->data, i); | ||
2459 | |||
2460 | if (rrow[DB_type][0] == DB_TYPE_VAL) { | ||
2461 | /* ignore entries that are not valid */ | ||
2462 | if (strncmp(rrow[DB_exp_date], "49", 2) <= 0) | ||
2463 | db_y2k = 1; | ||
2464 | else | ||
2465 | db_y2k = 0; | ||
2466 | |||
2467 | if (db_y2k == a_y2k) { | ||
2468 | /* all on the same y2k side */ | ||
2469 | if (strcmp(rrow[DB_exp_date], a_tm_s) <= 0) { | ||
2470 | rrow[DB_type][0] = DB_TYPE_EXP; | ||
2471 | rrow[DB_type][1] = '\0'; | ||
2472 | cnt++; | ||
2473 | |||
2474 | BIO_printf(bio_err, "%s=Expired\n", | ||
2475 | rrow[DB_serial]); | ||
2476 | } | ||
2477 | } else if (db_y2k < a_y2k) { | ||
2478 | rrow[DB_type][0] = DB_TYPE_EXP; | ||
2479 | rrow[DB_type][1] = '\0'; | ||
2480 | cnt++; | ||
2481 | |||
2482 | BIO_printf(bio_err, "%s=Expired\n", | ||
2483 | rrow[DB_serial]); | ||
2484 | } | ||
2485 | } | ||
2486 | } | ||
2487 | |||
2488 | err: | ||
2489 | ASN1_UTCTIME_free(a_tm); | ||
2490 | free(a_tm_s); | ||
2491 | |||
2492 | return (cnt); | ||
2493 | } | ||
2494 | |||
2495 | static const char *crl_reasons[] = { | ||
2496 | /* CRL reason strings */ | ||
2497 | "unspecified", | ||
2498 | "keyCompromise", | ||
2499 | "CACompromise", | ||
2500 | "affiliationChanged", | ||
2501 | "superseded", | ||
2502 | "cessationOfOperation", | ||
2503 | "certificateHold", | ||
2504 | "removeFromCRL", | ||
2505 | /* Additional pseudo reasons */ | ||
2506 | "holdInstruction", | ||
2507 | "keyTime", | ||
2508 | "CAkeyTime" | ||
2509 | }; | ||
2510 | |||
2511 | #define NUM_REASONS (sizeof(crl_reasons) / sizeof(char *)) | ||
2512 | |||
2513 | /* Given revocation information convert to a DB string. | ||
2514 | * The format of the string is: | ||
2515 | * revtime[,reason,extra]. Where 'revtime' is the | ||
2516 | * revocation time (the current time). 'reason' is the | ||
2517 | * optional CRL reason and 'extra' is any additional | ||
2518 | * argument | ||
2519 | */ | ||
2520 | |||
2521 | char * | ||
2522 | make_revocation_str(int rev_type, char *rev_arg) | ||
2523 | { | ||
2524 | char *other = NULL, *str; | ||
2525 | const char *reason = NULL; | ||
2526 | ASN1_OBJECT *otmp; | ||
2527 | ASN1_UTCTIME *revtm = NULL; | ||
2528 | int i; | ||
2529 | switch (rev_type) { | ||
2530 | case REV_NONE: | ||
2531 | break; | ||
2532 | |||
2533 | case REV_CRL_REASON: | ||
2534 | for (i = 0; i < 8; i++) { | ||
2535 | if (strcasecmp(rev_arg, crl_reasons[i]) == 0) { | ||
2536 | reason = crl_reasons[i]; | ||
2537 | break; | ||
2538 | } | ||
2539 | } | ||
2540 | if (reason == NULL) { | ||
2541 | BIO_printf(bio_err, "Unknown CRL reason %s\n", rev_arg); | ||
2542 | return NULL; | ||
2543 | } | ||
2544 | break; | ||
2545 | |||
2546 | case REV_HOLD: | ||
2547 | /* Argument is an OID */ | ||
2548 | otmp = OBJ_txt2obj(rev_arg, 0); | ||
2549 | ASN1_OBJECT_free(otmp); | ||
2550 | |||
2551 | if (otmp == NULL) { | ||
2552 | BIO_printf(bio_err, | ||
2553 | "Invalid object identifier %s\n", rev_arg); | ||
2554 | return NULL; | ||
2555 | } | ||
2556 | reason = "holdInstruction"; | ||
2557 | other = rev_arg; | ||
2558 | break; | ||
2559 | |||
2560 | case REV_KEY_COMPROMISE: | ||
2561 | case REV_CA_COMPROMISE: | ||
2562 | /* Argument is the key compromise time */ | ||
2563 | if (!ASN1_GENERALIZEDTIME_set_string(NULL, rev_arg)) { | ||
2564 | BIO_printf(bio_err, | ||
2565 | "Invalid time format %s. Need YYYYMMDDHHMMSSZ\n", | ||
2566 | rev_arg); | ||
2567 | return NULL; | ||
2568 | } | ||
2569 | other = rev_arg; | ||
2570 | if (rev_type == REV_KEY_COMPROMISE) | ||
2571 | reason = "keyTime"; | ||
2572 | else | ||
2573 | reason = "CAkeyTime"; | ||
2574 | |||
2575 | break; | ||
2576 | } | ||
2577 | |||
2578 | revtm = X509_gmtime_adj(NULL, 0); | ||
2579 | if (revtm == NULL) | ||
2580 | return NULL; | ||
2581 | |||
2582 | if (asprintf(&str, "%s%s%s%s%s", revtm->data, | ||
2583 | reason ? "," : "", reason ? reason : "", | ||
2584 | other ? "," : "", other ? other : "") == -1) | ||
2585 | str = NULL; | ||
2586 | |||
2587 | ASN1_UTCTIME_free(revtm); | ||
2588 | |||
2589 | return str; | ||
2590 | } | ||
2591 | |||
2592 | /* Convert revocation field to X509_REVOKED entry | ||
2593 | * return code: | ||
2594 | * 0 error | ||
2595 | * 1 OK | ||
2596 | * 2 OK and some extensions added (i.e. V2 CRL) | ||
2597 | */ | ||
2598 | |||
2599 | int | ||
2600 | make_revoked(X509_REVOKED *rev, const char *str) | ||
2601 | { | ||
2602 | char *tmp = NULL; | ||
2603 | int reason_code = -1; | ||
2604 | int i, ret = 0; | ||
2605 | ASN1_OBJECT *hold = NULL; | ||
2606 | ASN1_GENERALIZEDTIME *comp_time = NULL; | ||
2607 | ASN1_ENUMERATED *rtmp = NULL; | ||
2608 | |||
2609 | ASN1_TIME *revDate = NULL; | ||
2610 | |||
2611 | i = unpack_revinfo(&revDate, &reason_code, &hold, &comp_time, str); | ||
2612 | |||
2613 | if (i == 0) | ||
2614 | goto err; | ||
2615 | |||
2616 | if (rev != NULL && !X509_REVOKED_set_revocationDate(rev, revDate)) | ||
2617 | goto err; | ||
2618 | |||
2619 | if (rev != NULL && (reason_code != OCSP_REVOKED_STATUS_NOSTATUS)) { | ||
2620 | rtmp = ASN1_ENUMERATED_new(); | ||
2621 | if (rtmp == NULL || !ASN1_ENUMERATED_set(rtmp, reason_code)) | ||
2622 | goto err; | ||
2623 | if (!X509_REVOKED_add1_ext_i2d(rev, NID_crl_reason, rtmp, 0, 0)) | ||
2624 | goto err; | ||
2625 | } | ||
2626 | if (rev != NULL && comp_time != NULL) { | ||
2627 | if (!X509_REVOKED_add1_ext_i2d(rev, NID_invalidity_date, | ||
2628 | comp_time, 0, 0)) | ||
2629 | goto err; | ||
2630 | } | ||
2631 | if (rev != NULL && hold != NULL) { | ||
2632 | if (!X509_REVOKED_add1_ext_i2d(rev, NID_hold_instruction_code, | ||
2633 | hold, 0, 0)) | ||
2634 | goto err; | ||
2635 | } | ||
2636 | if (reason_code != OCSP_REVOKED_STATUS_NOSTATUS) | ||
2637 | ret = 2; | ||
2638 | else | ||
2639 | ret = 1; | ||
2640 | |||
2641 | err: | ||
2642 | free(tmp); | ||
2643 | |||
2644 | ASN1_OBJECT_free(hold); | ||
2645 | ASN1_GENERALIZEDTIME_free(comp_time); | ||
2646 | ASN1_ENUMERATED_free(rtmp); | ||
2647 | ASN1_TIME_free(revDate); | ||
2648 | |||
2649 | return ret; | ||
2650 | } | ||
2651 | |||
2652 | int | ||
2653 | old_entry_print(BIO *bp, ASN1_OBJECT *obj, ASN1_STRING *str) | ||
2654 | { | ||
2655 | char buf[25], *pbuf, *p; | ||
2656 | int j; | ||
2657 | |||
2658 | j = i2a_ASN1_OBJECT(bp, obj); | ||
2659 | pbuf = buf; | ||
2660 | for (j = 22 - j; j > 0; j--) | ||
2661 | *(pbuf++) = ' '; | ||
2662 | *(pbuf++) = ':'; | ||
2663 | *(pbuf++) = '\0'; | ||
2664 | BIO_puts(bp, buf); | ||
2665 | |||
2666 | if (str->type == V_ASN1_PRINTABLESTRING) | ||
2667 | BIO_printf(bp, "PRINTABLE:'"); | ||
2668 | else if (str->type == V_ASN1_T61STRING) | ||
2669 | BIO_printf(bp, "T61STRING:'"); | ||
2670 | else if (str->type == V_ASN1_IA5STRING) | ||
2671 | BIO_printf(bp, "IA5STRING:'"); | ||
2672 | else if (str->type == V_ASN1_UNIVERSALSTRING) | ||
2673 | BIO_printf(bp, "UNIVERSALSTRING:'"); | ||
2674 | else | ||
2675 | BIO_printf(bp, "ASN.1 %2d:'", str->type); | ||
2676 | |||
2677 | p = (char *) str->data; | ||
2678 | for (j = str->length; j > 0; j--) { | ||
2679 | if ((*p >= ' ') && (*p <= '~')) | ||
2680 | BIO_printf(bp, "%c", *p); | ||
2681 | else if (*p & 0x80) | ||
2682 | BIO_printf(bp, "\\0x%02X", *p); | ||
2683 | else if ((unsigned char) *p == 0xf7) | ||
2684 | BIO_printf(bp, "^?"); | ||
2685 | else | ||
2686 | BIO_printf(bp, "^%c", *p + '@'); | ||
2687 | p++; | ||
2688 | } | ||
2689 | BIO_printf(bp, "'\n"); | ||
2690 | return 1; | ||
2691 | } | ||
2692 | |||
2693 | int | ||
2694 | unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, | ||
2695 | ASN1_GENERALIZEDTIME **pinvtm, const char *str) | ||
2696 | { | ||
2697 | char *tmp = NULL; | ||
2698 | char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; | ||
2699 | int reason_code = -1; | ||
2700 | int ret = 0; | ||
2701 | unsigned int i; | ||
2702 | ASN1_OBJECT *hold = NULL; | ||
2703 | ASN1_GENERALIZEDTIME *comp_time = NULL; | ||
2704 | |||
2705 | if ((tmp = strdup(str)) == NULL) { | ||
2706 | BIO_printf(bio_err, "malloc failed\n"); | ||
2707 | goto err; | ||
2708 | } | ||
2709 | p = strchr(tmp, ','); | ||
2710 | rtime_str = tmp; | ||
2711 | |||
2712 | if (p != NULL) { | ||
2713 | *p = '\0'; | ||
2714 | p++; | ||
2715 | reason_str = p; | ||
2716 | p = strchr(p, ','); | ||
2717 | if (p != NULL) { | ||
2718 | *p = '\0'; | ||
2719 | arg_str = p + 1; | ||
2720 | } | ||
2721 | } | ||
2722 | if (prevtm != NULL) { | ||
2723 | *prevtm = ASN1_UTCTIME_new(); | ||
2724 | if (!ASN1_UTCTIME_set_string(*prevtm, rtime_str)) { | ||
2725 | BIO_printf(bio_err, "invalid revocation date %s\n", | ||
2726 | rtime_str); | ||
2727 | goto err; | ||
2728 | } | ||
2729 | } | ||
2730 | if (reason_str != NULL) { | ||
2731 | for (i = 0; i < NUM_REASONS; i++) { | ||
2732 | if (strcasecmp(reason_str, crl_reasons[i]) == 0) { | ||
2733 | reason_code = i; | ||
2734 | break; | ||
2735 | } | ||
2736 | } | ||
2737 | if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { | ||
2738 | BIO_printf(bio_err, "invalid reason code %s\n", | ||
2739 | reason_str); | ||
2740 | goto err; | ||
2741 | } | ||
2742 | if (reason_code == 7) | ||
2743 | reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; | ||
2744 | else if (reason_code == 8) { /* Hold instruction */ | ||
2745 | if (arg_str == NULL) { | ||
2746 | BIO_printf(bio_err, | ||
2747 | "missing hold instruction\n"); | ||
2748 | goto err; | ||
2749 | } | ||
2750 | reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; | ||
2751 | hold = OBJ_txt2obj(arg_str, 0); | ||
2752 | |||
2753 | if (hold == NULL) { | ||
2754 | BIO_printf(bio_err, | ||
2755 | "invalid object identifier %s\n", arg_str); | ||
2756 | goto err; | ||
2757 | } | ||
2758 | if (phold != NULL) | ||
2759 | *phold = hold; | ||
2760 | } else if ((reason_code == 9) || (reason_code == 10)) { | ||
2761 | if (arg_str == NULL) { | ||
2762 | BIO_printf(bio_err, | ||
2763 | "missing compromised time\n"); | ||
2764 | goto err; | ||
2765 | } | ||
2766 | comp_time = ASN1_GENERALIZEDTIME_new(); | ||
2767 | if (!ASN1_GENERALIZEDTIME_set_string(comp_time, | ||
2768 | arg_str)) { | ||
2769 | BIO_printf(bio_err, | ||
2770 | "invalid compromised time %s\n", arg_str); | ||
2771 | goto err; | ||
2772 | } | ||
2773 | if (reason_code == 9) | ||
2774 | reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; | ||
2775 | else | ||
2776 | reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; | ||
2777 | } | ||
2778 | } | ||
2779 | if (preason != NULL) | ||
2780 | *preason = reason_code; | ||
2781 | if (pinvtm != NULL) | ||
2782 | *pinvtm = comp_time; | ||
2783 | else | ||
2784 | ASN1_GENERALIZEDTIME_free(comp_time); | ||
2785 | |||
2786 | ret = 1; | ||
2787 | |||
2788 | err: | ||
2789 | free(tmp); | ||
2790 | |||
2791 | if (phold == NULL) | ||
2792 | ASN1_OBJECT_free(hold); | ||
2793 | if (pinvtm == NULL) | ||
2794 | ASN1_GENERALIZEDTIME_free(comp_time); | ||
2795 | |||
2796 | return ret; | ||
2797 | } | ||