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.
30 #include <sys/types.h>
32 #include <sys/wanboot_impl.h>
35 #include <libinetutil.h>
36 #include <wanbootutil.h>
38 #include <openssl/crypto.h>
39 #include <openssl/buffer.h>
40 #include <openssl/bio.h>
41 #include <openssl/err.h>
42 #include <openssl/x509.h>
43 #include <openssl/x509v3.h>
44 #include <openssl/pkcs12.h>
45 #include <openssl/evp.h>
48 static boolean_t verbose
= B_FALSE
; /* When nonzero, do in verbose mode */
50 /* The following match/cert values require PKCS12 */
51 static int matchty
; /* Type of matching do to on input */
52 static char *k_matchval
; /* localkeyid value to match */
53 static uint_t k_len
; /* length of k_matchval */
55 #define IO_KEYFILE 1 /* Have a separate key file or data */
56 #define IO_CERTFILE 2 /* Have a separate cert file or data */
57 #define IO_TRUSTFILE 4 /* Have a separate trustanchor file */
59 static char *input
= NULL
; /* Consolidated input file */
60 static char *key_out
= NULL
; /* Key file to be output */
61 static char *cert_out
= NULL
; /* Cert file to be output */
62 static char *trust_out
= NULL
; /* Trust anchor file to be output */
63 static uint_t outfiles
; /* What files are there for output */
64 static char *progname
;
66 /* Returns from time_check */
68 CHK_TIME_OK
= 0, /* Cert in effect and not expired */
69 CHK_TIME_BEFORE_BAD
, /* not_before field is invalid */
70 CHK_TIME_AFTER_BAD
, /* not_after field is invalid */
71 CHK_TIME_IS_BEFORE
, /* Cert not yet in force */
72 CHK_TIME_HAS_EXPIRED
/* Cert has expired */
75 static int parse_keyid(const char *);
76 static int do_certs(void);
77 static int read_files(STACK_OF(X509
) **, X509
**, EVP_PKEY
**);
78 static void check_certs(STACK_OF(X509
) *, X509
**);
79 static time_errs_t
time_check_print(X509
*);
80 static time_errs_t
time_check(X509
*);
81 static int write_files(STACK_OF(X509
) *, X509
*, EVP_PKEY
*);
82 static int get_ifile(char *, char *, EVP_PKEY
**, X509
**, STACK_OF(X509
) **);
83 static int do_ofile(char *, EVP_PKEY
*, X509
*, STACK_OF(X509
) *);
84 static void usage(void);
85 static const char *cryptoerr(void);
88 main(int argc
, char **argv
)
93 * Do the necessary magic for localization support.
95 (void) setlocale(LC_ALL
, "");
96 #if !defined(TEXT_DOMAIN)
97 #define TEXT_DOMAIN "SYS_TEST"
99 (void) textdomain(TEXT_DOMAIN
);
101 progname
= strrchr(argv
[0], '/');
102 if (progname
!= NULL
)
107 wbku_errinit(progname
);
109 matchty
= DO_FIRST_PAIR
;
110 while ((i
= getopt(argc
, argv
, "vc:i:k:l:t:")) != -1) {
117 if (parse_keyid(optarg
) < 0)
118 return (EXIT_FAILURE
);
119 matchty
= DO_FIND_KEYID
;
124 outfiles
|= IO_CERTFILE
;
129 outfiles
|= IO_KEYFILE
;
134 outfiles
|= IO_TRUSTFILE
;
147 wbku_printerr("no input file specified\n");
155 wbku_printerr("at least one output file must be specified\n");
160 return (EXIT_FAILURE
);
162 return (EXIT_SUCCESS
);
166 parse_keyid(const char *keystr
)
174 * In the worst case, we'll need one additional character in our
175 * output string -- e.g. "A\0" -> "0A\0"
177 nkeystrlen
= strlen(keystr
) + 2;
178 k_len
= (nkeystrlen
+ 1) / 2;
179 nkeystr
= malloc(nkeystrlen
);
180 k_matchval
= malloc(k_len
);
181 if (nkeystr
== NULL
|| k_matchval
== NULL
) {
184 wbku_printerr("cannot allocate keyid");
189 * For convenience, we allow the user to put spaces between each digit
190 * when entering it on the command line. As a result, we need to
191 * process it into a format that hexascii_to_octet() can handle. Note
192 * that we're careful to map strings like "AA B CC D" to "AA0BCC0D".
194 for (rp
= keystr
, wp
= nkeystr
; *rp
!= '\0'; rp
++) {
198 if (rp
[1] == ' ' || rp
[1] == '\0') {
199 *wp
++ = '0'; /* one character sequence; prepend 0 */
208 if (hexascii_to_octet(nkeystr
, wp
- nkeystr
, k_matchval
, &k_len
) != 0) {
211 wbku_printerr("invalid keyid `%s'\n", keystr
);
223 STACK_OF(X509
) *ta_in
= NULL
;
224 EVP_PKEY
*pkey_in
= NULL
;
225 X509
*xcert_in
= NULL
;
229 if (read_files(&ta_in
, &xcert_in
, &pkey_in
) < 0)
233 if (xcert_in
!= NULL
) {
234 (void) printf(gettext("\nMain cert:\n"));
237 * sunw_subject_attrs() returns a pointer to
238 * memory allocated on our behalf. The same
239 * behavior is exhibited by sunw_issuer_attrs().
241 bufp
= sunw_subject_attrs(xcert_in
, NULL
, 0);
243 (void) printf(gettext(" Subject: %s\n"),
248 bufp
= sunw_issuer_attrs(xcert_in
, NULL
, 0);
250 (void) printf(gettext(" Issuer: %s\n"), bufp
);
254 (void) sunw_print_times(stdout
, PRNT_BOTH
, NULL
,
262 for (i
= 0; i
< sk_X509_num(ta_in
); i
++) {
263 x
= sk_X509_value(ta_in
, i
);
265 gettext("\nTrust Anchor cert %d:\n"), i
);
268 * sunw_subject_attrs() returns a pointer to
269 * memory allocated on our behalf. We get the
270 * same behavior from sunw_issuer_attrs().
272 bufp
= sunw_subject_attrs(x
, NULL
, 0);
275 gettext(" Subject: %s\n"), bufp
);
279 bufp
= sunw_issuer_attrs(x
, NULL
, 0);
282 gettext(" Issuer: %s\n"), bufp
);
286 (void) sunw_print_times(stdout
, PRNT_BOTH
,
292 check_certs(ta_in
, &xcert_in
);
293 if (xcert_in
!= NULL
&& pkey_in
!= NULL
) {
294 if (sunw_check_keys(xcert_in
, pkey_in
) == 0) {
295 wbku_printerr("warning: key and certificate do "
300 return (write_files(ta_in
, xcert_in
, pkey_in
));
304 read_files(STACK_OF(X509
) **t_in
, X509
**c_in
, EVP_PKEY
**k_in
)
308 i_pass
= getpassphrase(gettext("Enter key password: "));
310 if (get_ifile(input
, i_pass
, k_in
, c_in
, t_in
) < 0)
314 * If we are only interested in getting a trust anchor, and if there
315 * is no trust anchor but is a regular cert, use it instead. Do this
316 * to handle the insanity with openssl, which requires a matching cert
317 * and key in order to write a PKCS12 file.
319 if (outfiles
== IO_TRUSTFILE
) {
320 if (c_in
!= NULL
&& *c_in
!= NULL
&& t_in
!= NULL
) {
322 if ((*t_in
= sk_X509_new_null()) == NULL
) {
323 wbku_printerr("out of memory\n");
328 if (sk_X509_num(*t_in
) == 0) {
329 if (sk_X509_push(*t_in
, *c_in
) == 0) {
330 wbku_printerr("out of memory\n");
338 if ((outfiles
& IO_KEYFILE
) && *k_in
== NULL
) {
339 wbku_printerr("no matching key found\n");
342 if ((outfiles
& IO_CERTFILE
) && *c_in
== NULL
) {
343 wbku_printerr("no matching certificate found\n");
346 if ((outfiles
& IO_TRUSTFILE
) && *t_in
== NULL
) {
347 wbku_printerr("no matching trust anchor found\n");
355 check_certs(STACK_OF(X509
) *ta_in
, X509
**c_in
)
360 int del_expired
= (outfiles
!= 0);
362 if (c_in
!= NULL
&& *c_in
!= NULL
) {
363 ret
= time_check_print(*c_in
);
364 if ((ret
!= CHK_TIME_OK
&& ret
!= CHK_TIME_IS_BEFORE
) &&
366 (void) fprintf(stderr
, gettext(" Removing cert\n"));
375 for (i
= 0; i
< sk_X509_num(ta_in
); ) {
376 curr
= sk_X509_value(ta_in
, i
);
377 ret
= time_check_print(curr
);
378 if ((ret
!= CHK_TIME_OK
&& ret
!= CHK_TIME_IS_BEFORE
) &&
380 (void) fprintf(stderr
, gettext(" Removing cert\n"));
381 curr
= sk_X509_delete(ta_in
, i
);
390 time_check_print(X509
*cert
)
395 ret
= time_check(cert
);
396 if (ret
== CHK_TIME_OK
)
397 return (CHK_TIME_OK
);
399 (void) fprintf(stderr
, gettext(" Subject: %s"),
400 sunw_subject_attrs(cert
, buf
, sizeof (buf
)));
401 (void) fprintf(stderr
, gettext(" Issuer: %s"),
402 sunw_issuer_attrs(cert
, buf
, sizeof (buf
)));
405 case CHK_TIME_BEFORE_BAD
:
406 (void) fprintf(stderr
,
407 gettext("\n Invalid cert 'not before' field\n"));
410 case CHK_TIME_AFTER_BAD
:
411 (void) fprintf(stderr
,
412 gettext("\n Invalid cert 'not after' field\n"));
415 case CHK_TIME_HAS_EXPIRED
:
416 (void) sunw_print_times(stderr
, PRNT_NOT_AFTER
,
417 gettext("\n Cert has expired\n"), cert
);
420 case CHK_TIME_IS_BEFORE
:
421 (void) sunw_print_times(stderr
, PRNT_NOT_BEFORE
,
422 gettext("\n Warning: cert not yet valid\n"), cert
);
433 time_check(X509
*cert
)
437 i
= X509_cmp_time(X509_get_notBefore(cert
), NULL
);
439 return (CHK_TIME_BEFORE_BAD
);
441 return (CHK_TIME_IS_BEFORE
);
442 /* After 'not before' time */
444 i
= X509_cmp_time(X509_get_notAfter(cert
), NULL
);
446 return (CHK_TIME_AFTER_BAD
);
448 return (CHK_TIME_HAS_EXPIRED
);
449 return (CHK_TIME_OK
);
453 write_files(STACK_OF(X509
) *t_out
, X509
*c_out
, EVP_PKEY
*k_out
)
455 if (key_out
!= NULL
) {
457 (void) printf(gettext("%s: writing key\n"), progname
);
458 if (do_ofile(key_out
, k_out
, NULL
, NULL
) < 0)
462 if (cert_out
!= NULL
) {
464 (void) printf(gettext("%s: writing cert\n"), progname
);
465 if (do_ofile(cert_out
, NULL
, c_out
, NULL
) < 0)
469 if (trust_out
!= NULL
) {
471 (void) printf(gettext("%s: writing trust\n"),
473 if (do_ofile(trust_out
, NULL
, NULL
, t_out
) < 0)
481 get_ifile(char *name
, char *pass
, EVP_PKEY
**tmp_k
, X509
**tmp_c
,
482 STACK_OF(X509
) **tmp_t
)
489 if (stat(name
, &sbuf
) == 0 && !S_ISREG(sbuf
.st_mode
)) {
490 wbku_printerr("%s is not a regular file\n", name
);
494 if ((fp
= fopen(name
, "r")) == NULL
) {
495 wbku_printerr("cannot open input file %s", name
);
499 p12
= d2i_PKCS12_fp(fp
, NULL
);
501 wbku_printerr("cannot read file %s: %s\n", name
, cryptoerr());
507 ret
= sunw_PKCS12_parse(p12
, pass
, matchty
, k_matchval
, k_len
,
508 NULL
, tmp_k
, tmp_c
, tmp_t
);
511 wbku_printerr("cannot find matching cert and key\n");
513 wbku_printerr("cannot parse %s: %s\n", name
,
522 do_ofile(char *name
, EVP_PKEY
*pkey
, X509
*cert
, STACK_OF(X509
) *ta
)
524 STACK_OF(EVP_PKEY
) *klist
= NULL
;
525 STACK_OF(X509
) *clist
= NULL
;
531 if (stat(name
, &sbuf
) == 0 && !S_ISREG(sbuf
.st_mode
)) {
532 wbku_printerr("%s is not a regular file\n", name
);
536 if ((fp
= fopen(name
, "w")) == NULL
) {
537 wbku_printerr("cannot open output file %s", name
);
541 if ((clist
= sk_X509_new_null()) == NULL
||
542 (klist
= sk_EVP_PKEY_new_null()) == NULL
) {
543 wbku_printerr("out of memory\n");
548 if (cert
!= NULL
&& sk_X509_push(clist
, cert
) == 0) {
549 wbku_printerr("out of memory\n");
554 if (pkey
!= NULL
&& sk_EVP_PKEY_push(klist
, pkey
) == 0) {
555 wbku_printerr("out of memory\n");
560 p12
= sunw_PKCS12_create(WANBOOT_PASSPHRASE
, klist
, clist
, ta
);
562 wbku_printerr("cannot create %s: %s\n", name
, cryptoerr());
567 if (i2d_PKCS12_fp(fp
, p12
) == 0) {
568 wbku_printerr("cannot write %s: %s\n", name
, cryptoerr());
578 * Put the cert and pkey off of the stack so that they won't
579 * be freed two times. (If they get left in the stack then
580 * they will be freed with the stack.)
583 if (cert
!= NULL
&& sk_X509_num(clist
) == 1) {
584 (void) sk_X509_delete(clist
, 0);
586 sk_X509_pop_free(clist
, X509_free
);
589 if (pkey
!= NULL
&& sk_EVP_PKEY_num(klist
) == 1) {
590 (void) sk_EVP_PKEY_delete(klist
, 0);
592 sk_EVP_PKEY_pop_free(klist
, sunw_evp_pkey_free
);
601 (void) fprintf(stderr
,
603 " %s -i <file> -c <file> -k <file> -t <file> [-l <keyid> -v]\n"
606 (void) fprintf(stderr
,
608 " -i - input file to be split into component parts and put in\n"
609 " files given by -c, -k and -t\n"
610 " -c - output file for the client certificate\n"
611 " -k - output file for the client private key\n"
612 " -t - output file for the remaining certificates (assumed\n"
613 " to be trust anchors)\n"
614 "\n Files are assumed to be pkcs12-format files.\n\n"
616 " -l - value of 'localkeyid' attribute in client cert and\n"
617 " private key to be selected from the input file.\n\n"));
622 * Return a pointer to a static buffer that contains a listing of crypto
623 * errors. We presume that the user doesn't want more than 8KB of error
629 static char errbuf
[8192];
633 unsigned int nerr
= 0;
636 while ((err
= ERR_get_error_line(&pfile
, &line
)) != 0) {
638 (void) strlcat(errbuf
, "\n\t", sizeof (errbuf
));
640 if (err
== (ulong_t
)-1) {
641 (void) strlcat(errbuf
, strerror(errno
),
645 (void) strlcat(errbuf
, ERR_reason_error_string(err
),