1 /* $NetBSD: tls_certkey.c,v 1.4 2022/10/08 16:12:50 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* tls_certkey 3
6 /* SUMMARY
7 /* public key certificate and private key loader
8 /* SYNOPSIS
9 /* #define TLS_INTERNAL
10 /* #include <tls.h>
11 /*
12 /* int tls_set_ca_certificate_info(ctx, CAfile, CApath)
13 /* SSL_CTX *ctx;
14 /* const char *CAfile;
15 /* const char *CApath;
16 /*
17 /* int tls_set_my_certificate_key_info(ctx, chain_files,
18 /* cert_file, key_file,
19 /* dcert_file, dkey_file,
20 /* eccert_file, eckey_file)
21 /* SSL_CTX *ctx;
22 /* const char *chain_files;
23 /* const char *cert_file;
24 /* const char *key_file;
25 /* const char *dcert_file;
26 /* const char *dkey_file;
27 /* const char *eccert_file;
28 /* const char *eckey_file;
29 /*
30 /* int tls_load_pem_chain(ssl, pem, origin);
31 /* SSL *ssl;
32 /* const char *pem;
33 /* const char *origin;
34 /* DESCRIPTION
35 /* OpenSSL supports two options to specify CA certificates:
36 /* either one file CAfile that contains all CA certificates,
37 /* or a directory CApath with separate files for each
38 /* individual CA, with symbolic links named after the hash
39 /* values of the certificates. The second option is not
40 /* convenient with a chrooted process.
41 /*
42 /* tls_set_ca_certificate_info() loads the CA certificate
43 /* information for the specified TLS server or client context.
44 /* The result is -1 on failure, 0 on success.
45 /*
46 /* tls_set_my_certificate_key_info() loads the public key
47 /* certificates and private keys for the specified TLS server
48 /* or client context. Up to 3 pairs of key pairs (RSA, DSA and
49 /* ECDSA) may be specified; each certificate and key pair must
50 /* match. The chain_files argument makes it possible to load
51 /* keys and certificates for more than 3 algorithms, via either
52 /* a single file, or a list of multiple files. The result is -1
53 /* on failure, 0 on success.
54 /*
55 /* tls_load_pem_chain() loads one or more (key, cert, [chain])
56 /* triples from an in-memory PEM blob. The "origin" argument
57 /* is used for error logging, to identify the provenance of the
58 /* PEM blob. "ssl" must be non-zero, and the keys and certificates
59 /* will be loaded into that object.
60 /* LICENSE
61 /* .ad
62 /* .fi
63 /* This software is free. You can do with it whatever you want.
64 /* The original author kindly requests that you acknowledge
65 /* the use of his software.
66 /* AUTHOR(S)
67 /* Originally written by:
68 /* Lutz Jaenicke
69 /* BTU Cottbus
70 /* Allgemeine Elektrotechnik
71 /* Universitaetsplatz 3-4
72 /* D-03044 Cottbus, Germany
73 /*
74 /* Updated by:
75 /* Wietse Venema
76 /* IBM T.J. Watson Research
77 /* P.O. Box 704
78 /* Yorktown Heights, NY 10598, USA
79 /*
80 /* Wietse Venema
81 /* Google, Inc.
82 /* 111 8th Avenue
83 /* New York, NY 10011, USA
84 /*--*/
85
86 /* System library. */
87
88 #include <sys_defs.h>
89
90 #ifdef USE_TLS
91
92 /* Utility library. */
93
94 #include <msg.h>
95
96 /* Global library. */
97
98 #include <mail_params.h>
99
100 /* TLS library. */
101
102 #define TLS_INTERNAL
103 #include <tls.h>
104
105 #define PEM_LOAD_STATE_NOGO -2 /* Unusable object or sequence */
106 #define PEM_LOAD_STATE_FAIL -1 /* Error in libcrypto */
107 #define PEM_LOAD_STATE_DONE 0 /* End of PEM file, return value only */
108 #define PEM_LOAD_STATE_INIT 1 /* No PEM objects seen */
109 #define PEM_LOAD_STATE_PKEY 2 /* Last object was a private key */
110 #define PEM_LOAD_STATE_CERT 3 /* Last object was a certificate */
111 #define PEM_LOAD_STATE_BOTH 4 /* Unordered, key + first cert seen */
112
113 #define PEM_LOAD_READ_LAST 0 /* Reading last file */
114 #define PEM_LOAD_READ_MORE 1 /* More files to be read */
115
116 typedef struct pem_load_state_t {
117 const char *origin; /* PEM chain origin description */
118 const char *source; /* PEM BIO origin description */
119 const char *keysrc; /* Source of last key */
120 BIO *pembio; /* PEM input stream */
121 SSL_CTX *ctx; /* SSL connection factory */
122 SSL *ssl; /* SSL connection handle */
123 EVP_PKEY *pkey; /* current key */
124 X509 *cert; /* current certificate */
125 x509_stack_t *chain; /* current chain */
126 int keynum; /* Index of last key */
127 int objnum; /* Index in current source */
128 int state; /* Current state, never "DONE" */
129 int mixed; /* Single file with key anywhere */
130 } pem_load_state_t;
131
132 /* init_pem_load_state - fill in initial pem_load_state structure */
133
init_pem_load_state(pem_load_state_t * st,SSL_CTX * ctx,SSL * ssl,const char * origin)134 static void init_pem_load_state(pem_load_state_t *st, SSL_CTX *ctx, SSL *ssl,
135 const char *origin)
136 {
137 st->origin = origin;
138 st->source = origin;
139 st->keysrc = 0;
140 st->pembio = 0;
141 st->ctx = ctx;
142 st->ssl = ssl;
143 st->pkey = 0;
144 st->cert = 0;
145 st->chain = 0;
146 st->keynum = 0;
147 st->objnum = 0;
148 st->state = PEM_LOAD_STATE_INIT;
149 st->mixed = 0;
150 }
151
152 /* use_chain - load cert, key and chain into ctx or ssl */
153
use_chain(pem_load_state_t * st)154 static int use_chain(pem_load_state_t *st)
155 {
156 int ret;
157 int replace = 0;
158
159 /*
160 * With replace == 0, an error is returned if the algorithm slot is
161 * already taken, and a previous key + chain of the same type would be
162 * clobbered.
163 */
164 if (st->ctx)
165 ret = SSL_CTX_use_cert_and_key(st->ctx, st->cert, st->pkey, st->chain,
166 replace);
167 else
168 ret = SSL_use_cert_and_key(st->ssl, st->cert, st->pkey, st->chain,
169 replace);
170
171 /*
172 * SSL_[CTX_]_use_cert_key() uprefs all the objects in question, so we
173 * must free ours.
174 */
175 X509_free(st->cert);
176 st->cert = 0;
177 EVP_PKEY_free(st->pkey);
178 st->pkey = 0;
179 sk_X509_pop_free(st->chain, X509_free);
180 st->chain = 0;
181
182 return ret;
183 }
184
185 /* load_cert - decode and load a DER-encoded X509 certificate */
186
load_cert(pem_load_state_t * st,unsigned char * buf,long buflen)187 static void load_cert(pem_load_state_t *st, unsigned char *buf,
188 long buflen)
189 {
190 const unsigned char *p = buf;
191 X509 *cert = d2i_X509(0, &p, buflen);
192
193 /*
194 * When expecting one or more keys, each key must precede the associated
195 * certificate (chain).
196 */
197 if (!st->mixed && st->state == PEM_LOAD_STATE_INIT) {
198 msg_warn("error loading chain from %s: key not first", st->source);
199 if (cert)
200 X509_free(cert);
201 st->state = PEM_LOAD_STATE_NOGO;
202 return;
203 }
204 if (!cert) {
205 msg_warn("error loading certificate (PEM object number %d) from %s",
206 st->objnum, st->source);
207 st->state = PEM_LOAD_STATE_FAIL;
208 return;
209 }
210 if (p - buf != buflen) {
211 msg_warn("error loading certificate (PEM object number %d) from %s:"
212 " excess data", st->objnum, st->source);
213 X509_free(cert);
214 st->state = PEM_LOAD_STATE_NOGO;
215 return;
216 }
217
218 /*
219 * The first certificate after a new key becomes the leaf certificate for
220 * that key. Subsequent certificates are added to the issuer chain.
221 *
222 * In "mixed" mode, the first certificate is either after the key, or else
223 * comes first.
224 */
225 switch (st->state) {
226 case PEM_LOAD_STATE_PKEY:
227 st->cert = cert;
228 st->state = st->mixed ? PEM_LOAD_STATE_BOTH : PEM_LOAD_STATE_CERT;
229 return;
230 case PEM_LOAD_STATE_INIT:
231 st->cert = cert;
232 st->state = PEM_LOAD_STATE_CERT;
233 return;
234 case PEM_LOAD_STATE_CERT:
235 case PEM_LOAD_STATE_BOTH:
236 if ((!st->chain && (st->chain = sk_X509_new_null()) == 0)
237 || !sk_X509_push(st->chain, cert)) {
238 X509_free(cert);
239 st->state = PEM_LOAD_STATE_FAIL;
240 }
241 return;
242 }
243 }
244
245 /* load_pkey - decode and load a DER-encoded private key */
246
load_pkey(pem_load_state_t * st,int pkey_type,unsigned char * buf,long buflen)247 static void load_pkey(pem_load_state_t *st, int pkey_type,
248 unsigned char *buf, long buflen)
249 {
250 const char *myname = "load_pkey";
251 const unsigned char *p = buf;
252 PKCS8_PRIV_KEY_INFO *p8;
253 EVP_PKEY *pkey = 0;
254
255 /*
256 * Keys are either algorithm-specific, or else (ideally) algorithm
257 * agnostic, in which case they are wrapped as PKCS#8 objects with an
258 * algorithm OID.
259 */
260 if (pkey_type != NID_undef) {
261 pkey = d2i_PrivateKey(pkey_type, 0, &p, buflen);
262 } else {
263 p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, buflen);
264 if (p8) {
265 pkey = EVP_PKCS82PKEY(p8);
266 PKCS8_PRIV_KEY_INFO_free(p8);
267 }
268 }
269
270 /*
271 * Except in "mixed" mode, where a single key appears anywhere in a file
272 * with multiple certificates, a given key is either at the first object
273 * we process, or occurs after a previous key and one or more associated
274 * certificates. Thus, encountering a key in a state other than "INIT"
275 * or "CERT" is an error, except in "mixed" mode where a second key is
276 * ignored with a warning.
277 */
278 switch (st->state) {
279 case PEM_LOAD_STATE_CERT:
280
281 /*
282 * When processing the key of a "next" chain, we're in the "CERT"
283 * state, and first complete the processing of the previous chain.
284 */
285 if (!st->mixed && !use_chain(st)) {
286 msg_warn("error loading certificate chain: "
287 "key at index %d in %s does not match the certificate",
288 st->keynum, st->keysrc);
289 st->state = PEM_LOAD_STATE_FAIL;
290 return;
291 }
292 /* FALLTHROUGH */
293 case PEM_LOAD_STATE_INIT:
294
295 if (!pkey) {
296 msg_warn("error loading private key (PEM object number %d) from %s",
297 st->objnum, st->source);
298 st->state = PEM_LOAD_STATE_FAIL;
299 return;
300 }
301 /* Reject unexpected data beyond the end of the DER-encoded object */
302 if (p - buf != buflen) {
303 msg_warn("error loading private key (PEM object number %d) from"
304 " %s: excess data", st->objnum, st->source);
305 EVP_PKEY_free(pkey);
306 st->state = PEM_LOAD_STATE_NOGO;
307 return;
308 }
309 /* All's well, update the state */
310 st->pkey = pkey;
311 if (st->state == PEM_LOAD_STATE_INIT)
312 st->state = PEM_LOAD_STATE_PKEY;
313 else if (st->mixed)
314 st->state = PEM_LOAD_STATE_BOTH;
315 else
316 st->state = PEM_LOAD_STATE_PKEY;
317 return;
318
319 case PEM_LOAD_STATE_PKEY:
320 case PEM_LOAD_STATE_BOTH:
321 if (pkey)
322 EVP_PKEY_free(pkey);
323
324 /* XXX: Legacy behavior was silent, should we stay silent? */
325 if (st->mixed) {
326 msg_warn("ignoring 2nd key at index %d in %s after 1st at %d",
327 st->objnum, st->source, st->keynum);
328 return;
329 }
330 /* else back-to-back keys */
331 msg_warn("error loading certificate chain: "
332 "key at index %d in %s not followed by a certificate",
333 st->keynum, st->keysrc);
334 st->state = PEM_LOAD_STATE_NOGO;
335 return;
336
337 default:
338 msg_error("%s: internal error: bad state: %d", myname, st->state);
339 st->state = PEM_LOAD_STATE_NOGO;
340 return;
341 }
342 }
343
344 /* load_pem_object - load next pkey or cert from open BIO */
345
load_pem_object(pem_load_state_t * st)346 static int load_pem_object(pem_load_state_t *st)
347 {
348 char *name = 0;
349 char *header = 0;
350 unsigned char *buf = 0;
351 long buflen;
352 int pkey_type = NID_undef;
353
354 if (!PEM_read_bio(st->pembio, &name, &header, &buf, &buflen)) {
355 if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
356 return (st->state = PEM_LOAD_STATE_FAIL);
357
358 ERR_clear_error();
359 /* Clean EOF, preserve stored state for any next input file */
360 return (PEM_LOAD_STATE_DONE);
361 }
362 if (strcmp(name, PEM_STRING_X509) == 0
363 || strcmp(name, PEM_STRING_X509_OLD) == 0) {
364 load_cert(st, buf, buflen);
365 } else if (strcmp(name, PEM_STRING_PKCS8INF) == 0
366 || ((pkey_type = EVP_PKEY_RSA) != NID_undef
367 && strcmp(name, PEM_STRING_RSA) == 0)
368 || ((pkey_type = EVP_PKEY_EC) != NID_undef
369 && strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0)
370 || ((pkey_type = EVP_PKEY_DSA) != NID_undef
371 && strcmp(name, PEM_STRING_DSA) == 0)) {
372 load_pkey(st, pkey_type, buf, buflen);
373 } else if (!st->mixed) {
374 msg_warn("loading %s: ignoring PEM type: %s", st->source, name);
375 }
376 OPENSSL_free(name);
377 OPENSSL_free(header);
378 OPENSSL_free(buf);
379 return (st->state);
380 }
381
382 /* load_pem_bio - load all key/certs from bio and free the bio */
383
load_pem_bio(pem_load_state_t * st,int more)384 static int load_pem_bio(pem_load_state_t *st, int more)
385 {
386 int state = st->state;
387
388 /* Don't report old news */
389 ERR_clear_error();
390
391 /*
392 * When "more" is PEM_LOAD_READ_MORE, more files will be loaded after the
393 * current file, and final processing for the last key and chain is
394 * deferred.
395 *
396 * When "more" is PEM_LOAD_READ_LAST, this is the last file in the list, and
397 * we validate the final chain.
398 *
399 * When st->mixed is true, this is the only file, and its key can occur at
400 * any location. In this case we load at most one key.
401 */
402 for (st->objnum = 1; state > PEM_LOAD_STATE_DONE; ++st->objnum) {
403 state = load_pem_object(st);
404 if ((st->mixed && st->keynum == 0 &&
405 (state == PEM_LOAD_STATE_PKEY || state == PEM_LOAD_STATE_BOTH))
406 || (!st->mixed && state == PEM_LOAD_STATE_PKEY)) {
407 /* Squirrel-away the current key location */
408 st->keynum = st->objnum;
409 st->keysrc = st->source;
410 }
411 }
412 /* We're responsible for unconditionally freeing the BIO */
413 BIO_free(st->pembio);
414
415 /* Success with current file, go back for more? */
416 if (more == PEM_LOAD_READ_MORE && state >= PEM_LOAD_STATE_DONE)
417 return 0;
418
419 /*
420 * If all is well so far, complete processing for the final chain.
421 */
422 switch (st->state) {
423 case PEM_LOAD_STATE_FAIL:
424 tls_print_errors();
425 break;
426 default:
427 break;
428 case PEM_LOAD_STATE_INIT:
429 msg_warn("No PEM data in %s", st->origin);
430 break;
431 case PEM_LOAD_STATE_PKEY:
432 msg_warn("No certs for key at index %d in %s", st->keynum, st->keysrc);
433 break;
434 case PEM_LOAD_STATE_CERT:
435 if (st->mixed) {
436 msg_warn("No private key found in %s", st->origin);
437 break;
438 }
439 /* FALLTHROUGH */
440 case PEM_LOAD_STATE_BOTH:
441 /* use_chain() frees the key and certs, and zeroes the pointers */
442 if (use_chain(st))
443 return (0);
444 msg_warn("key at index %d in %s does not match next certificate",
445 st->keynum, st->keysrc);
446 tls_print_errors();
447 break;
448 }
449 /* Free any left-over unused keys and certs */
450 EVP_PKEY_free(st->pkey);
451 X509_free(st->cert);
452 sk_X509_pop_free(st->chain, X509_free);
453
454 msg_warn("error loading private keys and certificates from: %s: %s",
455 st->origin, st->ctx ? "disabling TLS support" :
456 "aborting TLS handshake");
457 return (-1);
458 }
459
460 /* load_chain_files - load sequence of (key, cert, [chain]) from files */
461
load_chain_files(SSL_CTX * ctx,const char * chain_files)462 static int load_chain_files(SSL_CTX *ctx, const char *chain_files)
463 {
464 pem_load_state_t st;
465 ARGV *files = argv_split(chain_files, CHARS_COMMA_SP);
466 char **filep;
467 int ret = 0;
468 int more;
469
470 init_pem_load_state(&st, ctx, 0, chain_files);
471 for (filep = files->argv; ret == 0 && *filep; ++filep) {
472 st.source = *filep;
473 if ((st.pembio = BIO_new_file(st.source, "r")) == NULL) {
474 msg_warn("error opening chain file: %s: %m", st.source);
475 st.state = PEM_LOAD_STATE_NOGO;
476 break;
477 }
478 more = filep[1] ? PEM_LOAD_READ_MORE : PEM_LOAD_READ_LAST;
479 /* load_pem_bio() frees the BIO */
480 ret = load_pem_bio(&st, more);
481 }
482 argv_free(files);
483 return (ret);
484 }
485
486 /* load_mixed_file - load certs with single key anywhere in the file */
487
load_mixed_file(SSL_CTX * ctx,const char * file)488 static int load_mixed_file(SSL_CTX *ctx, const char *file)
489 {
490 pem_load_state_t st;
491
492 init_pem_load_state(&st, ctx, 0, file);
493 if ((st.pembio = BIO_new_file(st.source, "r")) == NULL) {
494 msg_warn("error opening chain file: %s: %m", st.source);
495 return (-1);
496 }
497 st.mixed = 1;
498 /* load_pem_bio() frees the BIO */
499 return load_pem_bio(&st, PEM_LOAD_READ_LAST);
500 }
501
502 /* tls_set_ca_certificate_info - load Certification Authority certificates */
503
tls_set_ca_certificate_info(SSL_CTX * ctx,const char * CAfile,const char * CApath)504 int tls_set_ca_certificate_info(SSL_CTX *ctx, const char *CAfile,
505 const char *CApath)
506 {
507 if (*CAfile == 0)
508 CAfile = 0;
509 if (*CApath == 0)
510 CApath = 0;
511
512 #define CA_PATH_FMT "%s%s%s"
513 #define CA_PATH_ARGS(var, nextvar) \
514 var ? #var "=\"" : "", \
515 var ? var : "", \
516 var ? (nextvar ? "\", " : "\"") : ""
517
518 if (CAfile || CApath) {
519 if (!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) {
520 msg_info("cannot load Certification Authority data, "
521 CA_PATH_FMT CA_PATH_FMT ": disabling TLS support",
522 CA_PATH_ARGS(CAfile, CApath),
523 CA_PATH_ARGS(CApath, 0));
524 tls_print_errors();
525 return (-1);
526 }
527 if (var_tls_append_def_CA && !SSL_CTX_set_default_verify_paths(ctx)) {
528 msg_info("cannot set default OpenSSL certificate verification "
529 "paths: disabling TLS support");
530 tls_print_errors();
531 return (-1);
532 }
533 }
534 return (0);
535 }
536
537 /* set_cert_stuff - specify certificate and key information */
538
set_cert_stuff(SSL_CTX * ctx,const char * cert_type,const char * cert_file,const char * key_file)539 static int set_cert_stuff(SSL_CTX *ctx, const char *cert_type,
540 const char *cert_file,
541 const char *key_file)
542 {
543
544 /*
545 * When the certfile and keyfile are one and the same, load both in a
546 * single pass, avoiding potential race conditions during key rollover.
547 */
548 if (strcmp(cert_file, key_file) == 0)
549 return (load_mixed_file(ctx, cert_file) == 0);
550
551 /*
552 * We need both the private key (in key_file) and the public key
553 * certificate (in cert_file).
554 *
555 * Code adapted from OpenSSL apps/s_cb.c.
556 */
557 ERR_clear_error();
558 if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) {
559 msg_warn("cannot get %s certificate from file \"%s\": "
560 "disabling TLS support", cert_type, cert_file);
561 tls_print_errors();
562 return (0);
563 }
564 if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
565 msg_warn("cannot get %s private key from file \"%s\": "
566 "disabling TLS support", cert_type, key_file);
567 tls_print_errors();
568 return (0);
569 }
570
571 /*
572 * Sanity check.
573 */
574 if (!SSL_CTX_check_private_key(ctx)) {
575 msg_warn("%s private key in %s does not match public key in %s: "
576 "disabling TLS support", cert_type, key_file, cert_file);
577 return (0);
578 }
579 return (1);
580 }
581
582 /* tls_set_my_certificate_key_info - load client or server certificates/keys */
583
tls_set_my_certificate_key_info(SSL_CTX * ctx,const char * chain_files,const char * cert_file,const char * key_file,const char * dcert_file,const char * dkey_file,const char * eccert_file,const char * eckey_file)584 int tls_set_my_certificate_key_info(SSL_CTX *ctx, const char *chain_files,
585 const char *cert_file,
586 const char *key_file,
587 const char *dcert_file,
588 const char *dkey_file,
589 const char *eccert_file,
590 const char *eckey_file)
591 {
592
593 /* The "chain_files" parameter overrides all the legacy parameters */
594 if (chain_files && *chain_files)
595 return load_chain_files(ctx, chain_files);
596
597 /*
598 * Lack of certificates is fine so long as we are prepared to use
599 * anonymous ciphers.
600 */
601 if (*cert_file && !set_cert_stuff(ctx, "RSA", cert_file, key_file))
602 return (-1); /* logged */
603 if (*dcert_file && !set_cert_stuff(ctx, "DSA", dcert_file, dkey_file))
604 return (-1); /* logged */
605 #ifndef OPENSSL_NO_ECDH
606 if (*eccert_file && !set_cert_stuff(ctx, "ECDSA", eccert_file, eckey_file))
607 return (-1); /* logged */
608 #else
609 if (*eccert_file)
610 msg_warn("ECDSA not supported. Ignoring ECDSA certificate file \"%s\"",
611 eccert_file);
612 #endif
613 return (0);
614 }
615
616 /* tls_load_pem_chain - load in-memory PEM client or server chain */
617
tls_load_pem_chain(SSL * ssl,const char * pem,const char * origin)618 int tls_load_pem_chain(SSL *ssl, const char *pem, const char *origin)
619 {
620 static VSTRING *obuf;
621 pem_load_state_t st;
622
623 if (!obuf)
624 obuf = vstring_alloc(100);
625 vstring_sprintf(obuf, "SNI data for %s", origin);
626 init_pem_load_state(&st, 0, ssl, vstring_str(obuf));
627
628 if ((st.pembio = BIO_new_mem_buf(pem, -1)) == NULL) {
629 msg_warn("error opening memory BIO for %s", st.origin);
630 tls_print_errors();
631 return (-1);
632 }
633 /* load_pem_bio() frees the BIO */
634 return (load_pem_bio(&st, PEM_LOAD_READ_LAST));
635 }
636
637 #ifdef TEST
638
usage(void)639 static NORETURN usage(void)
640 {
641 fprintf(stderr, "usage: tls_certkey [-m] <chainfiles>\n");
642 exit(1);
643 }
644
main(int argc,char * argv[])645 int main(int argc, char *argv[])
646 {
647 int ch;
648 int mixed = 0;
649 int ret;
650 char *key_file = 0;
651 SSL_CTX *ctx;
652
653 if (!(ctx = SSL_CTX_new(TLS_client_method()))) {
654 tls_print_errors();
655 exit(1);
656 }
657 while ((ch = GETOPT(argc, argv, "mk:")) > 0) {
658 switch (ch) {
659 case 'k':
660 key_file = optarg;
661 break;
662 case 'm':
663 mixed = 1;
664 break;
665 default:
666 usage();
667 }
668 }
669 argc -= optind;
670 argv += optind;
671
672 if (argc < 1)
673 usage();
674
675 if (key_file)
676 ret = set_cert_stuff(ctx, "any", argv[0], key_file) == 0;
677 else if (mixed)
678 ret = load_mixed_file(ctx, argv[0]);
679 else
680 ret = load_chain_files(ctx, argv[0]);
681
682 if (ret != 0)
683 exit(1);
684
685 if (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST) != 1) {
686 fprintf(stderr, "error selecting first certificate\n");
687 tls_print_errors();
688 exit(1);
689 }
690 do {
691 STACK_OF(X509) *chain;
692 int i;
693
694 if (SSL_CTX_get0_chain_certs(ctx, &chain) != 1) {
695 fprintf(stderr, "error locating certificate chain\n");
696 tls_print_errors();
697 exit(1);
698 }
699 for (i = 0; i <= sk_X509_num(chain); ++i) {
700 char buf[CCERT_BUFSIZ];
701 X509 *cert;
702
703 if (i > 0)
704 cert = sk_X509_value(chain, i - 1);
705 else
706 cert = SSL_CTX_get0_certificate(ctx);
707
708 printf("depth = %d\n", i);
709
710 X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
711 printf("issuer = %s\n", buf);
712
713 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
714 printf("subject = %s\n\n", buf);
715 }
716 } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT) != 0);
717
718 exit(0);
719 }
720
721 #endif
722
723 #endif
724