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
202c9a247fSWyllys Ingersoll  *
212c9a247fSWyllys Ingersoll  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2299ebb4caSwyllys  */
2399ebb4caSwyllys 
2499ebb4caSwyllys #include <stdio.h>
2599ebb4caSwyllys #include <stdlib.h>
2699ebb4caSwyllys #include <string.h>
2799ebb4caSwyllys #include <ctype.h>
2899ebb4caSwyllys #include <malloc.h>
2999ebb4caSwyllys #include <libgen.h>
3099ebb4caSwyllys #include <fcntl.h>
3199ebb4caSwyllys #include <errno.h>
3299ebb4caSwyllys #include <cryptoutil.h>
3399ebb4caSwyllys #include "common.h"
3499ebb4caSwyllys #include <kmfapi.h>
3599ebb4caSwyllys 
3699ebb4caSwyllys int
pk_download(int argc,char * argv[])3799ebb4caSwyllys pk_download(int argc, char *argv[])
3899ebb4caSwyllys {
3999ebb4caSwyllys 	int rv;
4099ebb4caSwyllys 	int opt;
4199ebb4caSwyllys 	extern int	optind_av;
4299ebb4caSwyllys 	extern char	*optarg_av;
4399ebb4caSwyllys 	int oclass = 0;
4499ebb4caSwyllys 	char *url = NULL;
4599ebb4caSwyllys 	char *http_proxy = NULL;
4699ebb4caSwyllys 	char *dir = NULL;
4799ebb4caSwyllys 	char *outfile = NULL;
4899ebb4caSwyllys 	char *proxy = NULL;
4999ebb4caSwyllys 	int  proxy_port = 0;
5099ebb4caSwyllys 	KMF_HANDLE_T	kmfhandle = NULL;
5199ebb4caSwyllys 	KMF_ENCODE_FORMAT format;
5299ebb4caSwyllys 	KMF_RETURN ch_rv = KMF_OK;
5399ebb4caSwyllys 	char *fullpath = NULL;
54*6b35cb3cSRichard PALO 	KMF_DATA cert = { 0, NULL };
55*6b35cb3cSRichard PALO 	KMF_DATA cert_der = { 0, NULL };
5699ebb4caSwyllys 
5799ebb4caSwyllys 	while ((opt = getopt_av(argc, argv,
5899ebb4caSwyllys 	    "t:(objtype)u:(url)h:(http_proxy)o:(outfile)d:(dir)")) != EOF) {
5999ebb4caSwyllys 
6099ebb4caSwyllys 		if (EMPTYSTRING(optarg_av))
6199ebb4caSwyllys 			return (PK_ERR_USAGE);
6299ebb4caSwyllys 		switch (opt) {
6399ebb4caSwyllys 		case 't':
6499ebb4caSwyllys 			if (oclass)
6599ebb4caSwyllys 				return (PK_ERR_USAGE);
6699ebb4caSwyllys 			oclass = OT2Int(optarg_av);
6799ebb4caSwyllys 			if (!(oclass & (PK_CERT_OBJ | PK_CRL_OBJ)))
6899ebb4caSwyllys 				return (PK_ERR_USAGE);
6999ebb4caSwyllys 			break;
7099ebb4caSwyllys 		case 'u':
7199ebb4caSwyllys 			if (url)
7299ebb4caSwyllys 				return (PK_ERR_USAGE);
7399ebb4caSwyllys 			url = optarg_av;
7499ebb4caSwyllys 			break;
7599ebb4caSwyllys 		case 'h':
7699ebb4caSwyllys 			if (http_proxy)
7799ebb4caSwyllys 				return (PK_ERR_USAGE);
7899ebb4caSwyllys 			http_proxy = optarg_av;
7999ebb4caSwyllys 			break;
8099ebb4caSwyllys 		case 'o':
8199ebb4caSwyllys 			if (outfile)
8299ebb4caSwyllys 				return (PK_ERR_USAGE);
8399ebb4caSwyllys 			outfile = optarg_av;
8499ebb4caSwyllys 			break;
8599ebb4caSwyllys 		case 'd':
8699ebb4caSwyllys 			if (dir)
8799ebb4caSwyllys 				return (PK_ERR_USAGE);
8899ebb4caSwyllys 			dir = optarg_av;
8999ebb4caSwyllys 			break;
9099ebb4caSwyllys 		default:
9199ebb4caSwyllys 			cryptoerror(LOG_STDERR, gettext(
9299ebb4caSwyllys 			    "unrecognized download option '%s'\n"),
9399ebb4caSwyllys 			    argv[optind_av]);
9499ebb4caSwyllys 			return (PK_ERR_USAGE);
9599ebb4caSwyllys 		}
9699ebb4caSwyllys 	}
9799ebb4caSwyllys 
9899ebb4caSwyllys 	/* No additional args allowed. */
9999ebb4caSwyllys 	argc -= optind_av;
10099ebb4caSwyllys 	argv += optind_av;
10199ebb4caSwyllys 	if (argc) {
10299ebb4caSwyllys 		return (PK_ERR_USAGE);
10399ebb4caSwyllys 	}
10499ebb4caSwyllys 
10599ebb4caSwyllys 	/* Check the dir and outfile options */
10699ebb4caSwyllys 	if (outfile == NULL) {
10799ebb4caSwyllys 		/* If outfile is not specified, use the basename of URI */
10899ebb4caSwyllys 		outfile = basename(url);
10999ebb4caSwyllys 	}
11099ebb4caSwyllys 
11199ebb4caSwyllys 	fullpath = get_fullpath(dir, outfile);
11299ebb4caSwyllys 	if (fullpath == NULL) {
11399ebb4caSwyllys 		cryptoerror(LOG_STDERR, gettext("Incorrect dir or outfile "
11499ebb4caSwyllys 		    "option value \n"));
11599ebb4caSwyllys 		return (PK_ERR_USAGE);
11699ebb4caSwyllys 	}
11799ebb4caSwyllys 	/* Check if the file exists and might be overwritten. */
1182c9a247fSWyllys Ingersoll 	if (verify_file(fullpath) != KMF_OK) {
11999ebb4caSwyllys 		cryptoerror(LOG_STDERR,
12030a5e8faSwyllys 		    gettext("Warning: file \"%s\" exists, "
12130a5e8faSwyllys 		    "will be overwritten."), fullpath);
12299ebb4caSwyllys 		if (yesno(gettext("Continue with download? "),
12399ebb4caSwyllys 		    gettext("Respond with yes or no.\n"), B_FALSE) == B_FALSE) {
12499ebb4caSwyllys 			return (0);
12599ebb4caSwyllys 		}
12699ebb4caSwyllys 	}
12799ebb4caSwyllys 	/* URI MUST be specified */
12899ebb4caSwyllys 	if (url == NULL) {
12999ebb4caSwyllys 		cryptoerror(LOG_STDERR, gettext("A URL must be specified\n"));
13099ebb4caSwyllys 		rv = PK_ERR_USAGE;
13199ebb4caSwyllys 		goto end;
13299ebb4caSwyllys 	}
13399ebb4caSwyllys 
13499ebb4caSwyllys 	/*
13599ebb4caSwyllys 	 * Get the http proxy from the command "http_proxy" option or the
13699ebb4caSwyllys 	 * environment variable.  The command option has a higher priority.
13799ebb4caSwyllys 	 */
13899ebb4caSwyllys 	if (http_proxy == NULL)
13999ebb4caSwyllys 		http_proxy = getenv("http_proxy");
14099ebb4caSwyllys 
14199ebb4caSwyllys 	if (http_proxy != NULL) {
14299ebb4caSwyllys 		char *ptmp = http_proxy;
14399ebb4caSwyllys 		char *proxy_port_s;
14499ebb4caSwyllys 
14599ebb4caSwyllys 		if (strncasecmp(ptmp, "http://", 7) == 0)
14699ebb4caSwyllys 			ptmp += 7;	/* skip the scheme prefix */
14799ebb4caSwyllys 
14899ebb4caSwyllys 		proxy = strtok(ptmp, ":");
14999ebb4caSwyllys 		proxy_port_s = strtok(NULL, "\0");
15099ebb4caSwyllys 		if (proxy_port_s != NULL)
15199ebb4caSwyllys 			proxy_port = strtol(proxy_port_s, NULL, 0);
15299ebb4caSwyllys 		else
15399ebb4caSwyllys 			proxy_port = 8080;
15499ebb4caSwyllys 	}
15599ebb4caSwyllys 
15699ebb4caSwyllys 	/* If objtype is not specified, default to CRL */
15799ebb4caSwyllys 	if (oclass == 0) {
15899ebb4caSwyllys 		oclass = PK_CRL_OBJ;
15999ebb4caSwyllys 	}
16099ebb4caSwyllys 
16130a5e8faSwyllys 	if ((rv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
16299ebb4caSwyllys 		cryptoerror(LOG_STDERR, gettext("Error initializing KMF\n"));
16399ebb4caSwyllys 		rv = PK_ERR_USAGE;
16499ebb4caSwyllys 		goto end;
16599ebb4caSwyllys 	}
16699ebb4caSwyllys 
16799ebb4caSwyllys 	/* Now we are ready to download */
16899ebb4caSwyllys 	if (oclass & PK_CRL_OBJ) {
16930a5e8faSwyllys 		rv = kmf_download_crl(kmfhandle, url, proxy, proxy_port, 30,
17099ebb4caSwyllys 		    fullpath, &format);
17199ebb4caSwyllys 	} else if (oclass & PK_CERT_OBJ) {
17230a5e8faSwyllys 		rv = kmf_download_cert(kmfhandle, url, proxy, proxy_port, 30,
17399ebb4caSwyllys 		    fullpath, &format);
17499ebb4caSwyllys 	}
17599ebb4caSwyllys 
17699ebb4caSwyllys 	if (rv != KMF_OK) {
17799ebb4caSwyllys 		switch (rv) {
17899ebb4caSwyllys 		case KMF_ERR_BAD_URI:
17999ebb4caSwyllys 			cryptoerror(LOG_STDERR,
18099ebb4caSwyllys 			    gettext("Error in parsing URI\n"));
18199ebb4caSwyllys 			rv = PK_ERR_USAGE;
18299ebb4caSwyllys 			break;
18399ebb4caSwyllys 		case KMF_ERR_OPEN_FILE:
18499ebb4caSwyllys 			cryptoerror(LOG_STDERR,
18599ebb4caSwyllys 			    gettext("Error in opening file\n"));
18699ebb4caSwyllys 			rv = PK_ERR_USAGE;
18799ebb4caSwyllys 			break;
18899ebb4caSwyllys 		case KMF_ERR_WRITE_FILE:
18999ebb4caSwyllys 			cryptoerror(LOG_STDERR,
19099ebb4caSwyllys 			    gettext("Error in writing file\n"));
19199ebb4caSwyllys 			rv = PK_ERR_USAGE;
19299ebb4caSwyllys 			break;
19399ebb4caSwyllys 		case KMF_ERR_BAD_CRLFILE:
19499ebb4caSwyllys 			cryptoerror(LOG_STDERR, gettext("Not a CRL file\n"));
19599ebb4caSwyllys 			rv = PK_ERR_USAGE;
19699ebb4caSwyllys 			break;
19799ebb4caSwyllys 		case KMF_ERR_BAD_CERTFILE:
19899ebb4caSwyllys 			cryptoerror(LOG_STDERR,
19999ebb4caSwyllys 			    gettext("Not a certificate file\n"));
20099ebb4caSwyllys 			rv = PK_ERR_USAGE;
20199ebb4caSwyllys 			break;
20299ebb4caSwyllys 		case KMF_ERR_MEMORY:
20399ebb4caSwyllys 			cryptoerror(LOG_STDERR,
20499ebb4caSwyllys 			    gettext("Not enough memory\n"));
20599ebb4caSwyllys 			rv = PK_ERR_SYSTEM;
20699ebb4caSwyllys 			break;
20799ebb4caSwyllys 		default:
20899ebb4caSwyllys 			cryptoerror(LOG_STDERR,
20999ebb4caSwyllys 			    gettext("Error in downloading the file.\n"));
21099ebb4caSwyllys 			rv = PK_ERR_SYSTEM;
21199ebb4caSwyllys 			break;
21299ebb4caSwyllys 		}
21399ebb4caSwyllys 		goto end;
21499ebb4caSwyllys 	}
21599ebb4caSwyllys 
21699ebb4caSwyllys 	/*
21799ebb4caSwyllys 	 * If the file is successfully downloaded, we also check the date.
21899ebb4caSwyllys 	 * If the downloaded file is outdated, give a warning.
21999ebb4caSwyllys 	 */
22099ebb4caSwyllys 	if (oclass & PK_CRL_OBJ) {
22130a5e8faSwyllys 		ch_rv = kmf_check_crl_date(kmfhandle, fullpath);
22299ebb4caSwyllys 	} else { /* certificate */
22330a5e8faSwyllys 		ch_rv = kmf_read_input_file(kmfhandle, fullpath, &cert);
22499ebb4caSwyllys 		if (ch_rv != KMF_OK)
22599ebb4caSwyllys 			goto end;
22699ebb4caSwyllys 
22799ebb4caSwyllys 		if (format == KMF_FORMAT_PEM) {
22899ebb4caSwyllys 			int len;
22930a5e8faSwyllys 			ch_rv = kmf_pem_to_der(cert.Data, cert.Length,
23099ebb4caSwyllys 			    &cert_der.Data, &len);
23199ebb4caSwyllys 			if (ch_rv != KMF_OK)
23299ebb4caSwyllys 				goto end;
23399ebb4caSwyllys 			cert_der.Length = (size_t)len;
23499ebb4caSwyllys 		}
23599ebb4caSwyllys 
23630a5e8faSwyllys 		ch_rv = kmf_check_cert_date(kmfhandle,
23799ebb4caSwyllys 		    format == KMF_FORMAT_ASN1 ? &cert : &cert_der);
23899ebb4caSwyllys 	}
23999ebb4caSwyllys 
24099ebb4caSwyllys end:
24199ebb4caSwyllys 	if (ch_rv == KMF_ERR_VALIDITY_PERIOD) {
24299ebb4caSwyllys 		cryptoerror(LOG_STDERR,
24399ebb4caSwyllys 		    gettext("Warning: the downloaded file is expired.\n"));
24499ebb4caSwyllys 	} else if (ch_rv != KMF_OK) {
24599ebb4caSwyllys 		cryptoerror(LOG_STDERR,
24699ebb4caSwyllys 		    gettext("Warning: failed to check the validity.\n"));
24799ebb4caSwyllys 	}
24899ebb4caSwyllys 
24999ebb4caSwyllys 	if (fullpath)
25099ebb4caSwyllys 		free(fullpath);
25199ebb4caSwyllys 
25230a5e8faSwyllys 	kmf_free_data(&cert);
25330a5e8faSwyllys 	kmf_free_data(&cert_der);
25499ebb4caSwyllys 
25530a5e8faSwyllys 	(void) kmf_finalize(kmfhandle);
25699ebb4caSwyllys 	return (rv);
25799ebb4caSwyllys }
258