xref: /illumos-gate/usr/src/cmd/cmd-crypto/pktool/download.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <malloc.h>
33 #include <libgen.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <cryptoutil.h>
37 #include "common.h"
38 #include <kmfapi.h>
39 
40 int
41 pk_download(int argc, char *argv[])
42 {
43 	int rv;
44 	int opt;
45 	extern int	optind_av;
46 	extern char	*optarg_av;
47 	int oclass = 0;
48 	char *url = NULL;
49 	char *http_proxy = NULL;
50 	char *dir = NULL;
51 	char *outfile = NULL;
52 	char *proxy = NULL;
53 	int  proxy_port = 0;
54 	KMF_HANDLE_T	kmfhandle = NULL;
55 	KMF_ENCODE_FORMAT format;
56 	KMF_RETURN ch_rv = KMF_OK;
57 	char *fullpath = NULL;
58 	KMF_DATA cert = {NULL, 0};
59 	KMF_DATA cert_der = {NULL, 0};
60 
61 	while ((opt = getopt_av(argc, argv,
62 	    "t:(objtype)u:(url)h:(http_proxy)o:(outfile)d:(dir)")) != EOF) {
63 
64 		if (EMPTYSTRING(optarg_av))
65 			return (PK_ERR_USAGE);
66 		switch (opt) {
67 		case 't':
68 			if (oclass)
69 				return (PK_ERR_USAGE);
70 			oclass = OT2Int(optarg_av);
71 			if (!(oclass & (PK_CERT_OBJ | PK_CRL_OBJ)))
72 				return (PK_ERR_USAGE);
73 			break;
74 		case 'u':
75 			if (url)
76 				return (PK_ERR_USAGE);
77 			url = optarg_av;
78 			break;
79 		case 'h':
80 			if (http_proxy)
81 				return (PK_ERR_USAGE);
82 			http_proxy = optarg_av;
83 			break;
84 		case 'o':
85 			if (outfile)
86 				return (PK_ERR_USAGE);
87 			outfile = optarg_av;
88 			break;
89 		case 'd':
90 			if (dir)
91 				return (PK_ERR_USAGE);
92 			dir = optarg_av;
93 			break;
94 		default:
95 			cryptoerror(LOG_STDERR, gettext(
96 			    "unrecognized download option '%s'\n"),
97 			    argv[optind_av]);
98 			return (PK_ERR_USAGE);
99 		}
100 	}
101 
102 	/* No additional args allowed. */
103 	argc -= optind_av;
104 	argv += optind_av;
105 	if (argc) {
106 		return (PK_ERR_USAGE);
107 	}
108 
109 	/* Check the dir and outfile options */
110 	if (outfile == NULL) {
111 		/* If outfile is not specified, use the basename of URI */
112 		outfile = basename(url);
113 	}
114 
115 	fullpath = get_fullpath(dir, outfile);
116 	if (fullpath == NULL) {
117 		cryptoerror(LOG_STDERR, gettext("Incorrect dir or outfile "
118 		    "option value \n"));
119 		return (PK_ERR_USAGE);
120 	}
121 	/* Check if the file exists and might be overwritten. */
122 	if (access(fullpath, F_OK) == 0) {
123 		cryptoerror(LOG_STDERR,
124 		    gettext("Warning: file \"%s\" exists, "
125 		    "will be overwritten."), fullpath);
126 		if (yesno(gettext("Continue with download? "),
127 		    gettext("Respond with yes or no.\n"), B_FALSE) == B_FALSE) {
128 			return (0);
129 		}
130 	} else {
131 		rv = verify_file(fullpath);
132 		if (rv != KMF_OK) {
133 			cryptoerror(LOG_STDERR, gettext("The file (%s) "
134 			    "cannot be created.\n"), fullpath);
135 			return (PK_ERR_USAGE);
136 		}
137 	}
138 
139 
140 	/* URI MUST be specified */
141 	if (url == NULL) {
142 		cryptoerror(LOG_STDERR, gettext("A URL must be specified\n"));
143 		rv = PK_ERR_USAGE;
144 		goto end;
145 	}
146 
147 	/*
148 	 * Get the http proxy from the command "http_proxy" option or the
149 	 * environment variable.  The command option has a higher priority.
150 	 */
151 	if (http_proxy == NULL)
152 		http_proxy = getenv("http_proxy");
153 
154 	if (http_proxy != NULL) {
155 		char *ptmp = http_proxy;
156 		char *proxy_port_s;
157 
158 		if (strncasecmp(ptmp, "http://", 7) == 0)
159 			ptmp += 7;	/* skip the scheme prefix */
160 
161 		proxy = strtok(ptmp, ":");
162 		proxy_port_s = strtok(NULL, "\0");
163 		if (proxy_port_s != NULL)
164 			proxy_port = strtol(proxy_port_s, NULL, 0);
165 		else
166 			proxy_port = 8080;
167 	}
168 
169 	/* If objtype is not specified, default to CRL */
170 	if (oclass == 0) {
171 		oclass = PK_CRL_OBJ;
172 	}
173 
174 	if ((rv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
175 		cryptoerror(LOG_STDERR, gettext("Error initializing KMF\n"));
176 		rv = PK_ERR_USAGE;
177 		goto end;
178 	}
179 
180 	/* Now we are ready to download */
181 	if (oclass & PK_CRL_OBJ) {
182 		rv = kmf_download_crl(kmfhandle, url, proxy, proxy_port, 30,
183 		    fullpath, &format);
184 	} else if (oclass & PK_CERT_OBJ) {
185 		rv = kmf_download_cert(kmfhandle, url, proxy, proxy_port, 30,
186 		    fullpath, &format);
187 	}
188 
189 	if (rv != KMF_OK) {
190 		switch (rv) {
191 		case KMF_ERR_BAD_URI:
192 			cryptoerror(LOG_STDERR,
193 			    gettext("Error in parsing URI\n"));
194 			rv = PK_ERR_USAGE;
195 			break;
196 		case KMF_ERR_OPEN_FILE:
197 			cryptoerror(LOG_STDERR,
198 			    gettext("Error in opening file\n"));
199 			rv = PK_ERR_USAGE;
200 			break;
201 		case KMF_ERR_WRITE_FILE:
202 			cryptoerror(LOG_STDERR,
203 			    gettext("Error in writing file\n"));
204 			rv = PK_ERR_USAGE;
205 			break;
206 		case KMF_ERR_BAD_CRLFILE:
207 			cryptoerror(LOG_STDERR, gettext("Not a CRL file\n"));
208 			rv = PK_ERR_USAGE;
209 			break;
210 		case KMF_ERR_BAD_CERTFILE:
211 			cryptoerror(LOG_STDERR,
212 			    gettext("Not a certificate file\n"));
213 			rv = PK_ERR_USAGE;
214 			break;
215 		case KMF_ERR_MEMORY:
216 			cryptoerror(LOG_STDERR,
217 			    gettext("Not enough memory\n"));
218 			rv = PK_ERR_SYSTEM;
219 			break;
220 		default:
221 			cryptoerror(LOG_STDERR,
222 			    gettext("Error in downloading the file.\n"));
223 			rv = PK_ERR_SYSTEM;
224 			break;
225 		}
226 		goto end;
227 	}
228 
229 	/*
230 	 * If the file is successfully downloaded, we also check the date.
231 	 * If the downloaded file is outdated, give a warning.
232 	 */
233 	if (oclass & PK_CRL_OBJ) {
234 		ch_rv = kmf_check_crl_date(kmfhandle, fullpath);
235 	} else { /* certificate */
236 		ch_rv = kmf_read_input_file(kmfhandle, fullpath, &cert);
237 		if (ch_rv != KMF_OK)
238 			goto end;
239 
240 		if (format == KMF_FORMAT_PEM) {
241 			int len;
242 			ch_rv = kmf_pem_to_der(cert.Data, cert.Length,
243 			    &cert_der.Data, &len);
244 			if (ch_rv != KMF_OK)
245 				goto end;
246 			cert_der.Length = (size_t)len;
247 		}
248 
249 		ch_rv = kmf_check_cert_date(kmfhandle,
250 		    format == KMF_FORMAT_ASN1 ? &cert : &cert_der);
251 	}
252 
253 end:
254 	if (ch_rv == KMF_ERR_VALIDITY_PERIOD) {
255 		cryptoerror(LOG_STDERR,
256 		    gettext("Warning: the downloaded file is expired.\n"));
257 	} else if (ch_rv != KMF_OK) {
258 		cryptoerror(LOG_STDERR,
259 		    gettext("Warning: failed to check the validity.\n"));
260 	}
261 
262 	if (fullpath)
263 		free(fullpath);
264 
265 	kmf_free_data(&cert);
266 	kmf_free_data(&cert_der);
267 
268 	(void) kmf_finalize(kmfhandle);
269 	return (rv);
270 }
271