199ebb4caSwyllys /*
299ebb4caSwyllys  * CDDL HEADER START
399ebb4caSwyllys  *
499ebb4caSwyllys  * The contents of this file are subject to the terms of the
599ebb4caSwyllys  * Common Development and Distribution License (the "License").
699ebb4caSwyllys  * You may not use this file except in compliance with the License.
799ebb4caSwyllys  *
899ebb4caSwyllys  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
999ebb4caSwyllys  * or http://www.opensolaris.org/os/licensing.
1099ebb4caSwyllys  * See the License for the specific language governing permissions
1199ebb4caSwyllys  * and limitations under the License.
1299ebb4caSwyllys  *
1399ebb4caSwyllys  * When distributing Covered Code, include this CDDL HEADER in each
1499ebb4caSwyllys  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1599ebb4caSwyllys  * If applicable, add the following below this CDDL HEADER, with the
1699ebb4caSwyllys  * fields enclosed by brackets "[]" replaced with your own identifying
1799ebb4caSwyllys  * information: Portions Copyright [yyyy] [name of copyright owner]
1899ebb4caSwyllys  *
1999ebb4caSwyllys  * CDDL HEADER END
2099ebb4caSwyllys  */
2199ebb4caSwyllys /*
22*30a5e8faSwyllys  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2399ebb4caSwyllys  * Use is subject to license terms.
2499ebb4caSwyllys  */
2599ebb4caSwyllys 
2699ebb4caSwyllys #pragma ident	"%Z%%M%	%I%	%E% SMI"
2799ebb4caSwyllys 
2899ebb4caSwyllys #include <stdio.h>
2999ebb4caSwyllys #include <stdlib.h>
3099ebb4caSwyllys #include <string.h>
3199ebb4caSwyllys #include <ctype.h>
3299ebb4caSwyllys #include <malloc.h>
3399ebb4caSwyllys #include <libgen.h>
3499ebb4caSwyllys #include <fcntl.h>
3599ebb4caSwyllys #include <errno.h>
3699ebb4caSwyllys #include <cryptoutil.h>
3799ebb4caSwyllys #include "common.h"
3899ebb4caSwyllys #include <kmfapi.h>
3999ebb4caSwyllys 
4099ebb4caSwyllys int
4199ebb4caSwyllys pk_download(int argc, char *argv[])
4299ebb4caSwyllys {
4399ebb4caSwyllys 	int rv;
4499ebb4caSwyllys 	int opt;
4599ebb4caSwyllys 	extern int	optind_av;
4699ebb4caSwyllys 	extern char	*optarg_av;
4799ebb4caSwyllys 	int oclass = 0;
4899ebb4caSwyllys 	char *url = NULL;
4999ebb4caSwyllys 	char *http_proxy = NULL;
5099ebb4caSwyllys 	char *dir = NULL;
5199ebb4caSwyllys 	char *outfile = NULL;
5299ebb4caSwyllys 	char *proxy = NULL;
5399ebb4caSwyllys 	int  proxy_port = 0;
5499ebb4caSwyllys 	KMF_HANDLE_T	kmfhandle = NULL;
5599ebb4caSwyllys 	KMF_ENCODE_FORMAT format;
5699ebb4caSwyllys 	KMF_RETURN ch_rv = KMF_OK;
5799ebb4caSwyllys 	char *fullpath = NULL;
5899ebb4caSwyllys 	KMF_DATA cert = {NULL, 0};
5999ebb4caSwyllys 	KMF_DATA cert_der = {NULL, 0};
6099ebb4caSwyllys 
6199ebb4caSwyllys 	while ((opt = getopt_av(argc, argv,
6299ebb4caSwyllys 	    "t:(objtype)u:(url)h:(http_proxy)o:(outfile)d:(dir)")) != EOF) {
6399ebb4caSwyllys 
6499ebb4caSwyllys 		if (EMPTYSTRING(optarg_av))
6599ebb4caSwyllys 			return (PK_ERR_USAGE);
6699ebb4caSwyllys 		switch (opt) {
6799ebb4caSwyllys 		case 't':
6899ebb4caSwyllys 			if (oclass)
6999ebb4caSwyllys 				return (PK_ERR_USAGE);
7099ebb4caSwyllys 			oclass = OT2Int(optarg_av);
7199ebb4caSwyllys 			if (!(oclass & (PK_CERT_OBJ | PK_CRL_OBJ)))
7299ebb4caSwyllys 				return (PK_ERR_USAGE);
7399ebb4caSwyllys 			break;
7499ebb4caSwyllys 		case 'u':
7599ebb4caSwyllys 			if (url)
7699ebb4caSwyllys 				return (PK_ERR_USAGE);
7799ebb4caSwyllys 			url = optarg_av;
7899ebb4caSwyllys 			break;
7999ebb4caSwyllys 		case 'h':
8099ebb4caSwyllys 			if (http_proxy)
8199ebb4caSwyllys 				return (PK_ERR_USAGE);
8299ebb4caSwyllys 			http_proxy = optarg_av;
8399ebb4caSwyllys 			break;
8499ebb4caSwyllys 		case 'o':
8599ebb4caSwyllys 			if (outfile)
8699ebb4caSwyllys 				return (PK_ERR_USAGE);
8799ebb4caSwyllys 			outfile = optarg_av;
8899ebb4caSwyllys 			break;
8999ebb4caSwyllys 		case 'd':
9099ebb4caSwyllys 			if (dir)
9199ebb4caSwyllys 				return (PK_ERR_USAGE);
9299ebb4caSwyllys 			dir = optarg_av;
9399ebb4caSwyllys 			break;
9499ebb4caSwyllys 		default:
9599ebb4caSwyllys 			cryptoerror(LOG_STDERR, gettext(
9699ebb4caSwyllys 			    "unrecognized download option '%s'\n"),
9799ebb4caSwyllys 			    argv[optind_av]);
9899ebb4caSwyllys 			return (PK_ERR_USAGE);
9999ebb4caSwyllys 		}
10099ebb4caSwyllys 	}
10199ebb4caSwyllys 
10299ebb4caSwyllys 	/* No additional args allowed. */
10399ebb4caSwyllys 	argc -= optind_av;
10499ebb4caSwyllys 	argv += optind_av;
10599ebb4caSwyllys 	if (argc) {
10699ebb4caSwyllys 		return (PK_ERR_USAGE);
10799ebb4caSwyllys 	}
10899ebb4caSwyllys 
10999ebb4caSwyllys 	/* Check the dir and outfile options */
11099ebb4caSwyllys 	if (outfile == NULL) {
11199ebb4caSwyllys 		/* If outfile is not specified, use the basename of URI */
11299ebb4caSwyllys 		outfile = basename(url);
11399ebb4caSwyllys 	}
11499ebb4caSwyllys 
11599ebb4caSwyllys 	fullpath = get_fullpath(dir, outfile);
11699ebb4caSwyllys 	if (fullpath == NULL) {
11799ebb4caSwyllys 		cryptoerror(LOG_STDERR, gettext("Incorrect dir or outfile "
11899ebb4caSwyllys 		    "option value \n"));
11999ebb4caSwyllys 		return (PK_ERR_USAGE);
12099ebb4caSwyllys 	}
12199ebb4caSwyllys 	/* Check if the file exists and might be overwritten. */
12299ebb4caSwyllys 	if (access(fullpath, F_OK) == 0) {
12399ebb4caSwyllys 		cryptoerror(LOG_STDERR,
124*30a5e8faSwyllys 		    gettext("Warning: file \"%s\" exists, "
125*30a5e8faSwyllys 		    "will be overwritten."), fullpath);
12699ebb4caSwyllys 		if (yesno(gettext("Continue with download? "),
12799ebb4caSwyllys 		    gettext("Respond with yes or no.\n"), B_FALSE) == B_FALSE) {
12899ebb4caSwyllys 			return (0);
12999ebb4caSwyllys 		}
13099ebb4caSwyllys 	} else {
13199ebb4caSwyllys 		rv = verify_file(fullpath);
13299ebb4caSwyllys 		if (rv != KMF_OK) {
13399ebb4caSwyllys 			cryptoerror(LOG_STDERR, gettext("The file (%s) "
134*30a5e8faSwyllys 			    "cannot be created.\n"), fullpath);
13599ebb4caSwyllys 			return (PK_ERR_USAGE);
13699ebb4caSwyllys 		}
13799ebb4caSwyllys 	}
13899ebb4caSwyllys 
13999ebb4caSwyllys 
14099ebb4caSwyllys 	/* URI MUST be specified */
14199ebb4caSwyllys 	if (url == NULL) {
14299ebb4caSwyllys 		cryptoerror(LOG_STDERR, gettext("A URL must be specified\n"));
14399ebb4caSwyllys 		rv = PK_ERR_USAGE;
14499ebb4caSwyllys 		goto end;
14599ebb4caSwyllys 	}
14699ebb4caSwyllys 
14799ebb4caSwyllys 	/*
14899ebb4caSwyllys 	 * Get the http proxy from the command "http_proxy" option or the
14999ebb4caSwyllys 	 * environment variable.  The command option has a higher priority.
15099ebb4caSwyllys 	 */
15199ebb4caSwyllys 	if (http_proxy == NULL)
15299ebb4caSwyllys 		http_proxy = getenv("http_proxy");
15399ebb4caSwyllys 
15499ebb4caSwyllys 	if (http_proxy != NULL) {
15599ebb4caSwyllys 		char *ptmp = http_proxy;
15699ebb4caSwyllys 		char *proxy_port_s;
15799ebb4caSwyllys 
15899ebb4caSwyllys 		if (strncasecmp(ptmp, "http://", 7) == 0)
15999ebb4caSwyllys 			ptmp += 7;	/* skip the scheme prefix */
16099ebb4caSwyllys 
16199ebb4caSwyllys 		proxy = strtok(ptmp, ":");
16299ebb4caSwyllys 		proxy_port_s = strtok(NULL, "\0");
16399ebb4caSwyllys 		if (proxy_port_s != NULL)
16499ebb4caSwyllys 			proxy_port = strtol(proxy_port_s, NULL, 0);
16599ebb4caSwyllys 		else
16699ebb4caSwyllys 			proxy_port = 8080;
16799ebb4caSwyllys 	}
16899ebb4caSwyllys 
16999ebb4caSwyllys 	/* If objtype is not specified, default to CRL */
17099ebb4caSwyllys 	if (oclass == 0) {
17199ebb4caSwyllys 		oclass = PK_CRL_OBJ;
17299ebb4caSwyllys 	}
17399ebb4caSwyllys 
174*30a5e8faSwyllys 	if ((rv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
17599ebb4caSwyllys 		cryptoerror(LOG_STDERR, gettext("Error initializing KMF\n"));
17699ebb4caSwyllys 		rv = PK_ERR_USAGE;
17799ebb4caSwyllys 		goto end;
17899ebb4caSwyllys 	}
17999ebb4caSwyllys 
18099ebb4caSwyllys 	/* Now we are ready to download */
18199ebb4caSwyllys 	if (oclass & PK_CRL_OBJ) {
182*30a5e8faSwyllys 		rv = kmf_download_crl(kmfhandle, url, proxy, proxy_port, 30,
18399ebb4caSwyllys 		    fullpath, &format);
18499ebb4caSwyllys 	} else if (oclass & PK_CERT_OBJ) {
185*30a5e8faSwyllys 		rv = kmf_download_cert(kmfhandle, url, proxy, proxy_port, 30,
18699ebb4caSwyllys 		    fullpath, &format);
18799ebb4caSwyllys 	}
18899ebb4caSwyllys 
18999ebb4caSwyllys 	if (rv != KMF_OK) {
19099ebb4caSwyllys 		switch (rv) {
19199ebb4caSwyllys 		case KMF_ERR_BAD_URI:
19299ebb4caSwyllys 			cryptoerror(LOG_STDERR,
19399ebb4caSwyllys 			    gettext("Error in parsing URI\n"));
19499ebb4caSwyllys 			rv = PK_ERR_USAGE;
19599ebb4caSwyllys 			break;
19699ebb4caSwyllys 		case KMF_ERR_OPEN_FILE:
19799ebb4caSwyllys 			cryptoerror(LOG_STDERR,
19899ebb4caSwyllys 			    gettext("Error in opening file\n"));
19999ebb4caSwyllys 			rv = PK_ERR_USAGE;
20099ebb4caSwyllys 			break;
20199ebb4caSwyllys 		case KMF_ERR_WRITE_FILE:
20299ebb4caSwyllys 			cryptoerror(LOG_STDERR,
20399ebb4caSwyllys 			    gettext("Error in writing file\n"));
20499ebb4caSwyllys 			rv = PK_ERR_USAGE;
20599ebb4caSwyllys 			break;
20699ebb4caSwyllys 		case KMF_ERR_BAD_CRLFILE:
20799ebb4caSwyllys 			cryptoerror(LOG_STDERR, gettext("Not a CRL file\n"));
20899ebb4caSwyllys 			rv = PK_ERR_USAGE;
20999ebb4caSwyllys 			break;
21099ebb4caSwyllys 		case KMF_ERR_BAD_CERTFILE:
21199ebb4caSwyllys 			cryptoerror(LOG_STDERR,
21299ebb4caSwyllys 			    gettext("Not a certificate file\n"));
21399ebb4caSwyllys 			rv = PK_ERR_USAGE;
21499ebb4caSwyllys 			break;
21599ebb4caSwyllys 		case KMF_ERR_MEMORY:
21699ebb4caSwyllys 			cryptoerror(LOG_STDERR,
21799ebb4caSwyllys 			    gettext("Not enough memory\n"));
21899ebb4caSwyllys 			rv = PK_ERR_SYSTEM;
21999ebb4caSwyllys 			break;
22099ebb4caSwyllys 		default:
22199ebb4caSwyllys 			cryptoerror(LOG_STDERR,
22299ebb4caSwyllys 			    gettext("Error in downloading the file.\n"));
22399ebb4caSwyllys 			rv = PK_ERR_SYSTEM;
22499ebb4caSwyllys 			break;
22599ebb4caSwyllys 		}
22699ebb4caSwyllys 		goto end;
22799ebb4caSwyllys 	}
22899ebb4caSwyllys 
22999ebb4caSwyllys 	/*
23099ebb4caSwyllys 	 * If the file is successfully downloaded, we also check the date.
23199ebb4caSwyllys 	 * If the downloaded file is outdated, give a warning.
23299ebb4caSwyllys 	 */
23399ebb4caSwyllys 	if (oclass & PK_CRL_OBJ) {
234*30a5e8faSwyllys 		ch_rv = kmf_check_crl_date(kmfhandle, fullpath);
23599ebb4caSwyllys 	} else { /* certificate */
236*30a5e8faSwyllys 		ch_rv = kmf_read_input_file(kmfhandle, fullpath, &cert);
23799ebb4caSwyllys 		if (ch_rv != KMF_OK)
23899ebb4caSwyllys 			goto end;
23999ebb4caSwyllys 
24099ebb4caSwyllys 		if (format == KMF_FORMAT_PEM) {
24199ebb4caSwyllys 			int len;
242*30a5e8faSwyllys 			ch_rv = kmf_pem_to_der(cert.Data, cert.Length,
24399ebb4caSwyllys 			    &cert_der.Data, &len);
24499ebb4caSwyllys 			if (ch_rv != KMF_OK)
24599ebb4caSwyllys 				goto end;
24699ebb4caSwyllys 			cert_der.Length = (size_t)len;
24799ebb4caSwyllys 		}
24899ebb4caSwyllys 
249*30a5e8faSwyllys 		ch_rv = kmf_check_cert_date(kmfhandle,
25099ebb4caSwyllys 		    format == KMF_FORMAT_ASN1 ? &cert : &cert_der);
25199ebb4caSwyllys 	}
25299ebb4caSwyllys 
25399ebb4caSwyllys end:
25499ebb4caSwyllys 	if (ch_rv == KMF_ERR_VALIDITY_PERIOD) {
25599ebb4caSwyllys 		cryptoerror(LOG_STDERR,
25699ebb4caSwyllys 		    gettext("Warning: the downloaded file is expired.\n"));
25799ebb4caSwyllys 	} else if (ch_rv != KMF_OK) {
25899ebb4caSwyllys 		cryptoerror(LOG_STDERR,
25999ebb4caSwyllys 		    gettext("Warning: failed to check the validity.\n"));
26099ebb4caSwyllys 	}
26199ebb4caSwyllys 
26299ebb4caSwyllys 	if (fullpath)
26399ebb4caSwyllys 		free(fullpath);
26499ebb4caSwyllys 
265*30a5e8faSwyllys 	kmf_free_data(&cert);
266*30a5e8faSwyllys 	kmf_free_data(&cert_der);
26799ebb4caSwyllys 
268*30a5e8faSwyllys 	(void) kmf_finalize(kmfhandle);
26999ebb4caSwyllys 	return (rv);
27099ebb4caSwyllys }
271