xref: /illumos-gate/usr/src/cmd/cmd-crypto/pktool/download.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 
36 int
37 pk_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 = { 0, NULL };
55 	KMF_DATA cert_der = { 0, NULL };
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 
240 end:
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