1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 *
21 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <ctype.h>
28#include <malloc.h>
29#include <libgen.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <cryptoutil.h>
33#include "common.h"
34#include <kmfapi.h>
35
36int
37pk_download(int argc, char *argv[])
38{
39	int rv;
40	int opt;
41	extern int	optind_av;
42	extern char	*optarg_av;
43	int oclass = 0;
44	char *url = NULL;
45	char *http_proxy = NULL;
46	char *dir = NULL;
47	char *outfile = NULL;
48	char *proxy = NULL;
49	int  proxy_port = 0;
50	KMF_HANDLE_T	kmfhandle = NULL;
51	KMF_ENCODE_FORMAT format;
52	KMF_RETURN ch_rv = KMF_OK;
53	char *fullpath = NULL;
54	KMF_DATA cert = {NULL, 0};
55	KMF_DATA cert_der = {NULL, 0};
56
57	while ((opt = getopt_av(argc, argv,
58	    "t:(objtype)u:(url)h:(http_proxy)o:(outfile)d:(dir)")) != EOF) {
59
60		if (EMPTYSTRING(optarg_av))
61			return (PK_ERR_USAGE);
62		switch (opt) {
63		case 't':
64			if (oclass)
65				return (PK_ERR_USAGE);
66			oclass = OT2Int(optarg_av);
67			if (!(oclass & (PK_CERT_OBJ | PK_CRL_OBJ)))
68				return (PK_ERR_USAGE);
69			break;
70		case 'u':
71			if (url)
72				return (PK_ERR_USAGE);
73			url = optarg_av;
74			break;
75		case 'h':
76			if (http_proxy)
77				return (PK_ERR_USAGE);
78			http_proxy = optarg_av;
79			break;
80		case 'o':
81			if (outfile)
82				return (PK_ERR_USAGE);
83			outfile = optarg_av;
84			break;
85		case 'd':
86			if (dir)
87				return (PK_ERR_USAGE);
88			dir = optarg_av;
89			break;
90		default:
91			cryptoerror(LOG_STDERR, gettext(
92			    "unrecognized download option '%s'\n"),
93			    argv[optind_av]);
94			return (PK_ERR_USAGE);
95		}
96	}
97
98	/* No additional args allowed. */
99	argc -= optind_av;
100	argv += optind_av;
101	if (argc) {
102		return (PK_ERR_USAGE);
103	}
104
105	/* Check the dir and outfile options */
106	if (outfile == NULL) {
107		/* If outfile is not specified, use the basename of URI */
108		outfile = basename(url);
109	}
110
111	fullpath = get_fullpath(dir, outfile);
112	if (fullpath == NULL) {
113		cryptoerror(LOG_STDERR, gettext("Incorrect dir or outfile "
114		    "option value \n"));
115		return (PK_ERR_USAGE);
116	}
117	/* Check if the file exists and might be overwritten. */
118	if (verify_file(fullpath) != KMF_OK) {
119		cryptoerror(LOG_STDERR,
120		    gettext("Warning: file \"%s\" exists, "
121		    "will be overwritten."), fullpath);
122		if (yesno(gettext("Continue with download? "),
123		    gettext("Respond with yes or no.\n"), B_FALSE) == B_FALSE) {
124			return (0);
125		}
126	}
127	/* URI MUST be specified */
128	if (url == NULL) {
129		cryptoerror(LOG_STDERR, gettext("A URL must be specified\n"));
130		rv = PK_ERR_USAGE;
131		goto end;
132	}
133
134	/*
135	 * Get the http proxy from the command "http_proxy" option or the
136	 * environment variable.  The command option has a higher priority.
137	 */
138	if (http_proxy == NULL)
139		http_proxy = getenv("http_proxy");
140
141	if (http_proxy != NULL) {
142		char *ptmp = http_proxy;
143		char *proxy_port_s;
144
145		if (strncasecmp(ptmp, "http://", 7) == 0)
146			ptmp += 7;	/* skip the scheme prefix */
147
148		proxy = strtok(ptmp, ":");
149		proxy_port_s = strtok(NULL, "\0");
150		if (proxy_port_s != NULL)
151			proxy_port = strtol(proxy_port_s, NULL, 0);
152		else
153			proxy_port = 8080;
154	}
155
156	/* If objtype is not specified, default to CRL */
157	if (oclass == 0) {
158		oclass = PK_CRL_OBJ;
159	}
160
161	if ((rv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
162		cryptoerror(LOG_STDERR, gettext("Error initializing KMF\n"));
163		rv = PK_ERR_USAGE;
164		goto end;
165	}
166
167	/* Now we are ready to download */
168	if (oclass & PK_CRL_OBJ) {
169		rv = kmf_download_crl(kmfhandle, url, proxy, proxy_port, 30,
170		    fullpath, &format);
171	} else if (oclass & PK_CERT_OBJ) {
172		rv = kmf_download_cert(kmfhandle, url, proxy, proxy_port, 30,
173		    fullpath, &format);
174	}
175
176	if (rv != KMF_OK) {
177		switch (rv) {
178		case KMF_ERR_BAD_URI:
179			cryptoerror(LOG_STDERR,
180			    gettext("Error in parsing URI\n"));
181			rv = PK_ERR_USAGE;
182			break;
183		case KMF_ERR_OPEN_FILE:
184			cryptoerror(LOG_STDERR,
185			    gettext("Error in opening file\n"));
186			rv = PK_ERR_USAGE;
187			break;
188		case KMF_ERR_WRITE_FILE:
189			cryptoerror(LOG_STDERR,
190			    gettext("Error in writing file\n"));
191			rv = PK_ERR_USAGE;
192			break;
193		case KMF_ERR_BAD_CRLFILE:
194			cryptoerror(LOG_STDERR, gettext("Not a CRL file\n"));
195			rv = PK_ERR_USAGE;
196			break;
197		case KMF_ERR_BAD_CERTFILE:
198			cryptoerror(LOG_STDERR,
199			    gettext("Not a certificate file\n"));
200			rv = PK_ERR_USAGE;
201			break;
202		case KMF_ERR_MEMORY:
203			cryptoerror(LOG_STDERR,
204			    gettext("Not enough memory\n"));
205			rv = PK_ERR_SYSTEM;
206			break;
207		default:
208			cryptoerror(LOG_STDERR,
209			    gettext("Error in downloading the file.\n"));
210			rv = PK_ERR_SYSTEM;
211			break;
212		}
213		goto end;
214	}
215
216	/*
217	 * If the file is successfully downloaded, we also check the date.
218	 * If the downloaded file is outdated, give a warning.
219	 */
220	if (oclass & PK_CRL_OBJ) {
221		ch_rv = kmf_check_crl_date(kmfhandle, fullpath);
222	} else { /* certificate */
223		ch_rv = kmf_read_input_file(kmfhandle, fullpath, &cert);
224		if (ch_rv != KMF_OK)
225			goto end;
226
227		if (format == KMF_FORMAT_PEM) {
228			int len;
229			ch_rv = kmf_pem_to_der(cert.Data, cert.Length,
230			    &cert_der.Data, &len);
231			if (ch_rv != KMF_OK)
232				goto end;
233			cert_der.Length = (size_t)len;
234		}
235
236		ch_rv = kmf_check_cert_date(kmfhandle,
237		    format == KMF_FORMAT_ASN1 ? &cert : &cert_der);
238	}
239
240end:
241	if (ch_rv == KMF_ERR_VALIDITY_PERIOD) {
242		cryptoerror(LOG_STDERR,
243		    gettext("Warning: the downloaded file is expired.\n"));
244	} else if (ch_rv != KMF_OK) {
245		cryptoerror(LOG_STDERR,
246		    gettext("Warning: failed to check the validity.\n"));
247	}
248
249	if (fullpath)
250		free(fullpath);
251
252	kmf_free_data(&cert);
253	kmf_free_data(&cert_der);
254
255	(void) kmf_finalize(kmfhandle);
256	return (rv);
257}
258