4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <sys/types.h>
34 #include <sys/wanboot_impl.h>
37 #include <libinetutil.h>
38 #include <wanbootutil.h>
40 #include <openssl/crypto.h>
41 #include <openssl/buffer.h>
42 #include <openssl/bio.h>
43 #include <openssl/err.h>
44 #include <openssl/x509.h>
45 #include <openssl/x509v3.h>
46 #include <openssl/pkcs12.h>
47 #include <openssl/evp.h>
50 static boolean_t verbose
= B_FALSE
; /* When nonzero, do in verbose mode */
52 /* The following match/cert values require PKCS12 */
53 static int matchty
; /* Type of matching do to on input */
54 static char *k_matchval
; /* localkeyid value to match */
55 static uint_t k_len
; /* length of k_matchval */
57 #define IO_KEYFILE 1 /* Have a separate key file or data */
58 #define IO_CERTFILE 2 /* Have a separate cert file or data */
59 #define IO_TRUSTFILE 4 /* Have a separate trustanchor file */
61 static char *input
= NULL
; /* Consolidated input file */
62 static char *key_out
= NULL
; /* Key file to be output */
63 static char *cert_out
= NULL
; /* Cert file to be output */
64 static char *trust_out
= NULL
; /* Trust anchor file to be output */
65 static uint_t outfiles
; /* What files are there for output */
66 static char *progname
;
68 /* Returns from time_check */
70 CHK_TIME_OK
= 0, /* Cert in effect and not expired */
71 CHK_TIME_BEFORE_BAD
, /* not_before field is invalid */
72 CHK_TIME_AFTER_BAD
, /* not_after field is invalid */
73 CHK_TIME_IS_BEFORE
, /* Cert not yet in force */
74 CHK_TIME_HAS_EXPIRED
/* Cert has expired */
77 static int parse_keyid(const char *);
78 static int do_certs(void);
79 static int read_files(STACK_OF(X509
) **, X509
**, EVP_PKEY
**);
80 static void check_certs(STACK_OF(X509
) *, X509
**);
81 static time_errs_t
time_check_print(X509
*);
82 static time_errs_t
time_check(X509
*);
83 static int write_files(STACK_OF(X509
) *, X509
*, EVP_PKEY
*);
84 static int get_ifile(char *, char *, EVP_PKEY
**, X509
**, STACK_OF(X509
) **);
85 static int do_ofile(char *, EVP_PKEY
*, X509
*, STACK_OF(X509
) *);
86 static void usage(void);
87 static const char *cryptoerr(void);
90 main(int argc
, char **argv
)
95 * Do the necessary magic for localization support.
97 (void) setlocale(LC_ALL
, "");
98 #if !defined(TEXT_DOMAIN)
99 #define TEXT_DOMAIN "SYS_TEST"
101 (void) textdomain(TEXT_DOMAIN
);
103 progname
= strrchr(argv
[0], '/');
104 if (progname
!= NULL
)
109 wbku_errinit(progname
);
111 matchty
= DO_FIRST_PAIR
;
112 while ((i
= getopt(argc
, argv
, "vc:i:k:l:t:")) != -1) {
119 if (parse_keyid(optarg
) < 0)
120 return (EXIT_FAILURE
);
121 matchty
= DO_FIND_KEYID
;
126 outfiles
|= IO_CERTFILE
;
131 outfiles
|= IO_KEYFILE
;
136 outfiles
|= IO_TRUSTFILE
;
149 wbku_printerr("no input file specified\n");
157 wbku_printerr("at least one output file must be specified\n");
162 return (EXIT_FAILURE
);
164 return (EXIT_SUCCESS
);
168 parse_keyid(const char *keystr
)
176 * In the worst case, we'll need one additional character in our
177 * output string -- e.g. "A\0" -> "0A\0"
179 nkeystrlen
= strlen(keystr
) + 2;
180 k_len
= (nkeystrlen
+ 1) / 2;
181 nkeystr
= malloc(nkeystrlen
);
182 k_matchval
= malloc(k_len
);
183 if (nkeystr
== NULL
|| k_matchval
== NULL
) {
186 wbku_printerr("cannot allocate keyid");
191 * For convenience, we allow the user to put spaces between each digit
192 * when entering it on the command line. As a result, we need to
193 * process it into a format that hexascii_to_octet() can handle. Note
194 * that we're careful to map strings like "AA B CC D" to "AA0BCC0D".
196 for (rp
= keystr
, wp
= nkeystr
; *rp
!= '\0'; rp
++) {
200 if (rp
[1] == ' ' || rp
[1] == '\0') {
201 *wp
++ = '0'; /* one character sequence; prepend 0 */
210 if (hexascii_to_octet(nkeystr
, wp
- nkeystr
, k_matchval
, &k_len
) != 0) {
213 wbku_printerr("invalid keyid `%s'\n", keystr
);
225 STACK_OF(X509
) *ta_in
= NULL
;
226 EVP_PKEY
*pkey_in
= NULL
;
227 X509
*xcert_in
= NULL
;
231 if (read_files(&ta_in
, &xcert_in
, &pkey_in
) < 0)
235 if (xcert_in
!= NULL
) {
236 (void) printf(gettext("\nMain cert:\n"));
239 * sunw_subject_attrs() returns a pointer to
240 * memory allocated on our behalf. The same
241 * behavior is exhibited by sunw_issuer_attrs().
243 bufp
= sunw_subject_attrs(xcert_in
, NULL
, 0);
245 (void) printf(gettext(" Subject: %s\n"),
250 bufp
= sunw_issuer_attrs(xcert_in
, NULL
, 0);
252 (void) printf(gettext(" Issuer: %s\n"), bufp
);
256 (void) sunw_print_times(stdout
, PRNT_BOTH
, NULL
,
264 for (i
= 0; i
< sk_X509_num(ta_in
); i
++) {
266 x
= sk_X509_value(ta_in
, i
);
268 gettext("\nTrust Anchor cert %d:\n"), i
);
271 * sunw_subject_attrs() returns a pointer to
272 * memory allocated on our behalf. We get the
273 * same behavior from sunw_issuer_attrs().
275 bufp
= sunw_subject_attrs(x
, NULL
, 0);
278 gettext(" Subject: %s\n"), bufp
);
282 bufp
= sunw_issuer_attrs(x
, NULL
, 0);
285 gettext(" Issuer: %s\n"), bufp
);
289 (void) sunw_print_times(stdout
, PRNT_BOTH
,
295 check_certs(ta_in
, &xcert_in
);
296 if (xcert_in
!= NULL
&& pkey_in
!= NULL
) {
297 if (sunw_check_keys(xcert_in
, pkey_in
) == 0) {
298 wbku_printerr("warning: key and certificate do "
303 return (write_files(ta_in
, xcert_in
, pkey_in
));
307 read_files(STACK_OF(X509
) **t_in
, X509
**c_in
, EVP_PKEY
**k_in
)
311 i_pass
= getpassphrase(gettext("Enter key password: "));
313 if (get_ifile(input
, i_pass
, k_in
, c_in
, t_in
) < 0)
317 * If we are only interested in getting a trust anchor, and if there
318 * is no trust anchor but is a regular cert, use it instead. Do this
319 * to handle the insanity with openssl, which requires a matching cert
320 * and key in order to write a PKCS12 file.
322 if (outfiles
== IO_TRUSTFILE
) {
323 if (c_in
!= NULL
&& *c_in
!= NULL
&& t_in
!= NULL
) {
325 if ((*t_in
= sk_X509_new_null()) == NULL
) {
326 wbku_printerr("out of memory\n");
331 if (sk_X509_num(*t_in
) == 0) {
332 if (sk_X509_push(*t_in
, *c_in
) == 0) {
333 wbku_printerr("out of memory\n");
341 if ((outfiles
& IO_KEYFILE
) && *k_in
== NULL
) {
342 wbku_printerr("no matching key found\n");
345 if ((outfiles
& IO_CERTFILE
) && *c_in
== NULL
) {
346 wbku_printerr("no matching certificate found\n");
349 if ((outfiles
& IO_TRUSTFILE
) && *t_in
== NULL
) {
350 wbku_printerr("no matching trust anchor found\n");
358 check_certs(STACK_OF(X509
) *ta_in
, X509
**c_in
)
363 int del_expired
= (outfiles
!= 0);
365 if (c_in
!= NULL
&& *c_in
!= NULL
) {
366 ret
= time_check_print(*c_in
);
367 if ((ret
!= CHK_TIME_OK
&& ret
!= CHK_TIME_IS_BEFORE
) &&
369 (void) fprintf(stderr
, gettext(" Removing cert\n"));
378 for (i
= 0; i
< sk_X509_num(ta_in
); ) {
380 curr
= sk_X509_value(ta_in
, i
);
381 ret
= time_check_print(curr
);
382 if ((ret
!= CHK_TIME_OK
&& ret
!= CHK_TIME_IS_BEFORE
) &&
384 (void) fprintf(stderr
, gettext(" Removing cert\n"));
386 curr
= sk_X509_delete(ta_in
, i
);
395 time_check_print(X509
*cert
)
400 ret
= time_check(cert
);
401 if (ret
== CHK_TIME_OK
)
402 return (CHK_TIME_OK
);
404 (void) fprintf(stderr
, gettext(" Subject: %s"),
405 sunw_subject_attrs(cert
, buf
, sizeof (buf
)));
406 (void) fprintf(stderr
, gettext(" Issuer: %s"),
407 sunw_issuer_attrs(cert
, buf
, sizeof (buf
)));
410 case CHK_TIME_BEFORE_BAD
:
411 (void) fprintf(stderr
,
412 gettext("\n Invalid cert 'not before' field\n"));
415 case CHK_TIME_AFTER_BAD
:
416 (void) fprintf(stderr
,
417 gettext("\n Invalid cert 'not after' field\n"));
420 case CHK_TIME_HAS_EXPIRED
:
421 (void) sunw_print_times(stderr
, PRNT_NOT_AFTER
,
422 gettext("\n Cert has expired\n"), cert
);
425 case CHK_TIME_IS_BEFORE
:
426 (void) sunw_print_times(stderr
, PRNT_NOT_BEFORE
,
427 gettext("\n Warning: cert not yet valid\n"), cert
);
438 time_check(X509
*cert
)
442 i
= X509_cmp_time(X509_get_notBefore(cert
), NULL
);
444 return (CHK_TIME_BEFORE_BAD
);
446 return (CHK_TIME_IS_BEFORE
);
447 /* After 'not before' time */
449 i
= X509_cmp_time(X509_get_notAfter(cert
), NULL
);
451 return (CHK_TIME_AFTER_BAD
);
453 return (CHK_TIME_HAS_EXPIRED
);
454 return (CHK_TIME_OK
);
458 write_files(STACK_OF(X509
) *t_out
, X509
*c_out
, EVP_PKEY
*k_out
)
460 if (key_out
!= NULL
) {
462 (void) printf(gettext("%s: writing key\n"), progname
);
463 if (do_ofile(key_out
, k_out
, NULL
, NULL
) < 0)
467 if (cert_out
!= NULL
) {
469 (void) printf(gettext("%s: writing cert\n"), progname
);
470 if (do_ofile(cert_out
, NULL
, c_out
, NULL
) < 0)
474 if (trust_out
!= NULL
) {
476 (void) printf(gettext("%s: writing trust\n"),
478 if (do_ofile(trust_out
, NULL
, NULL
, t_out
) < 0)
486 get_ifile(char *name
, char *pass
, EVP_PKEY
**tmp_k
, X509
**tmp_c
,
487 STACK_OF(X509
) **tmp_t
)
494 if (stat(name
, &sbuf
) == 0 && !S_ISREG(sbuf
.st_mode
)) {
495 wbku_printerr("%s is not a regular file\n", name
);
499 if ((fp
= fopen(name
, "r")) == NULL
) {
500 wbku_printerr("cannot open input file %s", name
);
504 p12
= d2i_PKCS12_fp(fp
, NULL
);
506 wbku_printerr("cannot read file %s: %s\n", name
, cryptoerr());
512 ret
= sunw_PKCS12_parse(p12
, pass
, matchty
, k_matchval
, k_len
,
513 NULL
, tmp_k
, tmp_c
, tmp_t
);
516 wbku_printerr("cannot find matching cert and key\n");
518 wbku_printerr("cannot parse %s: %s\n", name
,
527 do_ofile(char *name
, EVP_PKEY
*pkey
, X509
*cert
, STACK_OF(X509
) *ta
)
529 STACK_OF(EVP_PKEY
) *klist
= NULL
;
530 STACK_OF(X509
) *clist
= NULL
;
536 if (stat(name
, &sbuf
) == 0 && !S_ISREG(sbuf
.st_mode
)) {
537 wbku_printerr("%s is not a regular file\n", name
);
541 if ((fp
= fopen(name
, "w")) == NULL
) {
542 wbku_printerr("cannot open output file %s", name
);
546 if ((clist
= sk_X509_new_null()) == NULL
||
547 (klist
= sk_EVP_PKEY_new_null()) == NULL
) {
548 wbku_printerr("out of memory\n");
553 if (cert
!= NULL
&& sk_X509_push(clist
, cert
) == 0) {
554 wbku_printerr("out of memory\n");
559 if (pkey
!= NULL
&& sk_EVP_PKEY_push(klist
, pkey
) == 0) {
560 wbku_printerr("out of memory\n");
565 p12
= sunw_PKCS12_create(WANBOOT_PASSPHRASE
, klist
, clist
, ta
);
567 wbku_printerr("cannot create %s: %s\n", name
, cryptoerr());
572 if (i2d_PKCS12_fp(fp
, p12
) == 0) {
573 wbku_printerr("cannot write %s: %s\n", name
, cryptoerr());
583 * Put the cert and pkey off of the stack so that they won't
584 * be freed two times. (If they get left in the stack then
585 * they will be freed with the stack.)
588 if (cert
!= NULL
&& sk_X509_num(clist
) == 1) {
590 (void) sk_X509_delete(clist
, 0);
592 sk_X509_pop_free(clist
, X509_free
);
595 if (pkey
!= NULL
&& sk_EVP_PKEY_num(klist
) == 1) {
597 (void) sk_EVP_PKEY_delete(klist
, 0);
599 sk_EVP_PKEY_pop_free(klist
, sunw_evp_pkey_free
);
608 (void) fprintf(stderr
,
610 " %s -i <file> -c <file> -k <file> -t <file> [-l <keyid> -v]\n"
613 (void) fprintf(stderr
,
615 " -i - input file to be split into component parts and put in\n"
616 " files given by -c, -k and -t\n"
617 " -c - output file for the client certificate\n"
618 " -k - output file for the client private key\n"
619 " -t - output file for the remaining certificates (assumed\n"
620 " to be trust anchors)\n"
621 "\n Files are assumed to be pkcs12-format files.\n\n"
623 " -l - value of 'localkeyid' attribute in client cert and\n"
624 " private key to be selected from the input file.\n\n"));
629 * Return a pointer to a static buffer that contains a listing of crypto
630 * errors. We presume that the user doesn't want more than 8KB of error
636 static char errbuf
[8192];
640 unsigned int nerr
= 0;
643 while ((err
= ERR_get_error_line(&pfile
, &line
)) != 0) {
645 (void) strlcat(errbuf
, "\n\t", sizeof (errbuf
));
647 if (err
== (ulong_t
)-1) {
648 (void) strlcat(errbuf
, strerror(errno
),
652 (void) strlcat(errbuf
, ERR_reason_error_string(err
),