xref: /titanic_52/usr/src/lib/libpkg/common/pkgweb.c (revision 5c51f1241dbbdf2656d0e10011981411ed0c9673)
1*5c51f124SMoriah Waterland /*
2*5c51f124SMoriah Waterland  * CDDL HEADER START
3*5c51f124SMoriah Waterland  *
4*5c51f124SMoriah Waterland  * The contents of this file are subject to the terms of the
5*5c51f124SMoriah Waterland  * Common Development and Distribution License (the "License").
6*5c51f124SMoriah Waterland  * You may not use this file except in compliance with the License.
7*5c51f124SMoriah Waterland  *
8*5c51f124SMoriah Waterland  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5c51f124SMoriah Waterland  * or http://www.opensolaris.org/os/licensing.
10*5c51f124SMoriah Waterland  * See the License for the specific language governing permissions
11*5c51f124SMoriah Waterland  * and limitations under the License.
12*5c51f124SMoriah Waterland  *
13*5c51f124SMoriah Waterland  * When distributing Covered Code, include this CDDL HEADER in each
14*5c51f124SMoriah Waterland  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5c51f124SMoriah Waterland  * If applicable, add the following below this CDDL HEADER, with the
16*5c51f124SMoriah Waterland  * fields enclosed by brackets "[]" replaced with your own identifying
17*5c51f124SMoriah Waterland  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5c51f124SMoriah Waterland  *
19*5c51f124SMoriah Waterland  * CDDL HEADER END
20*5c51f124SMoriah Waterland  */
21*5c51f124SMoriah Waterland 
22*5c51f124SMoriah Waterland /*
23*5c51f124SMoriah Waterland  * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24*5c51f124SMoriah Waterland  * Use is subject to license terms.
25*5c51f124SMoriah Waterland  */
26*5c51f124SMoriah Waterland 
27*5c51f124SMoriah Waterland /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28*5c51f124SMoriah Waterland /* All Rights Reserved */
29*5c51f124SMoriah Waterland 
30*5c51f124SMoriah Waterland 
31*5c51f124SMoriah Waterland #include <stdio.h>
32*5c51f124SMoriah Waterland #include <limits.h>
33*5c51f124SMoriah Waterland #include <stdlib.h>
34*5c51f124SMoriah Waterland #include <unistd.h>
35*5c51f124SMoriah Waterland #include <string.h>
36*5c51f124SMoriah Waterland #include <pkglocs.h>
37*5c51f124SMoriah Waterland #include <locale.h>
38*5c51f124SMoriah Waterland #include <libintl.h>
39*5c51f124SMoriah Waterland #include <libgen.h>
40*5c51f124SMoriah Waterland #include <signal.h>
41*5c51f124SMoriah Waterland #include <sys/stat.h>
42*5c51f124SMoriah Waterland #include <sys/statvfs.h>
43*5c51f124SMoriah Waterland #include <sys/types.h>
44*5c51f124SMoriah Waterland #include <fcntl.h>
45*5c51f124SMoriah Waterland #include <dirent.h>
46*5c51f124SMoriah Waterland #include <boot_http.h>
47*5c51f124SMoriah Waterland #include <errno.h>
48*5c51f124SMoriah Waterland #include <ctype.h>
49*5c51f124SMoriah Waterland #include <openssl/pkcs7.h>
50*5c51f124SMoriah Waterland #include <openssl/ocsp.h>
51*5c51f124SMoriah Waterland #include <openssl/pkcs12.h>
52*5c51f124SMoriah Waterland #include <openssl/err.h>
53*5c51f124SMoriah Waterland #include <openssl/x509.h>
54*5c51f124SMoriah Waterland #include <openssl/pem.h>
55*5c51f124SMoriah Waterland #include <openssl/evp.h>
56*5c51f124SMoriah Waterland #include <openssl/rand.h>
57*5c51f124SMoriah Waterland #include <openssl/x509v3.h>
58*5c51f124SMoriah Waterland #include "pkglib.h"
59*5c51f124SMoriah Waterland #include "pkglibmsgs.h"
60*5c51f124SMoriah Waterland #include "pkglocale.h"
61*5c51f124SMoriah Waterland #include "keystore.h"
62*5c51f124SMoriah Waterland #include "pkgweb.h"
63*5c51f124SMoriah Waterland #include "pkgerr.h"
64*5c51f124SMoriah Waterland #include "p12lib.h"
65*5c51f124SMoriah Waterland 
66*5c51f124SMoriah Waterland /* fixed format when making an OCSP request */
67*5c51f124SMoriah Waterland #define	OCSP_REQUEST_FORMAT \
68*5c51f124SMoriah Waterland 	"POST %s HTTP/1.0\r\n" \
69*5c51f124SMoriah Waterland 	"Content-Type: application/ocsp-request\r\n" \
70*5c51f124SMoriah Waterland 	"Content-Length: %d\r\n\r\n"
71*5c51f124SMoriah Waterland 
72*5c51f124SMoriah Waterland /*
73*5c51f124SMoriah Waterland  * no security is afforded by using this phrase to "encrypt" CA certificates,
74*5c51f124SMoriah Waterland  * but it might aid in debugging and has to be non-null
75*5c51f124SMoriah Waterland  */
76*5c51f124SMoriah Waterland #define	WEB_CA_PHRASE		"schizophrenic"
77*5c51f124SMoriah Waterland 
78*5c51f124SMoriah Waterland /* This one needs the ': ' at the end */
79*5c51f124SMoriah Waterland #define	CONTENT_TYPE_HDR	"Content-Type"
80*5c51f124SMoriah Waterland #define	CONTENT_DISPOSITION_HDR	"Content-Disposition"
81*5c51f124SMoriah Waterland #define	CONTENT_OCSP_RESP	"application/ocsp-response"
82*5c51f124SMoriah Waterland #define	CONTENT_LENGTH_HDR	"Content-Length"
83*5c51f124SMoriah Waterland #define	LAST_MODIFIED_HDR	"Last-Modified"
84*5c51f124SMoriah Waterland #define	OCSP_BUFSIZ	1024
85*5c51f124SMoriah Waterland 
86*5c51f124SMoriah Waterland /*
87*5c51f124SMoriah Waterland  * default amount of time that is allowed for error when checking
88*5c51f124SMoriah Waterland  * OCSP response validity.
89*5c51f124SMoriah Waterland  * For example, if this is set to 5 minutes, then if a response
90*5c51f124SMoriah Waterland  * is issued that is valid from 12:00 to 1:00, then we will
91*5c51f124SMoriah Waterland  * accept it if the local time is between 11:55 and 1:05.
92*5c51f124SMoriah Waterland  * This takes care of not-quite-synchronized server and client clocks.
93*5c51f124SMoriah Waterland  */
94*5c51f124SMoriah Waterland #define	OCSP_VALIDITY_PERIOD	(5 * 60)
95*5c51f124SMoriah Waterland 
96*5c51f124SMoriah Waterland /* this value is defined by getpassphrase(3c) manpage */
97*5c51f124SMoriah Waterland #define	MAX_PHRASELEN		257
98*5c51f124SMoriah Waterland 
99*5c51f124SMoriah Waterland /* Max length of "enter password again" prompt message */
100*5c51f124SMoriah Waterland #define	MAX_VERIFY_MSGLEN	1024
101*5c51f124SMoriah Waterland 
102*5c51f124SMoriah Waterland /* local prototypes */
103*5c51f124SMoriah Waterland static boolean_t remove_dwnld_file(char *);
104*5c51f124SMoriah Waterland static boolean_t get_ENV_proxyport(PKG_ERR *, ushort_t *);
105*5c51f124SMoriah Waterland static boolean_t make_link(char *, char *);
106*5c51f124SMoriah Waterland static WebStatus web_send_request(PKG_ERR *, int, int, int);
107*5c51f124SMoriah Waterland static boolean_t web_eval_headers(PKG_ERR *);
108*5c51f124SMoriah Waterland static WebStatus web_get_file(PKG_ERR *, char *, int, char **);
109*5c51f124SMoriah Waterland static boolean_t ck_dwnld_dir_space(PKG_ERR *, char *, ulong_t);
110*5c51f124SMoriah Waterland static WebStatus web_connect(PKG_ERR *);
111*5c51f124SMoriah Waterland static boolean_t web_setup(PKG_ERR *);
112*5c51f124SMoriah Waterland static boolean_t check_dwnld_dir(PKG_ERR *, char *);
113*5c51f124SMoriah Waterland static boolean_t parse_url_proxy(PKG_ERR *, char *, char *, ushort_t);
114*5c51f124SMoriah Waterland static boolean_t web_disconnect(void);
115*5c51f124SMoriah Waterland static char *get_unique_filename(char *, char *);
116*5c51f124SMoriah Waterland static boolean_t get_ENV_proxy(PKG_ERR *, char **);
117*5c51f124SMoriah Waterland static char *condense_lastmodified(char *);
118*5c51f124SMoriah Waterland static int web_verify(int, X509_STORE_CTX *);
119*5c51f124SMoriah Waterland static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
120*5c51f124SMoriah Waterland static boolean_t get_ocsp_uri(X509 *, char **);
121*5c51f124SMoriah Waterland static OCSPStatus ocsp_verify(PKG_ERR *, X509 *, X509 *, char *, url_hport_t *,
122*5c51f124SMoriah Waterland     STACK_OF(X509) *);
123*5c51f124SMoriah Waterland static char	*get_time_string(ASN1_GENERALIZEDTIME *);
124*5c51f124SMoriah Waterland static char	*write_ca_file(PKG_ERR *, char *, STACK_OF(X509) *, char *);
125*5c51f124SMoriah Waterland static boolean_t _get_random_info(void *, int);
126*5c51f124SMoriah Waterland static boolean_t	init_session(void);
127*5c51f124SMoriah Waterland static void	progress_setup(int, ulong_t);
128*5c51f124SMoriah Waterland static void	progress_report(int, ulong_t);
129*5c51f124SMoriah Waterland static void	progress_finish(int);
130*5c51f124SMoriah Waterland static char	*replace_token(char *, char, char);
131*5c51f124SMoriah Waterland static void	dequote(char *);
132*5c51f124SMoriah Waterland static void	trim(char *);
133*5c51f124SMoriah Waterland 
134*5c51f124SMoriah Waterland 
135*5c51f124SMoriah Waterland /*
136*5c51f124SMoriah Waterland  * structure used to hold data passed back to the
137*5c51f124SMoriah Waterland  * X509 verify callback routine in validate_signature()
138*5c51f124SMoriah Waterland  */
139*5c51f124SMoriah Waterland typedef struct {
140*5c51f124SMoriah Waterland 	url_hport_t	*proxy;
141*5c51f124SMoriah Waterland 	PKG_ERR		*err;
142*5c51f124SMoriah Waterland 	STACK_OF(X509)	*cas;
143*5c51f124SMoriah Waterland } verify_cb_data_t;
144*5c51f124SMoriah Waterland 
145*5c51f124SMoriah Waterland /* Progress bar variables */
146*5c51f124SMoriah Waterland static ulong_t const_increment, const_divider, completed, const_completed;
147*5c51f124SMoriah Waterland 
148*5c51f124SMoriah Waterland /* current network backoff wait period */
149*5c51f124SMoriah Waterland static int cur_backoff = 0;
150*5c51f124SMoriah Waterland 
151*5c51f124SMoriah Waterland /* download session context handle */
152*5c51f124SMoriah Waterland static WEB_SESSION *ps;
153*5c51f124SMoriah Waterland 
154*5c51f124SMoriah Waterland static int	webpkg_install = 0;
155*5c51f124SMoriah Waterland static char	*prompt = NULL;
156*5c51f124SMoriah Waterland static char	*passarg = NULL;
157*5c51f124SMoriah Waterland 
158*5c51f124SMoriah Waterland 
159*5c51f124SMoriah Waterland /* ~~~~~~~~~~~~~~ Public Functions ~~~~~~~~~~~~~~~~~~~ */
160*5c51f124SMoriah Waterland 
161*5c51f124SMoriah Waterland /*
162*5c51f124SMoriah Waterland  * Name:		set_prompt
163*5c51f124SMoriah Waterland  * Description:	Specifies the prompt to use with the pkglib
164*5c51f124SMoriah Waterland  *		passphrase callback routine.
165*5c51f124SMoriah Waterland  *
166*5c51f124SMoriah Waterland  * Arguments:	newprompt - The prompt to display
167*5c51f124SMoriah Waterland  *
168*5c51f124SMoriah Waterland  * Returns :	NONE
169*5c51f124SMoriah Waterland  */
170*5c51f124SMoriah Waterland void
171*5c51f124SMoriah Waterland set_passphrase_prompt(char *newprompt)
172*5c51f124SMoriah Waterland {
173*5c51f124SMoriah Waterland 	prompt = newprompt;
174*5c51f124SMoriah Waterland }
175*5c51f124SMoriah Waterland 
176*5c51f124SMoriah Waterland /*
177*5c51f124SMoriah Waterland  * Name:		set_passarg
178*5c51f124SMoriah Waterland  * Description:	Specifies the passphrase retrieval method
179*5c51f124SMoriah Waterland  *		 to use with the pkglib
180*5c51f124SMoriah Waterland  *		passphrase callback routine.
181*5c51f124SMoriah Waterland  *
182*5c51f124SMoriah Waterland  * Arguments:	newpassarg - The new password retrieval arg
183*5c51f124SMoriah Waterland  *
184*5c51f124SMoriah Waterland  * Returns :	NONE
185*5c51f124SMoriah Waterland  */
186*5c51f124SMoriah Waterland void
187*5c51f124SMoriah Waterland set_passphrase_passarg(char *newpassarg)
188*5c51f124SMoriah Waterland {
189*5c51f124SMoriah Waterland 	passarg = newpassarg;
190*5c51f124SMoriah Waterland }
191*5c51f124SMoriah Waterland 
192*5c51f124SMoriah Waterland /*
193*5c51f124SMoriah Waterland  * Name:		get_proxy_port
194*5c51f124SMoriah Waterland  * Description:	Resolves proxy specification
195*5c51f124SMoriah Waterland  *
196*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
197*5c51f124SMoriah Waterland  *     		proxy - Location to store result - if *proxy is not
198*5c51f124SMoriah Waterland  *		null, then it will be validated, but not changed
199*5c51f124SMoriah Waterland  *
200*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
201*5c51f124SMoriah Waterland  *		on success, *proxy and *port are set to either
202*5c51f124SMoriah Waterland  *		the user-supplied proxy and port, or the
203*5c51f124SMoriah Waterland  *		ones found in the environment variables
204*5c51f124SMoriah Waterland  *		HTTPPROXY and/or HTTPROXYPORT
205*5c51f124SMoriah Waterland  */
206*5c51f124SMoriah Waterland boolean_t
207*5c51f124SMoriah Waterland get_proxy_port(PKG_ERR *err, char **proxy, ushort_t *port)
208*5c51f124SMoriah Waterland {
209*5c51f124SMoriah Waterland 	if (*proxy != NULL) {
210*5c51f124SMoriah Waterland 		if (!path_valid(*proxy)) {
211*5c51f124SMoriah Waterland 			/* bad proxy supplied */
212*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
213*5c51f124SMoriah Waterland 			    gettext(ERR_BAD_PROXY), *proxy);
214*5c51f124SMoriah Waterland 			return (B_FALSE);
215*5c51f124SMoriah Waterland 		}
216*5c51f124SMoriah Waterland 		if (!get_ENV_proxyport(err, port)) {
217*5c51f124SMoriah Waterland 			/* env set, but bad */
218*5c51f124SMoriah Waterland 			return (B_FALSE);
219*5c51f124SMoriah Waterland 		}
220*5c51f124SMoriah Waterland 	} else {
221*5c51f124SMoriah Waterland 		if (!get_ENV_proxy(err, proxy)) {
222*5c51f124SMoriah Waterland 			/* environment variable set, but bad */
223*5c51f124SMoriah Waterland 			return (B_FALSE);
224*5c51f124SMoriah Waterland 		}
225*5c51f124SMoriah Waterland 		if ((*proxy != NULL) && !path_valid(*proxy)) {
226*5c51f124SMoriah Waterland 			/* env variable set, but bad */
227*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
228*5c51f124SMoriah Waterland 			    gettext(ERR_BAD_PROXY), *proxy);
229*5c51f124SMoriah Waterland 			return (B_FALSE);
230*5c51f124SMoriah Waterland 		}
231*5c51f124SMoriah Waterland 		if (!get_ENV_proxyport(err, port)) {
232*5c51f124SMoriah Waterland 			/* env variable set, but bad */
233*5c51f124SMoriah Waterland 			return (B_FALSE);
234*5c51f124SMoriah Waterland 		}
235*5c51f124SMoriah Waterland 	}
236*5c51f124SMoriah Waterland 	return (B_TRUE);
237*5c51f124SMoriah Waterland }
238*5c51f124SMoriah Waterland 
239*5c51f124SMoriah Waterland /*
240*5c51f124SMoriah Waterland  * Name:		path_valid
241*5c51f124SMoriah Waterland  * Description:	Checks a string for being a valid path
242*5c51f124SMoriah Waterland  *
243*5c51f124SMoriah Waterland  * Arguments:	path - path to validate
244*5c51f124SMoriah Waterland  *
245*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise.
246*5c51f124SMoriah Waterland  *		B_FALSE means path was null, too long (>PATH_MAX),
247*5c51f124SMoriah Waterland  *		or too short (<1)
248*5c51f124SMoriah Waterland  */
249*5c51f124SMoriah Waterland boolean_t
250*5c51f124SMoriah Waterland path_valid(char *path)
251*5c51f124SMoriah Waterland {
252*5c51f124SMoriah Waterland 	if (path == NULL) {
253*5c51f124SMoriah Waterland 		return (B_FALSE);
254*5c51f124SMoriah Waterland 	} else if (strlen(path) > PATH_MAX) {
255*5c51f124SMoriah Waterland 		return (B_FALSE);
256*5c51f124SMoriah Waterland 	} else if (strlen(path) >= 1) {
257*5c51f124SMoriah Waterland 		return (B_TRUE);
258*5c51f124SMoriah Waterland 	} else {
259*5c51f124SMoriah Waterland 		/* path < 1 */
260*5c51f124SMoriah Waterland 		return (B_FALSE);
261*5c51f124SMoriah Waterland 	}
262*5c51f124SMoriah Waterland }
263*5c51f124SMoriah Waterland 
264*5c51f124SMoriah Waterland /*
265*5c51f124SMoriah Waterland  * Name:		web_cleanup
266*5c51f124SMoriah Waterland  * Description:	Deletes temp files, closes, frees memory taken
267*5c51f124SMoriah Waterland  *		by 'ps' static structure
268*5c51f124SMoriah Waterland  *
269*5c51f124SMoriah Waterland  * Arguments:	none
270*5c51f124SMoriah Waterland  *
271*5c51f124SMoriah Waterland  * Returns :	none
272*5c51f124SMoriah Waterland  */
273*5c51f124SMoriah Waterland void
274*5c51f124SMoriah Waterland web_cleanup(void)
275*5c51f124SMoriah Waterland {
276*5c51f124SMoriah Waterland 	PKG_ERR *err;
277*5c51f124SMoriah Waterland 
278*5c51f124SMoriah Waterland 	if (ps == NULL)
279*5c51f124SMoriah Waterland 		return;
280*5c51f124SMoriah Waterland 
281*5c51f124SMoriah Waterland 	err = pkgerr_new();
282*5c51f124SMoriah Waterland 
283*5c51f124SMoriah Waterland 	if (ps->keystore) {
284*5c51f124SMoriah Waterland 		(void) close_keystore(err, ps->keystore, NULL);
285*5c51f124SMoriah Waterland 	}
286*5c51f124SMoriah Waterland 
287*5c51f124SMoriah Waterland 	ps->keystore = NULL;
288*5c51f124SMoriah Waterland 
289*5c51f124SMoriah Waterland 	pkgerr_free(err);
290*5c51f124SMoriah Waterland 
291*5c51f124SMoriah Waterland 	if (ps->uniqfile) {
292*5c51f124SMoriah Waterland 		(void) remove_dwnld_file(ps->uniqfile);
293*5c51f124SMoriah Waterland 		free(ps->uniqfile);
294*5c51f124SMoriah Waterland 		ps->uniqfile = NULL;
295*5c51f124SMoriah Waterland 	}
296*5c51f124SMoriah Waterland 	if (ps->link) {
297*5c51f124SMoriah Waterland 		(void) remove_dwnld_file(ps->link);
298*5c51f124SMoriah Waterland 		free(ps->link);
299*5c51f124SMoriah Waterland 		ps->link = NULL;
300*5c51f124SMoriah Waterland 	}
301*5c51f124SMoriah Waterland 	if (ps->dwnld_dir) {
302*5c51f124SMoriah Waterland 	    (void) rmdir(ps->dwnld_dir);
303*5c51f124SMoriah Waterland 	    ps->dwnld_dir = NULL;
304*5c51f124SMoriah Waterland 	}
305*5c51f124SMoriah Waterland 	if (ps->errstr) {
306*5c51f124SMoriah Waterland 	    free(ps->errstr);
307*5c51f124SMoriah Waterland 	    ps->errstr = NULL;
308*5c51f124SMoriah Waterland 	}
309*5c51f124SMoriah Waterland 
310*5c51f124SMoriah Waterland 	if (ps->content) {
311*5c51f124SMoriah Waterland 	    free(ps->content);
312*5c51f124SMoriah Waterland 	    ps->content = NULL;
313*5c51f124SMoriah Waterland 	}
314*5c51f124SMoriah Waterland 
315*5c51f124SMoriah Waterland 	if (ps->resp) {
316*5c51f124SMoriah Waterland 		http_free_respinfo(ps->resp);
317*5c51f124SMoriah Waterland 		ps->resp = NULL;
318*5c51f124SMoriah Waterland 	}
319*5c51f124SMoriah Waterland 
320*5c51f124SMoriah Waterland 	if (ps) {
321*5c51f124SMoriah Waterland 	    free(ps);
322*5c51f124SMoriah Waterland 	    ps = NULL;
323*5c51f124SMoriah Waterland 	}
324*5c51f124SMoriah Waterland }
325*5c51f124SMoriah Waterland 
326*5c51f124SMoriah Waterland /*
327*5c51f124SMoriah Waterland  * Name:		web_session_control
328*5c51f124SMoriah Waterland  * Description:	Downloads an arbitrary URL and saves to disk.
329*5c51f124SMoriah Waterland  *
330*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
331*5c51f124SMoriah Waterland  *     		url - URL pointing to content to download - can be
332*5c51f124SMoriah Waterland  *			http:// or https://
333*5c51f124SMoriah Waterland  *		dwnld_dir - Directory to download into
334*5c51f124SMoriah Waterland  *		keystore - keystore to use for accessing trusted
335*5c51f124SMoriah Waterland  *			certs when downloading using SSL
336*5c51f124SMoriah Waterland  *		proxy - HTTP proxy to use, or NULL for no proxy
337*5c51f124SMoriah Waterland  *		proxy_port - HTTP proxy port to use, ignored
338*5c51f124SMoriah Waterland  *			if proxy is NULL
339*5c51f124SMoriah Waterland  *		passarg - method to retrieve password
340*5c51f124SMoriah Waterland  *		retries - # of times to retry download before
341*5c51f124SMoriah Waterland  *			giving up
342*5c51f124SMoriah Waterland  *		timeout - how long to wait before retrying,
343*5c51f124SMoriah Waterland  *			when download is interrupted
344*5c51f124SMoriah Waterland  *		nointeract - if non-zero, do not output
345*5c51f124SMoriah Waterland  *			download progress to screen
346*5c51f124SMoriah Waterland  *
347*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
348*5c51f124SMoriah Waterland  */
349*5c51f124SMoriah Waterland boolean_t
350*5c51f124SMoriah Waterland web_session_control(PKG_ERR *err, char *url, char *dwnld_dir,
351*5c51f124SMoriah Waterland     keystore_handle_t keystore, char *proxy, ushort_t proxy_port,
352*5c51f124SMoriah Waterland     int retries, int timeout, int nointeract, char **fname)
353*5c51f124SMoriah Waterland {
354*5c51f124SMoriah Waterland 	int i;
355*5c51f124SMoriah Waterland 	boolean_t ret = B_TRUE;
356*5c51f124SMoriah Waterland 	boolean_t retrieved = B_FALSE;
357*5c51f124SMoriah Waterland 
358*5c51f124SMoriah Waterland 	if (!init_session()) {
359*5c51f124SMoriah Waterland 	    ret = B_FALSE;
360*5c51f124SMoriah Waterland 	    goto cleanup;
361*5c51f124SMoriah Waterland 	}
362*5c51f124SMoriah Waterland 
363*5c51f124SMoriah Waterland 	if (!parse_url_proxy(err, url, proxy, proxy_port)) {
364*5c51f124SMoriah Waterland 		ret = B_FALSE;
365*5c51f124SMoriah Waterland 		goto cleanup;
366*5c51f124SMoriah Waterland 	}
367*5c51f124SMoriah Waterland 
368*5c51f124SMoriah Waterland 	ps->timeout = timeout;
369*5c51f124SMoriah Waterland 
370*5c51f124SMoriah Waterland 	if (keystore != NULL)
371*5c51f124SMoriah Waterland 		ps->keystore = keystore;
372*5c51f124SMoriah Waterland 
373*5c51f124SMoriah Waterland 	if (dwnld_dir != NULL)
374*5c51f124SMoriah Waterland 		ps->dwnld_dir = xstrdup(dwnld_dir);
375*5c51f124SMoriah Waterland 	else {
376*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_DWNLD_DIR));
377*5c51f124SMoriah Waterland 		ret = B_FALSE;
378*5c51f124SMoriah Waterland 		goto cleanup;
379*5c51f124SMoriah Waterland 	}
380*5c51f124SMoriah Waterland 
381*5c51f124SMoriah Waterland 	if (!check_dwnld_dir(err, dwnld_dir)) {
382*5c51f124SMoriah Waterland 		ret = B_FALSE;
383*5c51f124SMoriah Waterland 		goto cleanup;
384*5c51f124SMoriah Waterland 	}
385*5c51f124SMoriah Waterland 
386*5c51f124SMoriah Waterland 	for (i = 0; i < retries && !retrieved; i++) {
387*5c51f124SMoriah Waterland 		if (!web_setup(err)) {
388*5c51f124SMoriah Waterland 			ret = B_FALSE;
389*5c51f124SMoriah Waterland 			goto cleanup;
390*5c51f124SMoriah Waterland 		}
391*5c51f124SMoriah Waterland 
392*5c51f124SMoriah Waterland 		switch (web_connect(err)) {
393*5c51f124SMoriah Waterland 		    /* time out and wait a little bit for these failures */
394*5c51f124SMoriah Waterland 		case WEB_OK:
395*5c51f124SMoriah Waterland 		    /* were able to connect */
396*5c51f124SMoriah Waterland 			reset_backoff();
397*5c51f124SMoriah Waterland 			break;
398*5c51f124SMoriah Waterland 		case WEB_TIMEOUT:
399*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
400*5c51f124SMoriah Waterland 			(void) web_disconnect();
401*5c51f124SMoriah Waterland 			backoff();
402*5c51f124SMoriah Waterland 			continue;
403*5c51f124SMoriah Waterland 
404*5c51f124SMoriah Waterland 		case WEB_CONNREFUSED:
405*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
406*5c51f124SMoriah Waterland 			    ps->url.hport.hostname);
407*5c51f124SMoriah Waterland 			(void) web_disconnect();
408*5c51f124SMoriah Waterland 			backoff();
409*5c51f124SMoriah Waterland 			continue;
410*5c51f124SMoriah Waterland 		case WEB_HOSTDOWN:
411*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
412*5c51f124SMoriah Waterland 			    ps->url.hport.hostname);
413*5c51f124SMoriah Waterland 			(void) web_disconnect();
414*5c51f124SMoriah Waterland 			backoff();
415*5c51f124SMoriah Waterland 			continue;
416*5c51f124SMoriah Waterland 
417*5c51f124SMoriah Waterland 		default:
418*5c51f124SMoriah Waterland 			/* every other failure is a hard failure, so bail */
419*5c51f124SMoriah Waterland 			ret = B_FALSE;
420*5c51f124SMoriah Waterland 			goto cleanup;
421*5c51f124SMoriah Waterland 		}
422*5c51f124SMoriah Waterland 
423*5c51f124SMoriah Waterland 		switch (web_send_request(err, HTTP_REQ_TYPE_HEAD,
424*5c51f124SMoriah Waterland 				ps->data.cur_pos, ps->data.content_length)) {
425*5c51f124SMoriah Waterland 		case WEB_OK:
426*5c51f124SMoriah Waterland 		    /* were able to connect */
427*5c51f124SMoriah Waterland 			reset_backoff();
428*5c51f124SMoriah Waterland 			break;
429*5c51f124SMoriah Waterland 		case WEB_TIMEOUT:
430*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
431*5c51f124SMoriah Waterland 			(void) web_disconnect();
432*5c51f124SMoriah Waterland 			backoff();
433*5c51f124SMoriah Waterland 			continue;
434*5c51f124SMoriah Waterland 
435*5c51f124SMoriah Waterland 		case WEB_CONNREFUSED:
436*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
437*5c51f124SMoriah Waterland 			    ps->url.hport.hostname);
438*5c51f124SMoriah Waterland 			(void) web_disconnect();
439*5c51f124SMoriah Waterland 			backoff();
440*5c51f124SMoriah Waterland 			continue;
441*5c51f124SMoriah Waterland 		case WEB_HOSTDOWN:
442*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
443*5c51f124SMoriah Waterland 			    ps->url.hport.hostname);
444*5c51f124SMoriah Waterland 			(void) web_disconnect();
445*5c51f124SMoriah Waterland 			backoff();
446*5c51f124SMoriah Waterland 			continue;
447*5c51f124SMoriah Waterland 		default:
448*5c51f124SMoriah Waterland 			/* every other case is failure, so bail */
449*5c51f124SMoriah Waterland 			ret = B_FALSE;
450*5c51f124SMoriah Waterland 			goto cleanup;
451*5c51f124SMoriah Waterland 		}
452*5c51f124SMoriah Waterland 
453*5c51f124SMoriah Waterland 		if (!web_eval_headers(err)) {
454*5c51f124SMoriah Waterland 			ret = B_FALSE;
455*5c51f124SMoriah Waterland 			goto cleanup;
456*5c51f124SMoriah Waterland 		}
457*5c51f124SMoriah Waterland 
458*5c51f124SMoriah Waterland 		switch (web_get_file(err, dwnld_dir, nointeract, fname)) {
459*5c51f124SMoriah Waterland 		case WEB_OK:
460*5c51f124SMoriah Waterland 			/* were able to retrieve file */
461*5c51f124SMoriah Waterland 			retrieved = B_TRUE;
462*5c51f124SMoriah Waterland 			reset_backoff();
463*5c51f124SMoriah Waterland 			break;
464*5c51f124SMoriah Waterland 
465*5c51f124SMoriah Waterland 		case WEB_TIMEOUT:
466*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
467*5c51f124SMoriah Waterland 			(void) web_disconnect();
468*5c51f124SMoriah Waterland 			backoff();
469*5c51f124SMoriah Waterland 			continue;
470*5c51f124SMoriah Waterland 
471*5c51f124SMoriah Waterland 		case WEB_CONNREFUSED:
472*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
473*5c51f124SMoriah Waterland 			    ps->url.hport.hostname);
474*5c51f124SMoriah Waterland 			(void) web_disconnect();
475*5c51f124SMoriah Waterland 			backoff();
476*5c51f124SMoriah Waterland 			continue;
477*5c51f124SMoriah Waterland 		case WEB_HOSTDOWN:
478*5c51f124SMoriah Waterland 			echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
479*5c51f124SMoriah Waterland 			    ps->url.hport.hostname);
480*5c51f124SMoriah Waterland 			(void) web_disconnect();
481*5c51f124SMoriah Waterland 			backoff();
482*5c51f124SMoriah Waterland 			continue;
483*5c51f124SMoriah Waterland 		default:
484*5c51f124SMoriah Waterland 			/* every other failure is a hard failure, so bail */
485*5c51f124SMoriah Waterland 			ret = B_FALSE;
486*5c51f124SMoriah Waterland 			goto cleanup;
487*5c51f124SMoriah Waterland 		}
488*5c51f124SMoriah Waterland 	}
489*5c51f124SMoriah Waterland 
490*5c51f124SMoriah Waterland 	if (!retrieved) {
491*5c51f124SMoriah Waterland 		/* max retries attempted */
492*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB,
493*5c51f124SMoriah Waterland 		    gettext(ERR_DWNLD_FAILED), retries);
494*5c51f124SMoriah Waterland 		ret = B_FALSE;
495*5c51f124SMoriah Waterland 	}
496*5c51f124SMoriah Waterland cleanup:
497*5c51f124SMoriah Waterland 	(void) web_disconnect();
498*5c51f124SMoriah Waterland 	if (!ret) {
499*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_DWNLD), url);
500*5c51f124SMoriah Waterland 	}
501*5c51f124SMoriah Waterland 	return (ret);
502*5c51f124SMoriah Waterland }
503*5c51f124SMoriah Waterland 
504*5c51f124SMoriah Waterland /*
505*5c51f124SMoriah Waterland  * Name:		get_signature
506*5c51f124SMoriah Waterland  * Description:	retrieves signature from signed package.
507*5c51f124SMoriah Waterland  *
508*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
509*5c51f124SMoriah Waterland  *		ids_name - name of package stream, for error reporting
510*5c51f124SMoriah Waterland  *     		devp - Device on which package resides that we
511*5c51f124SMoriah Waterland  *		result - where to store resulting PKCS7 signature
512*5c51f124SMoriah Waterland  *
513*5c51f124SMoriah Waterland  * Returns :	B_TRUE - package is signed and signature returned OR
514*5c51f124SMoriah Waterland  *		package is not signed, in which case result is NULL
515*5c51f124SMoriah Waterland  *
516*5c51f124SMoriah Waterland  *		B_FALSE - there were problems accessing signature,
517*5c51f124SMoriah Waterland  *		and it is unknown whether it is signed or not.  Errors
518*5c51f124SMoriah Waterland  *		recorded in 'err'.
519*5c51f124SMoriah Waterland  */
520*5c51f124SMoriah Waterland boolean_t
521*5c51f124SMoriah Waterland get_signature(PKG_ERR *err, char *ids_name, struct pkgdev *devp, PKCS7 **result)
522*5c51f124SMoriah Waterland {
523*5c51f124SMoriah Waterland 	char path[PATH_MAX];
524*5c51f124SMoriah Waterland 	int len, fd = -1;
525*5c51f124SMoriah Waterland 	struct stat buf;
526*5c51f124SMoriah Waterland 	FILE *fp = NULL;
527*5c51f124SMoriah Waterland 	boolean_t	ret = B_TRUE;
528*5c51f124SMoriah Waterland 	BIO	*sig_in = NULL;
529*5c51f124SMoriah Waterland 	PKCS7	*p7 = NULL;
530*5c51f124SMoriah Waterland 
531*5c51f124SMoriah Waterland 	/*
532*5c51f124SMoriah Waterland 	 * look for signature.  If one was in the stream,
533*5c51f124SMoriah Waterland 	 * it is now extracted
534*5c51f124SMoriah Waterland 	 */
535*5c51f124SMoriah Waterland 	if (((len = snprintf(path, PATH_MAX, "%s/%s", devp->dirname,
536*5c51f124SMoriah Waterland 	    SIGNATURE_FILENAME)) >= PATH_MAX) || (len < 0)) {
537*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), ids_name);
538*5c51f124SMoriah Waterland 		ret = B_FALSE;
539*5c51f124SMoriah Waterland 		goto cleanup;
540*5c51f124SMoriah Waterland 	}
541*5c51f124SMoriah Waterland 
542*5c51f124SMoriah Waterland 	if ((fd = open(path, O_RDONLY|O_NONBLOCK)) == -1) {
543*5c51f124SMoriah Waterland 		/*
544*5c51f124SMoriah Waterland 		 * only if the signature is non-existant
545*5c51f124SMoriah Waterland 		 * do we "pass"
546*5c51f124SMoriah Waterland 		 */
547*5c51f124SMoriah Waterland 		if (errno != ENOENT) {
548*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
549*5c51f124SMoriah Waterland 			    strerror(errno));
550*5c51f124SMoriah Waterland 			ret = B_FALSE;
551*5c51f124SMoriah Waterland 			goto cleanup;
552*5c51f124SMoriah Waterland 		}
553*5c51f124SMoriah Waterland 	} else {
554*5c51f124SMoriah Waterland 		/* found sig file.  parse it. */
555*5c51f124SMoriah Waterland 		if (fstat(fd, &buf) == -1) {
556*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
557*5c51f124SMoriah Waterland 			    gettext(ERR_OPENSIG), strerror(errno));
558*5c51f124SMoriah Waterland 			ret = B_FALSE;
559*5c51f124SMoriah Waterland 			goto cleanup;
560*5c51f124SMoriah Waterland 		}
561*5c51f124SMoriah Waterland 
562*5c51f124SMoriah Waterland 		if (!S_ISREG(buf.st_mode)) {
563*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
564*5c51f124SMoriah Waterland 			    (gettext(ERR_NOT_REG)));
565*5c51f124SMoriah Waterland 			ret = B_FALSE;
566*5c51f124SMoriah Waterland 			goto cleanup;
567*5c51f124SMoriah Waterland 		}
568*5c51f124SMoriah Waterland 
569*5c51f124SMoriah Waterland 		if ((fp = fdopen(fd, "r")) == NULL) {
570*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
571*5c51f124SMoriah Waterland 			    gettext(ERR_OPENSIG), strerror(errno));
572*5c51f124SMoriah Waterland 			ret = B_FALSE;
573*5c51f124SMoriah Waterland 			goto cleanup;
574*5c51f124SMoriah Waterland 		}
575*5c51f124SMoriah Waterland 
576*5c51f124SMoriah Waterland 		/*
577*5c51f124SMoriah Waterland 		 * read in signature.  If it's invalid, we
578*5c51f124SMoriah Waterland 		 * punt, unless we're ignoring it
579*5c51f124SMoriah Waterland 		 */
580*5c51f124SMoriah Waterland 		if ((sig_in = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
581*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
582*5c51f124SMoriah Waterland 			    gettext(ERR_OPENSIG), strerror(errno));
583*5c51f124SMoriah Waterland 			goto cleanup;
584*5c51f124SMoriah Waterland 		}
585*5c51f124SMoriah Waterland 
586*5c51f124SMoriah Waterland 		if ((p7 = PEM_read_bio_PKCS7(sig_in,
587*5c51f124SMoriah Waterland 		    NULL, NULL, NULL)) == NULL) {
588*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
589*5c51f124SMoriah Waterland 			    ids_name);
590*5c51f124SMoriah Waterland 			ret = B_FALSE;
591*5c51f124SMoriah Waterland 			goto cleanup;
592*5c51f124SMoriah Waterland 		}
593*5c51f124SMoriah Waterland 		*result = p7;
594*5c51f124SMoriah Waterland 		p7 = NULL;
595*5c51f124SMoriah Waterland 	}
596*5c51f124SMoriah Waterland 
597*5c51f124SMoriah Waterland cleanup:
598*5c51f124SMoriah Waterland 	if (sig_in)
599*5c51f124SMoriah Waterland 		(void) BIO_free(sig_in);
600*5c51f124SMoriah Waterland 	if (fp)
601*5c51f124SMoriah Waterland 		(void) fclose(fp);
602*5c51f124SMoriah Waterland 	if (fd != -1)
603*5c51f124SMoriah Waterland 		(void) close(fd);
604*5c51f124SMoriah Waterland 	if (p7)
605*5c51f124SMoriah Waterland 		(void) PKCS7_free(p7);
606*5c51f124SMoriah Waterland 
607*5c51f124SMoriah Waterland 	return (ret);
608*5c51f124SMoriah Waterland }
609*5c51f124SMoriah Waterland 
610*5c51f124SMoriah Waterland /*
611*5c51f124SMoriah Waterland  * Name:		echo_out
612*5c51f124SMoriah Waterland  * Description:	Conditionally output a message to stdout
613*5c51f124SMoriah Waterland  *
614*5c51f124SMoriah Waterland  * Arguments:	nointeract - if non-zero, do not output anything
615*5c51f124SMoriah Waterland  *		fmt - print format
616*5c51f124SMoriah Waterland  *		... - print arguments
617*5c51f124SMoriah Waterland  *
618*5c51f124SMoriah Waterland  * Returns :	none
619*5c51f124SMoriah Waterland  */
620*5c51f124SMoriah Waterland void
621*5c51f124SMoriah Waterland echo_out(int nointeract, char *fmt, ...)
622*5c51f124SMoriah Waterland {
623*5c51f124SMoriah Waterland 	va_list ap;
624*5c51f124SMoriah Waterland 
625*5c51f124SMoriah Waterland 	va_start(ap, fmt);
626*5c51f124SMoriah Waterland 
627*5c51f124SMoriah Waterland 	if (nointeract)
628*5c51f124SMoriah Waterland 		return;
629*5c51f124SMoriah Waterland 
630*5c51f124SMoriah Waterland 	(void) vfprintf(stdout, fmt, ap);
631*5c51f124SMoriah Waterland 
632*5c51f124SMoriah Waterland 	va_end(ap);
633*5c51f124SMoriah Waterland 
634*5c51f124SMoriah Waterland 	(void) putc('\n', stdout);
635*5c51f124SMoriah Waterland }
636*5c51f124SMoriah Waterland 
637*5c51f124SMoriah Waterland /*
638*5c51f124SMoriah Waterland  * Name:		strip_port
639*5c51f124SMoriah Waterland  * Description:	Returns "port" portion of a "hostname:port" string
640*5c51f124SMoriah Waterland  *
641*5c51f124SMoriah Waterland  * Arguments:	proxy - full "hostname:port" string pointer
642*5c51f124SMoriah Waterland  *
643*5c51f124SMoriah Waterland  * Returns :	the "port" portion of a "hostname:port" string,
644*5c51f124SMoriah Waterland  *		converted to a decimal integer, or (int)0
645*5c51f124SMoriah Waterland  *		if string contains no :port suffix.
646*5c51f124SMoriah Waterland  */
647*5c51f124SMoriah Waterland ushort_t
648*5c51f124SMoriah Waterland strip_port(char *proxy)
649*5c51f124SMoriah Waterland {
650*5c51f124SMoriah Waterland 	char *tmp_port;
651*5c51f124SMoriah Waterland 
652*5c51f124SMoriah Waterland 	if ((tmp_port = strpbrk(proxy, ":")) != NULL)
653*5c51f124SMoriah Waterland 		return (atoi(tmp_port));
654*5c51f124SMoriah Waterland 	else
655*5c51f124SMoriah Waterland 		return (0);
656*5c51f124SMoriah Waterland }
657*5c51f124SMoriah Waterland 
658*5c51f124SMoriah Waterland /*
659*5c51f124SMoriah Waterland  * Name:		set_web_install
660*5c51f124SMoriah Waterland  * Description:	Sets flag indicating we are doing a web-based install
661*5c51f124SMoriah Waterland  *
662*5c51f124SMoriah Waterland  * Arguments:	none
663*5c51f124SMoriah Waterland  *
664*5c51f124SMoriah Waterland  * Returns :	none
665*5c51f124SMoriah Waterland  */
666*5c51f124SMoriah Waterland void
667*5c51f124SMoriah Waterland set_web_install(void)
668*5c51f124SMoriah Waterland {
669*5c51f124SMoriah Waterland 	webpkg_install++;
670*5c51f124SMoriah Waterland }
671*5c51f124SMoriah Waterland 
672*5c51f124SMoriah Waterland /*
673*5c51f124SMoriah Waterland  * Name:		is_web_install
674*5c51f124SMoriah Waterland  * Description:	Determines whether we are doing a web-based install
675*5c51f124SMoriah Waterland  *
676*5c51f124SMoriah Waterland  * Arguments:	none
677*5c51f124SMoriah Waterland  *
678*5c51f124SMoriah Waterland  * Returns :	non-zero if we are doing a web-based install, 0 otherwise
679*5c51f124SMoriah Waterland  */
680*5c51f124SMoriah Waterland int
681*5c51f124SMoriah Waterland is_web_install(void)
682*5c51f124SMoriah Waterland {
683*5c51f124SMoriah Waterland 	return (webpkg_install);
684*5c51f124SMoriah Waterland }
685*5c51f124SMoriah Waterland 
686*5c51f124SMoriah Waterland /* ~~~~~~~~~~~~~~ Private Functions ~~~~~~~~~~~~~~~~~~~ */
687*5c51f124SMoriah Waterland 
688*5c51f124SMoriah Waterland /*
689*5c51f124SMoriah Waterland  * Name:		web_disconnect
690*5c51f124SMoriah Waterland  * Description:	Disconnects connection to web server
691*5c51f124SMoriah Waterland  *
692*5c51f124SMoriah Waterland  * Arguments:	none
693*5c51f124SMoriah Waterland  *
694*5c51f124SMoriah Waterland  * Returns :	B_TRUE - successful disconnect, B_FALSE otherwise
695*5c51f124SMoriah Waterland  *		Temp certificiate files are deleted,
696*5c51f124SMoriah Waterland  *		if one was used to initiate the connection
697*5c51f124SMoriah Waterland  *		(such as when using SSL)
698*5c51f124SMoriah Waterland  */
699*5c51f124SMoriah Waterland static boolean_t
700*5c51f124SMoriah Waterland web_disconnect(void)
701*5c51f124SMoriah Waterland {
702*5c51f124SMoriah Waterland 	if (ps->certfile) {
703*5c51f124SMoriah Waterland 		(void) unlink(ps->certfile);
704*5c51f124SMoriah Waterland 	}
705*5c51f124SMoriah Waterland 	if (http_srv_disconnect(ps->hps) == 0)
706*5c51f124SMoriah Waterland 		if (http_srv_close(ps->hps) == 0)
707*5c51f124SMoriah Waterland 			return (B_TRUE);
708*5c51f124SMoriah Waterland 
709*5c51f124SMoriah Waterland 	return (B_FALSE);
710*5c51f124SMoriah Waterland }
711*5c51f124SMoriah Waterland 
712*5c51f124SMoriah Waterland /*
713*5c51f124SMoriah Waterland  * Name:		check_dwnld_dir
714*5c51f124SMoriah Waterland  * Description:	Creates temp download directory
715*5c51f124SMoriah Waterland  *
716*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
717*5c51f124SMoriah Waterland  *     		dwnld_dir - name of directory to create
718*5c51f124SMoriah Waterland  *
719*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
720*5c51f124SMoriah Waterland  *		on success, directory is created with
721*5c51f124SMoriah Waterland  *		safe permissions
722*5c51f124SMoriah Waterland  */
723*5c51f124SMoriah Waterland static boolean_t
724*5c51f124SMoriah Waterland check_dwnld_dir(PKG_ERR *err, char *dwnld_dir)
725*5c51f124SMoriah Waterland {
726*5c51f124SMoriah Waterland 	DIR *dirp;
727*5c51f124SMoriah Waterland 
728*5c51f124SMoriah Waterland 	/*
729*5c51f124SMoriah Waterland 	 * Check the directory passed in. If it doesn't exist, create it
730*5c51f124SMoriah Waterland 	 * with strict permissions
731*5c51f124SMoriah Waterland 	 */
732*5c51f124SMoriah Waterland 	if ((dirp = opendir(dwnld_dir)) == NULL) {
733*5c51f124SMoriah Waterland 		if (mkdir(dwnld_dir, 0744) == -1) {
734*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
735*5c51f124SMoriah Waterland 			    dwnld_dir);
736*5c51f124SMoriah Waterland 			return (B_FALSE);
737*5c51f124SMoriah Waterland 		}
738*5c51f124SMoriah Waterland 	}
739*5c51f124SMoriah Waterland 	if (dirp) {
740*5c51f124SMoriah Waterland 		(void) closedir(dirp);
741*5c51f124SMoriah Waterland 	}
742*5c51f124SMoriah Waterland 	return (B_TRUE);
743*5c51f124SMoriah Waterland }
744*5c51f124SMoriah Waterland 
745*5c51f124SMoriah Waterland /*
746*5c51f124SMoriah Waterland  * Name:		ds_validate_signature
747*5c51f124SMoriah Waterland  * Description:	Validates signature found in a package datastream
748*5c51f124SMoriah Waterland  *
749*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
750*5c51f124SMoriah Waterland  *		pkgdev - Package context handle of package to verify
751*5c51f124SMoriah Waterland  *		pkgs - Null-terminated List of package name to verify
752*5c51f124SMoriah Waterland  *		ids_name - Pathname to stream to validate
753*5c51f124SMoriah Waterland  *		p7 - PKCS7 signature decoded from stream header
754*5c51f124SMoriah Waterland  *		cas - List of trusted CA certificates
755*5c51f124SMoriah Waterland  *		proxy - Proxy to use when doing online validation (OCSP)
756*5c51f124SMoriah Waterland  *		nointeract - if non-zero, do not output to screen
757*5c51f124SMoriah Waterland  *
758*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
759*5c51f124SMoriah Waterland  *		success means signature was completely validated,
760*5c51f124SMoriah Waterland  *		and contents of stream checked against signature.
761*5c51f124SMoriah Waterland  */
762*5c51f124SMoriah Waterland boolean_t
763*5c51f124SMoriah Waterland ds_validate_signature(PKG_ERR *err, struct pkgdev *pkgdev, char **pkgs,
764*5c51f124SMoriah Waterland     char *ids_name, PKCS7 *p7, STACK_OF(X509) *cas,
765*5c51f124SMoriah Waterland     url_hport_t *proxy, int nointeract)
766*5c51f124SMoriah Waterland {
767*5c51f124SMoriah Waterland 	BIO			 *p7_bio;
768*5c51f124SMoriah Waterland 	boolean_t		ret = B_TRUE;
769*5c51f124SMoriah Waterland 
770*5c51f124SMoriah Waterland 	/* make sure it's a Signed PKCS7 message */
771*5c51f124SMoriah Waterland 	if (!PKCS7_type_is_signed(p7)) {
772*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_TYPE),
773*5c51f124SMoriah Waterland 		    ids_name);
774*5c51f124SMoriah Waterland 		ret = B_FALSE;
775*5c51f124SMoriah Waterland 		goto cleanup;
776*5c51f124SMoriah Waterland 	}
777*5c51f124SMoriah Waterland 
778*5c51f124SMoriah Waterland 	/* initialize PKCS7 object to be filled in */
779*5c51f124SMoriah Waterland 	if (!PKCS7_get_detached(p7)) {
780*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_DT),
781*5c51f124SMoriah Waterland 		    ids_name);
782*5c51f124SMoriah Waterland 		ret = B_FALSE;
783*5c51f124SMoriah Waterland 		goto cleanup;
784*5c51f124SMoriah Waterland 	}
785*5c51f124SMoriah Waterland 
786*5c51f124SMoriah Waterland 	/* dump header and packages into BIO to calculate the message digest */
787*5c51f124SMoriah Waterland 	if ((p7_bio = PKCS7_dataInit(p7, NULL)) == NULL) {
788*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
789*5c51f124SMoriah Waterland 		    ids_name);
790*5c51f124SMoriah Waterland 		ret = B_FALSE;
791*5c51f124SMoriah Waterland 		goto cleanup;
792*5c51f124SMoriah Waterland 	}
793*5c51f124SMoriah Waterland 
794*5c51f124SMoriah Waterland 	if ((BIO_ds_dump_header(err, p7_bio) != 0) ||
795*5c51f124SMoriah Waterland 	    (BIO_ds_dump(err, ids_name, p7_bio) != 0)) {
796*5c51f124SMoriah Waterland 		ret = B_FALSE;
797*5c51f124SMoriah Waterland 		goto cleanup;
798*5c51f124SMoriah Waterland 	}
799*5c51f124SMoriah Waterland 	(void) BIO_flush(p7_bio);
800*5c51f124SMoriah Waterland 
801*5c51f124SMoriah Waterland 	/* validate the stream and its signature */
802*5c51f124SMoriah Waterland 	if (!validate_signature(err, ids_name, p7_bio, p7, cas,
803*5c51f124SMoriah Waterland 	    proxy, nointeract)) {
804*5c51f124SMoriah Waterland 		ret = B_FALSE;
805*5c51f124SMoriah Waterland 		goto cleanup;
806*5c51f124SMoriah Waterland 	}
807*5c51f124SMoriah Waterland 
808*5c51f124SMoriah Waterland 	/* reset device stream (really bad performance for tapes) */
809*5c51f124SMoriah Waterland 	(void) ds_close(1);
810*5c51f124SMoriah Waterland 	(void) ds_init(ids_name, pkgs, pkgdev->norewind);
811*5c51f124SMoriah Waterland 
812*5c51f124SMoriah Waterland cleanup:
813*5c51f124SMoriah Waterland 	return (ret);
814*5c51f124SMoriah Waterland }
815*5c51f124SMoriah Waterland 
816*5c51f124SMoriah Waterland 
817*5c51f124SMoriah Waterland /*
818*5c51f124SMoriah Waterland  * Name:		validate_signature
819*5c51f124SMoriah Waterland  * Description:	Validates signature of an arbitrary stream of bits
820*5c51f124SMoriah Waterland  *
821*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
822*5c51f124SMoriah Waterland  *		name - Descriptive name of object being validated,
823*5c51f124SMoriah Waterland  *			for good error reporting messages
824*5c51f124SMoriah Waterland  *		indata - BIO object to read stream bits from
825*5c51f124SMoriah Waterland  *		p7 - PKCS7 signature of stream
826*5c51f124SMoriah Waterland  *		cas - List of trusted CA certificates
827*5c51f124SMoriah Waterland  *		proxy - Proxy to use when doing online validation (OCSP)
828*5c51f124SMoriah Waterland  *		nointeract - if non-zero, do not output to screen
829*5c51f124SMoriah Waterland  *
830*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
831*5c51f124SMoriah Waterland  *		success means signature was completely validated,
832*5c51f124SMoriah Waterland  *		and contents of stream checked against signature.
833*5c51f124SMoriah Waterland  */
834*5c51f124SMoriah Waterland boolean_t
835*5c51f124SMoriah Waterland validate_signature(PKG_ERR *err, char *name, BIO *indata, PKCS7 *p7,
836*5c51f124SMoriah Waterland     STACK_OF(X509) *cas, url_hport_t *proxy, int nointeract)
837*5c51f124SMoriah Waterland {
838*5c51f124SMoriah Waterland 	STACK_OF(PKCS7_SIGNER_INFO) *sec_sinfos = NULL;
839*5c51f124SMoriah Waterland 
840*5c51f124SMoriah Waterland 	PKCS7_SIGNER_INFO	*signer = NULL;
841*5c51f124SMoriah Waterland 	X509_STORE		*sec_truststore = NULL;
842*5c51f124SMoriah Waterland 	X509_STORE_CTX		*ctx = NULL;
843*5c51f124SMoriah Waterland 	X509			*signer_cert = NULL, *issuer = NULL;
844*5c51f124SMoriah Waterland 	STACK_OF(X509)		*chaincerts = NULL;
845*5c51f124SMoriah Waterland 	int			i, k;
846*5c51f124SMoriah Waterland 	unsigned long		errcode;
847*5c51f124SMoriah Waterland 	const char		*err_data = NULL;
848*5c51f124SMoriah Waterland 	const char		*err_reason = NULL;
849*5c51f124SMoriah Waterland 	char			*err_string;
850*5c51f124SMoriah Waterland 	int			err_flags;
851*5c51f124SMoriah Waterland 	verify_cb_data_t	verify_data;
852*5c51f124SMoriah Waterland 	char			*signer_sname;
853*5c51f124SMoriah Waterland 	char			*signer_iname;
854*5c51f124SMoriah Waterland 	PKCS7_ISSUER_AND_SERIAL	*ias;
855*5c51f124SMoriah Waterland 	boolean_t		ret = B_TRUE;
856*5c51f124SMoriah Waterland 
857*5c51f124SMoriah Waterland 	/* only support signed PKCS7 signatures */
858*5c51f124SMoriah Waterland 	if (!PKCS7_type_is_signed(p7)) {
859*5c51f124SMoriah Waterland 	    PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_WRONG_PKCS7_TYPE);
860*5c51f124SMoriah Waterland 	    ret = B_FALSE;
861*5c51f124SMoriah Waterland 	    goto cleanup;
862*5c51f124SMoriah Waterland 	}
863*5c51f124SMoriah Waterland 
864*5c51f124SMoriah Waterland 	/* initialize temporary internal trust store used for verification */
865*5c51f124SMoriah Waterland 	sec_truststore = X509_STORE_new();
866*5c51f124SMoriah Waterland 
867*5c51f124SMoriah Waterland 	for (i = 0; i < sk_X509_num(cas); i++) {
868*5c51f124SMoriah Waterland 		if (X509_STORE_add_cert(sec_truststore,
869*5c51f124SMoriah Waterland 		    sk_X509_value(cas, i)) == 0) {
870*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
871*5c51f124SMoriah Waterland 			ret = B_FALSE;
872*5c51f124SMoriah Waterland 			goto cleanup;
873*5c51f124SMoriah Waterland 		}
874*5c51f124SMoriah Waterland 	}
875*5c51f124SMoriah Waterland 
876*5c51f124SMoriah Waterland 	/* get signers from the signature */
877*5c51f124SMoriah Waterland 	if ((sec_sinfos = PKCS7_get_signer_info(p7)) == NULL) {
878*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), name);
879*5c51f124SMoriah Waterland 		ret = B_FALSE;
880*5c51f124SMoriah Waterland 		goto cleanup;
881*5c51f124SMoriah Waterland 	}
882*5c51f124SMoriah Waterland 
883*5c51f124SMoriah Waterland 	/* verify each signer found in the PKCS7 signature */
884*5c51f124SMoriah Waterland 	for (k = 0; k < sk_PKCS7_SIGNER_INFO_num(sec_sinfos); k++) {
885*5c51f124SMoriah Waterland 		signer = sk_PKCS7_SIGNER_INFO_value(sec_sinfos, k);
886*5c51f124SMoriah Waterland 		signer_cert = PKCS7_cert_from_signer_info(p7, signer);
887*5c51f124SMoriah Waterland 		signer_sname = get_subject_display_name(signer_cert);
888*5c51f124SMoriah Waterland 		signer_iname = get_issuer_display_name(signer_cert);
889*5c51f124SMoriah Waterland 
890*5c51f124SMoriah Waterland 		echo_out(nointeract, gettext(MSG_VERIFY), signer_sname);
891*5c51f124SMoriah Waterland 
892*5c51f124SMoriah Waterland 		/* find the issuer of the current cert */
893*5c51f124SMoriah Waterland 		chaincerts = p7->d.sign->cert;
894*5c51f124SMoriah Waterland 		ias = signer->issuer_and_serial;
895*5c51f124SMoriah Waterland 		issuer = X509_find_by_issuer_and_serial(chaincerts,
896*5c51f124SMoriah Waterland 		    ias->issuer, ias->serial);
897*5c51f124SMoriah Waterland 
898*5c51f124SMoriah Waterland 		/* were we not able to find the issuer cert */
899*5c51f124SMoriah Waterland 		if (issuer == NULL) {
900*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
901*5c51f124SMoriah Waterland 			    gettext(ERR_VERIFY_ISSUER),
902*5c51f124SMoriah Waterland 			    signer_iname, signer_sname);
903*5c51f124SMoriah Waterland 			ret = B_FALSE;
904*5c51f124SMoriah Waterland 			goto cleanup;
905*5c51f124SMoriah Waterland 		}
906*5c51f124SMoriah Waterland 
907*5c51f124SMoriah Waterland 		/* Lets verify */
908*5c51f124SMoriah Waterland 		if ((ctx = X509_STORE_CTX_new()) == NULL) {
909*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
910*5c51f124SMoriah Waterland 			ret = B_FALSE;
911*5c51f124SMoriah Waterland 			goto cleanup;
912*5c51f124SMoriah Waterland 		}
913*5c51f124SMoriah Waterland 		(void) X509_STORE_CTX_init(ctx, sec_truststore,
914*5c51f124SMoriah Waterland 		    issuer, chaincerts);
915*5c51f124SMoriah Waterland 		(void) X509_STORE_CTX_set_purpose(ctx,
916*5c51f124SMoriah Waterland 		    X509_PURPOSE_ANY);
917*5c51f124SMoriah Waterland 
918*5c51f124SMoriah Waterland 		/* callback will perform OCSP on certificates with OCSP data */
919*5c51f124SMoriah Waterland 		X509_STORE_CTX_set_verify_cb(ctx, web_verify);
920*5c51f124SMoriah Waterland 
921*5c51f124SMoriah Waterland 		/* pass needed data into callback through the app_data handle */
922*5c51f124SMoriah Waterland 		verify_data.proxy = proxy;
923*5c51f124SMoriah Waterland 		verify_data.cas = cas;
924*5c51f124SMoriah Waterland 		verify_data.err = err;
925*5c51f124SMoriah Waterland 		(void) X509_STORE_CTX_set_app_data(ctx, &verify_data);
926*5c51f124SMoriah Waterland 
927*5c51f124SMoriah Waterland 		/* first verify the certificate chain */
928*5c51f124SMoriah Waterland 		i = X509_verify_cert(ctx);
929*5c51f124SMoriah Waterland 		if (i <= 0 && ctx->error != X509_V_ERR_CERT_HAS_EXPIRED) {
930*5c51f124SMoriah Waterland 			signer_sname =
931*5c51f124SMoriah Waterland 			    get_subject_display_name(ctx->current_cert);
932*5c51f124SMoriah Waterland 			signer_iname =
933*5c51f124SMoriah Waterland 			    get_issuer_display_name(ctx->current_cert);
934*5c51f124SMoriah Waterland 			/* if the verify context holds an error, print it */
935*5c51f124SMoriah Waterland 			if (ctx->error != X509_V_OK) {
936*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_VERIFY,
937*5c51f124SMoriah Waterland 				    gettext(ERR_VERIFY_SIG), signer_sname,
938*5c51f124SMoriah Waterland 				    signer_iname,
939*5c51f124SMoriah Waterland 			    (char *)X509_verify_cert_error_string(ctx->error));
940*5c51f124SMoriah Waterland 			} else {
941*5c51f124SMoriah Waterland 				/* some other error.  print them all. */
942*5c51f124SMoriah Waterland 				while ((errcode = ERR_get_error_line_data(NULL,
943*5c51f124SMoriah Waterland 				    NULL, &err_data, &err_flags)) != 0) {
944*5c51f124SMoriah Waterland 					err_reason =
945*5c51f124SMoriah Waterland 					    ERR_reason_error_string(errcode);
946*5c51f124SMoriah Waterland 					if (err_reason == NULL) {
947*5c51f124SMoriah Waterland 						err_reason =
948*5c51f124SMoriah Waterland 						    gettext(ERR_SIG_INT);
949*5c51f124SMoriah Waterland 					}
950*5c51f124SMoriah Waterland 
951*5c51f124SMoriah Waterland 					if (!(err_flags & ERR_TXT_STRING)) {
952*5c51f124SMoriah Waterland 						err_data =
953*5c51f124SMoriah Waterland 						    gettext(ERR_SIG_INT);
954*5c51f124SMoriah Waterland 					}
955*5c51f124SMoriah Waterland 					err_string =
956*5c51f124SMoriah Waterland 					    xmalloc(strlen(err_reason) +
957*5c51f124SMoriah Waterland 						strlen(err_data) + 3);
958*5c51f124SMoriah Waterland 					(void) sprintf(err_string, "%s: %s",
959*5c51f124SMoriah Waterland 					    err_reason, err_data);
960*5c51f124SMoriah Waterland 					pkgerr_add(err, PKGERR_VERIFY,
961*5c51f124SMoriah Waterland 					    gettext(ERR_VERIFY_SIG),
962*5c51f124SMoriah Waterland 					    signer_sname, signer_iname,
963*5c51f124SMoriah Waterland 					    err_string);
964*5c51f124SMoriah Waterland 					free(err_string);
965*5c51f124SMoriah Waterland 				}
966*5c51f124SMoriah Waterland 			}
967*5c51f124SMoriah Waterland 			ret = B_FALSE;
968*5c51f124SMoriah Waterland 			goto cleanup;
969*5c51f124SMoriah Waterland 		}
970*5c51f124SMoriah Waterland 
971*5c51f124SMoriah Waterland 		/* now verify the signature */
972*5c51f124SMoriah Waterland 		i = PKCS7_signatureVerify(indata, p7, signer, issuer);
973*5c51f124SMoriah Waterland 
974*5c51f124SMoriah Waterland 		if (i <= 0) {
975*5c51f124SMoriah Waterland 			/* print out any OpenSSL-specific errors */
976*5c51f124SMoriah Waterland 			signer_sname =
977*5c51f124SMoriah Waterland 			    get_subject_display_name(ctx->current_cert);
978*5c51f124SMoriah Waterland 			signer_iname =
979*5c51f124SMoriah Waterland 			    get_subject_display_name(ctx->current_cert);
980*5c51f124SMoriah Waterland 			while ((errcode = ERR_get_error_line_data(NULL,
981*5c51f124SMoriah Waterland 			    NULL, &err_data, &err_flags)) != 0) {
982*5c51f124SMoriah Waterland 				err_reason =
983*5c51f124SMoriah Waterland 				    ERR_reason_error_string(errcode);
984*5c51f124SMoriah Waterland 				if (err_reason == NULL) {
985*5c51f124SMoriah Waterland 					err_reason =
986*5c51f124SMoriah Waterland 					    gettext(ERR_SIG_INT);
987*5c51f124SMoriah Waterland 				}
988*5c51f124SMoriah Waterland 
989*5c51f124SMoriah Waterland 				if (!(err_flags & ERR_TXT_STRING)) {
990*5c51f124SMoriah Waterland 					err_data =
991*5c51f124SMoriah Waterland 					    gettext(ERR_SIG_INT);
992*5c51f124SMoriah Waterland 				}
993*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_VERIFY,
994*5c51f124SMoriah Waterland 				    gettext(ERR_VERIFY_SIG), signer_sname,
995*5c51f124SMoriah Waterland 				    signer_iname, err_reason);
996*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_VERIFY,
997*5c51f124SMoriah Waterland 				    gettext(ERR_VERIFY_SIG), signer_sname,
998*5c51f124SMoriah Waterland 				    signer_iname, err_data);
999*5c51f124SMoriah Waterland 			}
1000*5c51f124SMoriah Waterland 			ret = B_FALSE;
1001*5c51f124SMoriah Waterland 			goto cleanup;
1002*5c51f124SMoriah Waterland 		}
1003*5c51f124SMoriah Waterland 
1004*5c51f124SMoriah Waterland 		echo_out(nointeract, gettext(MSG_VERIFY_OK), signer_sname);
1005*5c51f124SMoriah Waterland 	}
1006*5c51f124SMoriah Waterland 
1007*5c51f124SMoriah Waterland 	/* signature(s) verified successfully */
1008*5c51f124SMoriah Waterland cleanup:
1009*5c51f124SMoriah Waterland 	if (ctx)
1010*5c51f124SMoriah Waterland 		X509_STORE_CTX_cleanup(ctx);
1011*5c51f124SMoriah Waterland 	return (ret);
1012*5c51f124SMoriah Waterland }
1013*5c51f124SMoriah Waterland 
1014*5c51f124SMoriah Waterland /*
1015*5c51f124SMoriah Waterland  * Name:		web_verify
1016*5c51f124SMoriah Waterland  * Description:	Callback used by PKCS7_dataVerify when
1017*5c51f124SMoriah Waterland  *		verifying a certificate chain.
1018*5c51f124SMoriah Waterland  *
1019*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
1020*5c51f124SMoriah Waterland  *     		ctx - The context handle of the current verification operation
1021*5c51f124SMoriah Waterland  *
1022*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
1023*5c51f124SMoriah Waterland  *		if it's '0' (not OK) we simply return it, since the
1024*5c51f124SMoriah Waterland  *		verification operation has already determined that the
1025*5c51f124SMoriah Waterland  *		cert is invalid.  if 'ok' is non-zero, then we do our
1026*5c51f124SMoriah Waterland  *		checks, and return 0 or 1 based on if the cert is
1027*5c51f124SMoriah Waterland  *		invalid or valid.
1028*5c51f124SMoriah Waterland  */
1029*5c51f124SMoriah Waterland static int
1030*5c51f124SMoriah Waterland web_verify(int ok, X509_STORE_CTX *ctx)
1031*5c51f124SMoriah Waterland {
1032*5c51f124SMoriah Waterland 	X509	*curr_cert;
1033*5c51f124SMoriah Waterland 	X509	*curr_issuer;
1034*5c51f124SMoriah Waterland 	char	*uri;
1035*5c51f124SMoriah Waterland 	url_hport_t	*proxy;
1036*5c51f124SMoriah Waterland 	PKG_ERR	*err = NULL;
1037*5c51f124SMoriah Waterland 	STACK_OF(X509) *cas;
1038*5c51f124SMoriah Waterland 	if (!ok) {
1039*5c51f124SMoriah Waterland 		/* don't override a verify failure */
1040*5c51f124SMoriah Waterland 		return (ok);
1041*5c51f124SMoriah Waterland 	}
1042*5c51f124SMoriah Waterland 
1043*5c51f124SMoriah Waterland 
1044*5c51f124SMoriah Waterland 	/* get app data supplied through callback context */
1045*5c51f124SMoriah Waterland 	err = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->err;
1046*5c51f124SMoriah Waterland 	proxy = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->proxy;
1047*5c51f124SMoriah Waterland 	cas = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->cas;
1048*5c51f124SMoriah Waterland 
1049*5c51f124SMoriah Waterland 	/* Check revocation status */
1050*5c51f124SMoriah Waterland 	curr_cert = X509_STORE_CTX_get_current_cert(ctx);
1051*5c51f124SMoriah Waterland 
1052*5c51f124SMoriah Waterland 	/* this shouldn't happen */
1053*5c51f124SMoriah Waterland 	if (curr_cert == NULL) {
1054*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
1055*5c51f124SMoriah Waterland 		    __FILE__, __LINE__);
1056*5c51f124SMoriah Waterland 		return (0);
1057*5c51f124SMoriah Waterland 	}
1058*5c51f124SMoriah Waterland 
1059*5c51f124SMoriah Waterland 	/* don't perform OCSP unless cert has required OCSP extensions */
1060*5c51f124SMoriah Waterland 	if (get_ocsp_uri(curr_cert, &uri)) {
1061*5c51f124SMoriah Waterland 		if (get_issuer(&curr_issuer, ctx, curr_cert) <= 0) {
1062*5c51f124SMoriah Waterland 			/* no issuer! */
1063*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_INTERNAL,
1064*5c51f124SMoriah Waterland 			    gettext(ERR_PKG_INTERNAL),
1065*5c51f124SMoriah Waterland 			    __FILE__, __LINE__);
1066*5c51f124SMoriah Waterland 			return (0);
1067*5c51f124SMoriah Waterland 		}
1068*5c51f124SMoriah Waterland 
1069*5c51f124SMoriah Waterland 		/*
1070*5c51f124SMoriah Waterland 		 * ok we have the current cert
1071*5c51f124SMoriah Waterland 		 * and its issuer.  Do the OCSP check
1072*5c51f124SMoriah Waterland 		 */
1073*5c51f124SMoriah Waterland 
1074*5c51f124SMoriah Waterland 		/*
1075*5c51f124SMoriah Waterland 		 * OCSP extensions are, by, RFC 2459, never critical
1076*5c51f124SMoriah Waterland 		 * extensions, therefore, we only fail if we were able
1077*5c51f124SMoriah Waterland 		 * to explicitly contact an OCSP responder, and that
1078*5c51f124SMoriah Waterland 		 * responder did not indicate the cert was valid.  We
1079*5c51f124SMoriah Waterland 		 * also fail if user-supplied data could not be parsed
1080*5c51f124SMoriah Waterland 		 * or we run out of memory.  We succeeed for "soft"
1081*5c51f124SMoriah Waterland 		 * failures, such as not being able to connect to the
1082*5c51f124SMoriah Waterland 		 * OCSP responder, or trying to use if the OCSP URI
1083*5c51f124SMoriah Waterland 		 * indicates SSL must be used (which we do not
1084*5c51f124SMoriah Waterland 		 * support)
1085*5c51f124SMoriah Waterland 		 */
1086*5c51f124SMoriah Waterland 		switch (ocsp_verify(err, curr_cert, curr_issuer,
1087*5c51f124SMoriah Waterland 		    uri, proxy, cas)) {
1088*5c51f124SMoriah Waterland 		case OCSPMem:		/* Ran out of memory */
1089*5c51f124SMoriah Waterland 		case OCSPInternal:	/* Some internal error */
1090*5c51f124SMoriah Waterland 		case OCSPVerify:	/* OCSP responder indicated fail */
1091*5c51f124SMoriah Waterland 			return (0);
1092*5c51f124SMoriah Waterland 		}
1093*5c51f124SMoriah Waterland 		/* all other cases are success, or soft failures */
1094*5c51f124SMoriah Waterland 		pkgerr_clear(err);
1095*5c51f124SMoriah Waterland 	}
1096*5c51f124SMoriah Waterland 
1097*5c51f124SMoriah Waterland 	return (ok);
1098*5c51f124SMoriah Waterland }
1099*5c51f124SMoriah Waterland 
1100*5c51f124SMoriah Waterland /*
1101*5c51f124SMoriah Waterland  * Name:		get_time_string
1102*5c51f124SMoriah Waterland  * Description:	Generates a human-readable string from an ASN1_GENERALIZED_TIME
1103*5c51f124SMoriah Waterland  *
1104*5c51f124SMoriah Waterland  * Arguments:	intime - The time to convert
1105*5c51f124SMoriah Waterland  *
1106*5c51f124SMoriah Waterland  * Returns :	A pointer to a static string representing the passed-in time.
1107*5c51f124SMoriah Waterland  */
1108*5c51f124SMoriah Waterland static char
1109*5c51f124SMoriah Waterland *get_time_string(ASN1_GENERALIZEDTIME *intime)
1110*5c51f124SMoriah Waterland {
1111*5c51f124SMoriah Waterland 
1112*5c51f124SMoriah Waterland 	static char	time[ATTR_MAX];
1113*5c51f124SMoriah Waterland 	BIO		*mem;
1114*5c51f124SMoriah Waterland 	char	*p;
1115*5c51f124SMoriah Waterland 
1116*5c51f124SMoriah Waterland 	if (intime == NULL) {
1117*5c51f124SMoriah Waterland 		return (NULL);
1118*5c51f124SMoriah Waterland 	}
1119*5c51f124SMoriah Waterland 	if ((mem = BIO_new(BIO_s_mem())) == NULL) {
1120*5c51f124SMoriah Waterland 		return (NULL);
1121*5c51f124SMoriah Waterland 	}
1122*5c51f124SMoriah Waterland 
1123*5c51f124SMoriah Waterland 	if (ASN1_GENERALIZEDTIME_print(mem, intime) == 0) {
1124*5c51f124SMoriah Waterland 		(void) BIO_free(mem);
1125*5c51f124SMoriah Waterland 		return (NULL);
1126*5c51f124SMoriah Waterland 	}
1127*5c51f124SMoriah Waterland 
1128*5c51f124SMoriah Waterland 	if (BIO_gets(mem, time, ATTR_MAX) <= 0) {
1129*5c51f124SMoriah Waterland 		(void) BIO_free(mem);
1130*5c51f124SMoriah Waterland 		return (NULL);
1131*5c51f124SMoriah Waterland 	}
1132*5c51f124SMoriah Waterland 
1133*5c51f124SMoriah Waterland 	(void) BIO_free(mem);
1134*5c51f124SMoriah Waterland 
1135*5c51f124SMoriah Waterland 	/* trim the end of the string */
1136*5c51f124SMoriah Waterland 	for (p = time + strlen(time) - 1; isspace(*p); p--) {
1137*5c51f124SMoriah Waterland 		*p = '\0';
1138*5c51f124SMoriah Waterland 	}
1139*5c51f124SMoriah Waterland 
1140*5c51f124SMoriah Waterland 	return (time);
1141*5c51f124SMoriah Waterland }
1142*5c51f124SMoriah Waterland 
1143*5c51f124SMoriah Waterland /*
1144*5c51f124SMoriah Waterland  * Name:		get_ocsp_uri
1145*5c51f124SMoriah Waterland  * Description:	Examines an X509 certificate and retrieves the embedded
1146*5c51f124SMoriah Waterland  *		OCSP Responder URI if one exists.
1147*5c51f124SMoriah Waterland  *
1148*5c51f124SMoriah Waterland  * Arguments:	cert - The cert to inspect
1149*5c51f124SMoriah Waterland  *     		uri - pointer where the newly-allocated URI is placed, if found
1150*5c51f124SMoriah Waterland  *
1151*5c51f124SMoriah Waterland  * Returns :	Success if the URI was found.  Appropriate status otherwise.
1152*5c51f124SMoriah Waterland  */
1153*5c51f124SMoriah Waterland static boolean_t
1154*5c51f124SMoriah Waterland get_ocsp_uri(X509 *cert, char **uri)
1155*5c51f124SMoriah Waterland {
1156*5c51f124SMoriah Waterland 	AUTHORITY_INFO_ACCESS		*aia;
1157*5c51f124SMoriah Waterland 	ACCESS_DESCRIPTION		*ad;
1158*5c51f124SMoriah Waterland 	int				i;
1159*5c51f124SMoriah Waterland 
1160*5c51f124SMoriah Waterland 	if (getenv("PKGWEB_TEST_OCSP")) {
1161*5c51f124SMoriah Waterland 		*uri = xstrdup(getenv("PKGWEB_TEST_OCSP"));
1162*5c51f124SMoriah Waterland 		return (B_TRUE);
1163*5c51f124SMoriah Waterland 	}
1164*5c51f124SMoriah Waterland 
1165*5c51f124SMoriah Waterland 	/* get the X509v3 extension holding the OCSP URI */
1166*5c51f124SMoriah Waterland 	if ((aia = X509_get_ext_d2i(cert, NID_info_access,
1167*5c51f124SMoriah Waterland 	    NULL, NULL)) != NULL) {
1168*5c51f124SMoriah Waterland 		for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
1169*5c51f124SMoriah Waterland 			ad = sk_ACCESS_DESCRIPTION_value(aia, i);
1170*5c51f124SMoriah Waterland 			if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) {
1171*5c51f124SMoriah Waterland 				if (ad->location->type == GEN_URI) {
1172*5c51f124SMoriah Waterland 					*uri =
1173*5c51f124SMoriah Waterland 		    xstrdup((char *)ASN1_STRING_data(ad->location->d.ia5));
1174*5c51f124SMoriah Waterland 					return (B_TRUE);
1175*5c51f124SMoriah Waterland 				}
1176*5c51f124SMoriah Waterland 			}
1177*5c51f124SMoriah Waterland 		}
1178*5c51f124SMoriah Waterland 	}
1179*5c51f124SMoriah Waterland 
1180*5c51f124SMoriah Waterland 	/* no URI was found */
1181*5c51f124SMoriah Waterland 	return (B_FALSE);
1182*5c51f124SMoriah Waterland }
1183*5c51f124SMoriah Waterland 
1184*5c51f124SMoriah Waterland /*
1185*5c51f124SMoriah Waterland  * Name:		ocsp_verify
1186*5c51f124SMoriah Waterland  * Description:	Attempts to contact an OCSP Responder and ascertain the validity
1187*5c51f124SMoriah Waterland  *		of an X509 certificate.
1188*5c51f124SMoriah Waterland  *
1189*5c51f124SMoriah Waterland  * Arguments:	err - Error object to add error messages to
1190*5c51f124SMoriah Waterland  *		cert - The cert to validate
1191*5c51f124SMoriah Waterland  *		issuer - The certificate of the issuer of 'cert'
1192*5c51f124SMoriah Waterland  *     		uri - The OCSP Responder URI
1193*5c51f124SMoriah Waterland  *		cas - The trusted CA certificates used to verify the
1194*5c51f124SMoriah Waterland  *		signed OCSP response
1195*5c51f124SMoriah Waterland  * Returns :	Success - The OCSP Responder reported a 'good'
1196*5c51f124SMoriah Waterland  *		status for the cert otherwise, appropriate
1197*5c51f124SMoriah Waterland  *		error is returned.
1198*5c51f124SMoriah Waterland  */
1199*5c51f124SMoriah Waterland static OCSPStatus
1200*5c51f124SMoriah Waterland ocsp_verify(PKG_ERR *err, X509 *cert, X509 *issuer,
1201*5c51f124SMoriah Waterland     char *uri, url_hport_t *proxy, STACK_OF(X509) *cas)
1202*5c51f124SMoriah Waterland {
1203*5c51f124SMoriah Waterland 	OCSP_CERTID		*id;
1204*5c51f124SMoriah Waterland 	OCSP_REQUEST		*req;
1205*5c51f124SMoriah Waterland 	OCSP_RESPONSE		*resp;
1206*5c51f124SMoriah Waterland 	OCSP_BASICRESP		*bs;
1207*5c51f124SMoriah Waterland 	BIO			*cbio, *mem;
1208*5c51f124SMoriah Waterland 	char			ocspbuf[OCSP_BUFSIZ];
1209*5c51f124SMoriah Waterland 	char *host = NULL, *portstr = NULL, *path = "/", *p, *q, *r;
1210*5c51f124SMoriah Waterland 	int		port, status, reason;
1211*5c51f124SMoriah Waterland 	int	len, retval, respcode, use_ssl = 0;
1212*5c51f124SMoriah Waterland 	ASN1_GENERALIZEDTIME	*rev, *thisupd, *nextupd;
1213*5c51f124SMoriah Waterland 	char	*subjname;
1214*5c51f124SMoriah Waterland 	time_t			currtime;
1215*5c51f124SMoriah Waterland 	char			currtimestr[ATTR_MAX];
1216*5c51f124SMoriah Waterland 	unsigned long		errcode;
1217*5c51f124SMoriah Waterland 	const char		*err_reason;
1218*5c51f124SMoriah Waterland 
1219*5c51f124SMoriah Waterland 	subjname = get_subject_display_name(cert);
1220*5c51f124SMoriah Waterland 
1221*5c51f124SMoriah Waterland 	/* parse the URI into its constituent parts */
1222*5c51f124SMoriah Waterland 	if (OCSP_parse_url(uri, &host, &portstr, &path, &use_ssl) == NULL) {
1223*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_PARSE), uri);
1224*5c51f124SMoriah Waterland 		return (OCSPParse);
1225*5c51f124SMoriah Waterland 	}
1226*5c51f124SMoriah Waterland 
1227*5c51f124SMoriah Waterland 	/* we don't currently support SSL-based OCSP Responders */
1228*5c51f124SMoriah Waterland 	if (use_ssl) {
1229*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_UNSUP), uri);
1230*5c51f124SMoriah Waterland 		return (OCSPUnsupported);
1231*5c51f124SMoriah Waterland 	}
1232*5c51f124SMoriah Waterland 
1233*5c51f124SMoriah Waterland 	/* default port if none specified */
1234*5c51f124SMoriah Waterland 	if (portstr == NULL) {
1235*5c51f124SMoriah Waterland 		port = (int)URL_DFLT_SRVR_PORT;
1236*5c51f124SMoriah Waterland 	} else {
1237*5c51f124SMoriah Waterland 		port = (int)strtoul(portstr, &r, 10);
1238*5c51f124SMoriah Waterland 		if (*r != '\0') {
1239*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
1240*5c51f124SMoriah Waterland 			    gettext(ERR_OCSP_PARSE), uri);
1241*5c51f124SMoriah Waterland 			return (OCSPParse);
1242*5c51f124SMoriah Waterland 		}
1243*5c51f124SMoriah Waterland 	}
1244*5c51f124SMoriah Waterland 
1245*5c51f124SMoriah Waterland 	/* allocate new request structure */
1246*5c51f124SMoriah Waterland 	if ((req = OCSP_REQUEST_new()) == NULL) {
1247*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
1248*5c51f124SMoriah Waterland 		return (OCSPMem);
1249*5c51f124SMoriah Waterland 	}
1250*5c51f124SMoriah Waterland 
1251*5c51f124SMoriah Waterland 	/* convert cert and issuer fields into OCSP request data */
1252*5c51f124SMoriah Waterland 	if ((id = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) {
1253*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
1254*5c51f124SMoriah Waterland 		    __FILE__, __LINE__);
1255*5c51f124SMoriah Waterland 		return (OCSPInternal);
1256*5c51f124SMoriah Waterland 	}
1257*5c51f124SMoriah Waterland 
1258*5c51f124SMoriah Waterland 	/* fill out request structure with request data */
1259*5c51f124SMoriah Waterland 	if ((OCSP_request_add0_id(req, id)) == NULL) {
1260*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
1261*5c51f124SMoriah Waterland 		    __FILE__, __LINE__);
1262*5c51f124SMoriah Waterland 		return (OCSPInternal);
1263*5c51f124SMoriah Waterland 	}
1264*5c51f124SMoriah Waterland 
1265*5c51f124SMoriah Waterland 	/* add nonce */
1266*5c51f124SMoriah Waterland 	OCSP_request_add1_nonce(req, NULL, -1);
1267*5c51f124SMoriah Waterland 
1268*5c51f124SMoriah Waterland 	/* connect to host, or proxy */
1269*5c51f124SMoriah Waterland 	if (proxy != NULL) {
1270*5c51f124SMoriah Waterland 		if ((cbio = BIO_new_connect(proxy->hostname)) == NULL) {
1271*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
1272*5c51f124SMoriah Waterland 			return (OCSPMem);
1273*5c51f124SMoriah Waterland 		}
1274*5c51f124SMoriah Waterland 
1275*5c51f124SMoriah Waterland 		/*
1276*5c51f124SMoriah Waterland 		 * BIO_set_conn_int_port takes an int *, so let's give it one
1277*5c51f124SMoriah Waterland 		 * rather than an ushort_t *
1278*5c51f124SMoriah Waterland 		 */
1279*5c51f124SMoriah Waterland 		port = proxy->port;
1280*5c51f124SMoriah Waterland 		(void) BIO_set_conn_int_port(cbio, &port);
1281*5c51f124SMoriah Waterland 		if (BIO_do_connect(cbio) <= 0) {
1282*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
1283*5c51f124SMoriah Waterland 			    gettext(ERR_OCSP_CONNECT),
1284*5c51f124SMoriah Waterland 			    proxy->hostname, port);
1285*5c51f124SMoriah Waterland 			return (OCSPConnect);
1286*5c51f124SMoriah Waterland 		}
1287*5c51f124SMoriah Waterland 	} else {
1288*5c51f124SMoriah Waterland 		if ((cbio = BIO_new_connect(host)) == NULL) {
1289*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
1290*5c51f124SMoriah Waterland 			return (OCSPMem);
1291*5c51f124SMoriah Waterland 		}
1292*5c51f124SMoriah Waterland 
1293*5c51f124SMoriah Waterland 		(void) BIO_set_conn_int_port(cbio, &port);
1294*5c51f124SMoriah Waterland 		if (BIO_do_connect(cbio) <= 0) {
1295*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
1296*5c51f124SMoriah Waterland 			    gettext(ERR_OCSP_CONNECT),
1297*5c51f124SMoriah Waterland 			    host, port);
1298*5c51f124SMoriah Waterland 			return (OCSPConnect);
1299*5c51f124SMoriah Waterland 		}
1300*5c51f124SMoriah Waterland 	}
1301*5c51f124SMoriah Waterland 
1302*5c51f124SMoriah Waterland 	/* calculate length of binary request data */
1303*5c51f124SMoriah Waterland 	len = i2d_OCSP_REQUEST(req, NULL);
1304*5c51f124SMoriah Waterland 
1305*5c51f124SMoriah Waterland 	/* send the request headers */
1306*5c51f124SMoriah Waterland 	if (proxy != NULL) {
1307*5c51f124SMoriah Waterland 		retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, uri, len);
1308*5c51f124SMoriah Waterland 	} else {
1309*5c51f124SMoriah Waterland 		retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, path, len);
1310*5c51f124SMoriah Waterland 	}
1311*5c51f124SMoriah Waterland 
1312*5c51f124SMoriah Waterland 	if (retval <= 0) {
1313*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
1314*5c51f124SMoriah Waterland 		return (OCSPRequest);
1315*5c51f124SMoriah Waterland 	}
1316*5c51f124SMoriah Waterland 
1317*5c51f124SMoriah Waterland 	/* send the request binary data */
1318*5c51f124SMoriah Waterland 	if (i2d_OCSP_REQUEST_bio(cbio, req) <= 0) {
1319*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
1320*5c51f124SMoriah Waterland 		return (OCSPRequest);
1321*5c51f124SMoriah Waterland 	}
1322*5c51f124SMoriah Waterland 
1323*5c51f124SMoriah Waterland 	/*
1324*5c51f124SMoriah Waterland 	 * read the response into a memory BIO, so we can 'gets'
1325*5c51f124SMoriah Waterland 	 * (socket bio's don't support BIO_gets)
1326*5c51f124SMoriah Waterland 	 */
1327*5c51f124SMoriah Waterland 	if ((mem = BIO_new(BIO_s_mem())) == NULL) {
1328*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
1329*5c51f124SMoriah Waterland 		return (OCSPMem);
1330*5c51f124SMoriah Waterland 	}
1331*5c51f124SMoriah Waterland 
1332*5c51f124SMoriah Waterland 	while ((len = BIO_read(cbio, ocspbuf, OCSP_BUFSIZ))) {
1333*5c51f124SMoriah Waterland 		if (len < 0) {
1334*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
1335*5c51f124SMoriah Waterland 			    gettext(ERR_OCSP_READ), host);
1336*5c51f124SMoriah Waterland 			return (OCSPRequest);
1337*5c51f124SMoriah Waterland 		}
1338*5c51f124SMoriah Waterland 		if (BIO_write(mem, ocspbuf, len) != len) {
1339*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
1340*5c51f124SMoriah Waterland 			return (OCSPMem);
1341*5c51f124SMoriah Waterland 		}
1342*5c51f124SMoriah Waterland 	}
1343*5c51f124SMoriah Waterland 
1344*5c51f124SMoriah Waterland 	/* now get the first line of the response */
1345*5c51f124SMoriah Waterland 	if (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) <= 0) {
1346*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_PARSE));
1347*5c51f124SMoriah Waterland 		return (OCSPRequest);
1348*5c51f124SMoriah Waterland 	}
1349*5c51f124SMoriah Waterland 
1350*5c51f124SMoriah Waterland 	/* parse the header response */
1351*5c51f124SMoriah Waterland 	/* it should look like "HTTP/x.x 200 OK" */
1352*5c51f124SMoriah Waterland 
1353*5c51f124SMoriah Waterland 	/* skip past the protocol info */
1354*5c51f124SMoriah Waterland 	for (p = ocspbuf; (*p != '\0') && !isspace(*p); p++)
1355*5c51f124SMoriah Waterland 		continue;
1356*5c51f124SMoriah Waterland 
1357*5c51f124SMoriah Waterland 	/* skip past whitespace betwen protocol and start of response code */
1358*5c51f124SMoriah Waterland 	while ((*p != '\0') && isspace(*p)) {
1359*5c51f124SMoriah Waterland 		p++;
1360*5c51f124SMoriah Waterland 	}
1361*5c51f124SMoriah Waterland 
1362*5c51f124SMoriah Waterland 	if (*p == '\0') {
1363*5c51f124SMoriah Waterland 		/* premature end */
1364*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1365*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
1366*5c51f124SMoriah Waterland 		return (OCSPRequest);
1367*5c51f124SMoriah Waterland 	}
1368*5c51f124SMoriah Waterland 
1369*5c51f124SMoriah Waterland 	/* find end of response code */
1370*5c51f124SMoriah Waterland 	for (q = p; (*q != NULL) && !isspace(*q); q++)
1371*5c51f124SMoriah Waterland 		continue;
1372*5c51f124SMoriah Waterland 
1373*5c51f124SMoriah Waterland 	/* mark end of response code */
1374*5c51f124SMoriah Waterland 	*q++ = '\0';
1375*5c51f124SMoriah Waterland 
1376*5c51f124SMoriah Waterland 	/* parse response code */
1377*5c51f124SMoriah Waterland 	respcode = strtoul(p, &r, 10);
1378*5c51f124SMoriah Waterland 	if (*r != '\0') {
1379*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1380*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
1381*5c51f124SMoriah Waterland 		return (OCSPRequest);
1382*5c51f124SMoriah Waterland 	}
1383*5c51f124SMoriah Waterland 
1384*5c51f124SMoriah Waterland 	/* now find beginning of the response string */
1385*5c51f124SMoriah Waterland 	while ((*q != NULL) && isspace(*q)) {
1386*5c51f124SMoriah Waterland 		q++;
1387*5c51f124SMoriah Waterland 	}
1388*5c51f124SMoriah Waterland 
1389*5c51f124SMoriah Waterland 	/* trim whitespace from end of message */
1390*5c51f124SMoriah Waterland 	for (r = (q + strlen(q) - 1); isspace(*r); r--) {
1391*5c51f124SMoriah Waterland 		*r = '\0';
1392*5c51f124SMoriah Waterland 	}
1393*5c51f124SMoriah Waterland 
1394*5c51f124SMoriah Waterland 	/* response must be OK */
1395*5c51f124SMoriah Waterland 	if (respcode != 200) {
1396*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1397*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_RESP_NOTOK), 200,
1398*5c51f124SMoriah Waterland 		    respcode, q);
1399*5c51f124SMoriah Waterland 		return (OCSPRequest);
1400*5c51f124SMoriah Waterland 	}
1401*5c51f124SMoriah Waterland 
1402*5c51f124SMoriah Waterland 	/* read headers, looking for content-type or a blank line */
1403*5c51f124SMoriah Waterland 	while (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) > 0) {
1404*5c51f124SMoriah Waterland 
1405*5c51f124SMoriah Waterland 		/* if we get a content type, make sure it's the right type */
1406*5c51f124SMoriah Waterland 		if (ci_strneq(ocspbuf, CONTENT_TYPE_HDR,
1407*5c51f124SMoriah Waterland 		    strlen(CONTENT_TYPE_HDR))) {
1408*5c51f124SMoriah Waterland 
1409*5c51f124SMoriah Waterland 			/* look for the delimiting : */
1410*5c51f124SMoriah Waterland 			p = strchr(ocspbuf + strlen(CONTENT_TYPE_HDR), ':');
1411*5c51f124SMoriah Waterland 
1412*5c51f124SMoriah Waterland 			if (p == NULL) {
1413*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_PARSE,
1414*5c51f124SMoriah Waterland 				    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
1415*5c51f124SMoriah Waterland 				return (OCSPResponder);
1416*5c51f124SMoriah Waterland 			}
1417*5c51f124SMoriah Waterland 
1418*5c51f124SMoriah Waterland 			/* skip over ':' */
1419*5c51f124SMoriah Waterland 			p++;
1420*5c51f124SMoriah Waterland 
1421*5c51f124SMoriah Waterland 			/* find beginning of the content type */
1422*5c51f124SMoriah Waterland 			while ((*p != NULL) && isspace(*p)) {
1423*5c51f124SMoriah Waterland 				p++;
1424*5c51f124SMoriah Waterland 			}
1425*5c51f124SMoriah Waterland 
1426*5c51f124SMoriah Waterland 			if (!ci_strneq(p, CONTENT_OCSP_RESP,
1427*5c51f124SMoriah Waterland 			    strlen(CONTENT_OCSP_RESP))) {
1428*5c51f124SMoriah Waterland 				/* response is not right type */
1429*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_PARSE,
1430*5c51f124SMoriah Waterland 				    gettext(ERR_OCSP_RESP_TYPE),
1431*5c51f124SMoriah Waterland 				    p, CONTENT_OCSP_RESP);
1432*5c51f124SMoriah Waterland 				return (OCSPResponder);
1433*5c51f124SMoriah Waterland 			}
1434*5c51f124SMoriah Waterland 
1435*5c51f124SMoriah Waterland 			/* continue with next header line */
1436*5c51f124SMoriah Waterland 			continue;
1437*5c51f124SMoriah Waterland 		}
1438*5c51f124SMoriah Waterland 
1439*5c51f124SMoriah Waterland 		/* scan looking for a character */
1440*5c51f124SMoriah Waterland 		for (p = ocspbuf; (*p != '\0') && isspace(*p); p++) {
1441*5c51f124SMoriah Waterland 			continue;
1442*5c51f124SMoriah Waterland 		}
1443*5c51f124SMoriah Waterland 		/*
1444*5c51f124SMoriah Waterland 		 * if we got to the end of the line with
1445*5c51f124SMoriah Waterland 		 *  no chars, then this is a blank line
1446*5c51f124SMoriah Waterland 		 */
1447*5c51f124SMoriah Waterland 		if (*p == '\0') {
1448*5c51f124SMoriah Waterland 			break;
1449*5c51f124SMoriah Waterland 		}
1450*5c51f124SMoriah Waterland 	}
1451*5c51f124SMoriah Waterland 
1452*5c51f124SMoriah Waterland 
1453*5c51f124SMoriah Waterland 	if (*p != '\0') {
1454*5c51f124SMoriah Waterland 		/* last line was not blank */
1455*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1456*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
1457*5c51f124SMoriah Waterland 		return (OCSPResponder);
1458*5c51f124SMoriah Waterland 	}
1459*5c51f124SMoriah Waterland 
1460*5c51f124SMoriah Waterland 	/* now read in the binary response */
1461*5c51f124SMoriah Waterland 	if ((resp = d2i_OCSP_RESPONSE_bio(mem, NULL)) == NULL) {
1462*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
1463*5c51f124SMoriah Waterland 		return (OCSPResponder);
1464*5c51f124SMoriah Waterland 	}
1465*5c51f124SMoriah Waterland 
1466*5c51f124SMoriah Waterland 	/* free temp BIOs */
1467*5c51f124SMoriah Waterland 	(void) BIO_free(mem);
1468*5c51f124SMoriah Waterland 	(void) BIO_free_all(cbio);
1469*5c51f124SMoriah Waterland 	cbio = NULL;
1470*5c51f124SMoriah Waterland 
1471*5c51f124SMoriah Waterland 	/* make sure request was successful */
1472*5c51f124SMoriah Waterland 	if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
1473*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_NOTOK),
1474*5c51f124SMoriah Waterland 		    OCSP_RESPONSE_STATUS_SUCCESSFUL,
1475*5c51f124SMoriah Waterland 		    OCSP_response_status(resp),
1476*5c51f124SMoriah Waterland 		    OCSP_response_status_str(OCSP_response_status(resp)));
1477*5c51f124SMoriah Waterland 		return (OCSPResponder);
1478*5c51f124SMoriah Waterland 	}
1479*5c51f124SMoriah Waterland 
1480*5c51f124SMoriah Waterland 	/* parse binary response into internal structure */
1481*5c51f124SMoriah Waterland 	if ((bs = OCSP_response_get1_basic(resp)) == NULL) {
1482*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
1483*5c51f124SMoriah Waterland 		return (OCSPParse);
1484*5c51f124SMoriah Waterland 	}
1485*5c51f124SMoriah Waterland 
1486*5c51f124SMoriah Waterland 	/*
1487*5c51f124SMoriah Waterland 	 * From here to the end of the code, the return values
1488*5c51f124SMoriah Waterland 	 * should be hard failures
1489*5c51f124SMoriah Waterland 	 */
1490*5c51f124SMoriah Waterland 
1491*5c51f124SMoriah Waterland 	/* verify the response, warn if no nonce */
1492*5c51f124SMoriah Waterland 	if (OCSP_check_nonce(req, bs) <= 0) {
1493*5c51f124SMoriah Waterland 		logerr(pkg_gt(WRN_OCSP_RESP_NONCE));
1494*5c51f124SMoriah Waterland 	}
1495*5c51f124SMoriah Waterland 
1496*5c51f124SMoriah Waterland 	if (OCSP_basic_verify(bs, cas, NULL, OCSP_TRUSTOTHER) <= 0) {
1497*5c51f124SMoriah Waterland 		while ((errcode = ERR_get_error()) != NULL) {
1498*5c51f124SMoriah Waterland 			err_reason = ERR_reason_error_string(errcode);
1499*5c51f124SMoriah Waterland 			if (err_reason == NULL) {
1500*5c51f124SMoriah Waterland 				err_reason =
1501*5c51f124SMoriah Waterland 				    gettext(ERR_SIG_INT);
1502*5c51f124SMoriah Waterland 			}
1503*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE, (char *)err_reason);
1504*5c51f124SMoriah Waterland 		}
1505*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_VERIFY_FAIL),
1506*5c51f124SMoriah Waterland 		    uri);
1507*5c51f124SMoriah Waterland 		return (OCSPVerify);
1508*5c51f124SMoriah Waterland 	}
1509*5c51f124SMoriah Waterland 
1510*5c51f124SMoriah Waterland 	/* check the validity of our certificate */
1511*5c51f124SMoriah Waterland 	if (OCSP_resp_find_status(bs, id, &status, &reason,
1512*5c51f124SMoriah Waterland 	    &rev, &thisupd, &nextupd) == NULL) {
1513*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1514*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_VERIFY_NO_STATUS), subjname);
1515*5c51f124SMoriah Waterland 		return (OCSPVerify);
1516*5c51f124SMoriah Waterland 	}
1517*5c51f124SMoriah Waterland 
1518*5c51f124SMoriah Waterland 	if ((currtime = time(NULL)) == (time_t)-1) {
1519*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1520*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_VERIFY_NOTIME));
1521*5c51f124SMoriah Waterland 		return (OCSPVerify);
1522*5c51f124SMoriah Waterland 	}
1523*5c51f124SMoriah Waterland 
1524*5c51f124SMoriah Waterland 	(void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX);
1525*5c51f124SMoriah Waterland 
1526*5c51f124SMoriah Waterland 	/* trim end */
1527*5c51f124SMoriah Waterland 	for (r = currtimestr + strlen(currtimestr) - 1;
1528*5c51f124SMoriah Waterland 		isspace(*r); r--) {
1529*5c51f124SMoriah Waterland 		*r = '\0';
1530*5c51f124SMoriah Waterland 	}
1531*5c51f124SMoriah Waterland 
1532*5c51f124SMoriah Waterland 	if (!OCSP_check_validity(thisupd, nextupd,
1533*5c51f124SMoriah Waterland 	    OCSP_VALIDITY_PERIOD, -1)) {
1534*5c51f124SMoriah Waterland 		if (nextupd != NULL) {
1535*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
1536*5c51f124SMoriah Waterland 			    gettext(ERR_OCSP_VERIFY_VALIDITY),
1537*5c51f124SMoriah Waterland 			    get_time_string(thisupd), get_time_string(nextupd),
1538*5c51f124SMoriah Waterland 			    currtimestr);
1539*5c51f124SMoriah Waterland 		} else {
1540*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
1541*5c51f124SMoriah Waterland 			    gettext(ERR_OCSP_VERIFY_VALIDITY),
1542*5c51f124SMoriah Waterland 			    get_time_string(thisupd),
1543*5c51f124SMoriah Waterland 			    currtimestr);
1544*5c51f124SMoriah Waterland 		}
1545*5c51f124SMoriah Waterland 		return (OCSPVerify);
1546*5c51f124SMoriah Waterland 	}
1547*5c51f124SMoriah Waterland 
1548*5c51f124SMoriah Waterland 	if (status != V_OCSP_CERTSTATUS_GOOD) {
1549*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_PARSE,
1550*5c51f124SMoriah Waterland 		    gettext(ERR_OCSP_VERIFY_STATUS), subjname,
1551*5c51f124SMoriah Waterland 		    OCSP_cert_status_str(status));
1552*5c51f124SMoriah Waterland 		return (OCSPVerify);
1553*5c51f124SMoriah Waterland 	}
1554*5c51f124SMoriah Waterland 
1555*5c51f124SMoriah Waterland 	/* everythign checks out */
1556*5c51f124SMoriah Waterland 	return (OCSPSuccess);
1557*5c51f124SMoriah Waterland }
1558*5c51f124SMoriah Waterland 
1559*5c51f124SMoriah Waterland /*
1560*5c51f124SMoriah Waterland  * Name:		get_issuer
1561*5c51f124SMoriah Waterland  * Description:	Attempts to find the issuing certificate for a given certificate
1562*5c51f124SMoriah Waterland  *		This will look in both the list of trusted certificates found in
1563*5c51f124SMoriah Waterland  *		the X509_STORE_CTX structure, as well as the list of untrusted
1564*5c51f124SMoriah Waterland  *		chain certificates found in the X509_STORE_CTX structure.
1565*5c51f124SMoriah Waterland  * Arguments:
1566*5c51f124SMoriah Waterland  *		issuer - The resulting issuer cert is placed here, if found
1567*5c51f124SMoriah Waterland  *		ctx - The current verification context
1568*5c51f124SMoriah Waterland  *		x - The certificate whose issuer we are looking for
1569*5c51f124SMoriah Waterland  * Returns :	Success - The issuer cert was found and placed in *issuer.
1570*5c51f124SMoriah Waterland  *		otherwise, appropriate error is returned.
1571*5c51f124SMoriah Waterland  */
1572*5c51f124SMoriah Waterland static int
1573*5c51f124SMoriah Waterland get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
1574*5c51f124SMoriah Waterland {
1575*5c51f124SMoriah Waterland 	int		i, ok;
1576*5c51f124SMoriah Waterland 
1577*5c51f124SMoriah Waterland 	/*
1578*5c51f124SMoriah Waterland 	 * first look in the list of trusted
1579*5c51f124SMoriah Waterland 	 * certs, using the context's method to do so
1580*5c51f124SMoriah Waterland 	 */
1581*5c51f124SMoriah Waterland 	if ((ok = ctx->get_issuer(issuer, ctx, x)) > 0) {
1582*5c51f124SMoriah Waterland 		return (ok);
1583*5c51f124SMoriah Waterland 	}
1584*5c51f124SMoriah Waterland 
1585*5c51f124SMoriah Waterland 	if (ctx->untrusted != NULL) {
1586*5c51f124SMoriah Waterland 		/* didn't find it in trusted certs, look through untrusted */
1587*5c51f124SMoriah Waterland 		for (i = 0; i < sk_X509_num(ctx->untrusted); i++) {
1588*5c51f124SMoriah Waterland 			if (X509_check_issued(sk_X509_value(ctx->untrusted, i),
1589*5c51f124SMoriah Waterland 			    x) == X509_V_OK) {
1590*5c51f124SMoriah Waterland 				*issuer = sk_X509_value(ctx->untrusted, i);
1591*5c51f124SMoriah Waterland 				return (1);
1592*5c51f124SMoriah Waterland 			}
1593*5c51f124SMoriah Waterland 		}
1594*5c51f124SMoriah Waterland 	}
1595*5c51f124SMoriah Waterland 	*issuer = NULL;
1596*5c51f124SMoriah Waterland 	return (0);
1597*5c51f124SMoriah Waterland }
1598*5c51f124SMoriah Waterland 
1599*5c51f124SMoriah Waterland /*
1600*5c51f124SMoriah Waterland  * Name:		parse_url_proxy
1601*5c51f124SMoriah Waterland  * Description:	Parses URL and optional proxy specification, populates static
1602*5c51f124SMoriah Waterland  *		'ps' structure
1603*5c51f124SMoriah Waterland  *
1604*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
1605*5c51f124SMoriah Waterland  *		url - URL to parse
1606*5c51f124SMoriah Waterland  *		proxy - proxy to parse, or NULL for no proxy
1607*5c51f124SMoriah Waterland  *		proxy_port - Default proxy port to use if no proxy
1608*5c51f124SMoriah Waterland  *		port specified in 'proxy'
1609*5c51f124SMoriah Waterland  *
1610*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
1611*5c51f124SMoriah Waterland  *		on success, 'ps->url' and 'ps->proxy' are populated
1612*5c51f124SMoriah Waterland  *		with parsed data.
1613*5c51f124SMoriah Waterland  */
1614*5c51f124SMoriah Waterland static boolean_t
1615*5c51f124SMoriah Waterland parse_url_proxy(PKG_ERR *err, char *url, char *proxy, ushort_t proxy_port)
1616*5c51f124SMoriah Waterland {
1617*5c51f124SMoriah Waterland 	boolean_t ret = B_TRUE;
1618*5c51f124SMoriah Waterland 	if (!path_valid(url)) {
1619*5c51f124SMoriah Waterland 		ret = B_FALSE;
1620*5c51f124SMoriah Waterland 		goto cleanup;
1621*5c51f124SMoriah Waterland 	}
1622*5c51f124SMoriah Waterland 
1623*5c51f124SMoriah Waterland 	if (url_parse(url, &ps->url) != URL_PARSE_SUCCESS) {
1624*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_PARSE_URL), url);
1625*5c51f124SMoriah Waterland 		ret = B_FALSE;
1626*5c51f124SMoriah Waterland 		goto cleanup;
1627*5c51f124SMoriah Waterland 	}
1628*5c51f124SMoriah Waterland 
1629*5c51f124SMoriah Waterland 	if (proxy != NULL) {
1630*5c51f124SMoriah Waterland 		if (url_parse_hostport(proxy, &ps->proxy, proxy_port)
1631*5c51f124SMoriah Waterland 				!= URL_PARSE_SUCCESS) {
1632*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
1633*5c51f124SMoriah Waterland 			    gettext(ERR_BAD_PROXY), proxy);
1634*5c51f124SMoriah Waterland 			ret = B_FALSE;
1635*5c51f124SMoriah Waterland 			goto cleanup;
1636*5c51f124SMoriah Waterland 		}
1637*5c51f124SMoriah Waterland 	}
1638*5c51f124SMoriah Waterland 
1639*5c51f124SMoriah Waterland cleanup:
1640*5c51f124SMoriah Waterland 	return (ret);
1641*5c51f124SMoriah Waterland }
1642*5c51f124SMoriah Waterland 
1643*5c51f124SMoriah Waterland /*
1644*5c51f124SMoriah Waterland  * Name:		web_setup
1645*5c51f124SMoriah Waterland  * Description:	Initializes http library settings
1646*5c51f124SMoriah Waterland  *
1647*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
1648*5c51f124SMoriah Waterland  *
1649*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
1650*5c51f124SMoriah Waterland  */
1651*5c51f124SMoriah Waterland static boolean_t
1652*5c51f124SMoriah Waterland web_setup(PKG_ERR *err)
1653*5c51f124SMoriah Waterland {
1654*5c51f124SMoriah Waterland 	boolean_t ret = B_TRUE;
1655*5c51f124SMoriah Waterland 	static boolean_t keepalive = B_TRUE;
1656*5c51f124SMoriah Waterland 
1657*5c51f124SMoriah Waterland 	if ((ps->hps = http_srv_init(&ps->url)) == NULL) {
1658*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
1659*5c51f124SMoriah Waterland 		ret = B_FALSE;
1660*5c51f124SMoriah Waterland 		goto cleanup;
1661*5c51f124SMoriah Waterland 	}
1662*5c51f124SMoriah Waterland 
1663*5c51f124SMoriah Waterland 	if (getenv("WEBPKG_DEBUG") != NULL) {
1664*5c51f124SMoriah Waterland 		http_set_verbose(B_TRUE);
1665*5c51f124SMoriah Waterland 	}
1666*5c51f124SMoriah Waterland 
1667*5c51f124SMoriah Waterland 	if (ps->proxy.hostname[0] != '\0' &&
1668*5c51f124SMoriah Waterland 			http_set_proxy(ps->hps, &ps->proxy) != 0) {
1669*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
1670*5c51f124SMoriah Waterland 		ret = B_FALSE;
1671*5c51f124SMoriah Waterland 		goto cleanup;
1672*5c51f124SMoriah Waterland 	}
1673*5c51f124SMoriah Waterland 	if (http_set_keepalive(ps->hps, keepalive) != 0) {
1674*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
1675*5c51f124SMoriah Waterland 		ret = B_FALSE;
1676*5c51f124SMoriah Waterland 		goto cleanup;
1677*5c51f124SMoriah Waterland 	}
1678*5c51f124SMoriah Waterland 	if (http_set_socket_read_timeout(ps->hps, ps->timeout) != 0) {
1679*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
1680*5c51f124SMoriah Waterland 		ret = B_FALSE;
1681*5c51f124SMoriah Waterland 		goto cleanup;
1682*5c51f124SMoriah Waterland 	}
1683*5c51f124SMoriah Waterland 	if (http_set_random_file(ps->hps, RANDOM) != 0) {
1684*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
1685*5c51f124SMoriah Waterland 		ret = B_FALSE;
1686*5c51f124SMoriah Waterland 		goto cleanup;
1687*5c51f124SMoriah Waterland 	}
1688*5c51f124SMoriah Waterland 
1689*5c51f124SMoriah Waterland 	(void) http_set_p12_format(B_TRUE);
1690*5c51f124SMoriah Waterland 
1691*5c51f124SMoriah Waterland cleanup:
1692*5c51f124SMoriah Waterland 	return (ret);
1693*5c51f124SMoriah Waterland }
1694*5c51f124SMoriah Waterland 
1695*5c51f124SMoriah Waterland /*
1696*5c51f124SMoriah Waterland  * Name:		web_connect
1697*5c51f124SMoriah Waterland  * Description:	Makes connection with URL stored in static 'ps' structure.
1698*5c51f124SMoriah Waterland  *
1699*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
1700*5c51f124SMoriah Waterland  *
1701*5c51f124SMoriah Waterland  * Returns :   	WEB_OK - connection successful
1702*5c51f124SMoriah Waterland  *		WEB_VERIFY_SETUP - Unable to complete necessary
1703*5c51f124SMoriah Waterland  *			SSL setup
1704*5c51f124SMoriah Waterland  *		WEB_CONNREFUSED - Connection was refused to web site
1705*5c51f124SMoriah Waterland  *		WEB_HOSTDOWN - Host was not responding to request
1706*5c51f124SMoriah Waterland  *		WEB_NOCONNECT - Some other connection failure
1707*5c51f124SMoriah Waterland  */
1708*5c51f124SMoriah Waterland static WebStatus
1709*5c51f124SMoriah Waterland web_connect(PKG_ERR *err)
1710*5c51f124SMoriah Waterland {
1711*5c51f124SMoriah Waterland 	STACK_OF(X509)  *sec_cas = NULL;
1712*5c51f124SMoriah Waterland 	char *path;
1713*5c51f124SMoriah Waterland 	WebStatus ret = WEB_OK;
1714*5c51f124SMoriah Waterland 	ulong_t		errcode;
1715*5c51f124SMoriah Waterland 	uint_t		errsrc;
1716*5c51f124SMoriah Waterland 	int		my_errno = 0;
1717*5c51f124SMoriah Waterland 	const char		*libhttperr = NULL;
1718*5c51f124SMoriah Waterland 
1719*5c51f124SMoriah Waterland 	if (ps->url.https == B_TRUE) {
1720*5c51f124SMoriah Waterland 		/* get CA certificates */
1721*5c51f124SMoriah Waterland 		if (find_ca_certs(err, ps->keystore, &sec_cas) != 0) {
1722*5c51f124SMoriah Waterland 			ret = WEB_VERIFY_SETUP;
1723*5c51f124SMoriah Waterland 			goto cleanup;
1724*5c51f124SMoriah Waterland 		}
1725*5c51f124SMoriah Waterland 
1726*5c51f124SMoriah Waterland 		if (sk_X509_num(sec_cas) < 1) {
1727*5c51f124SMoriah Waterland 			/* no trusted websites */
1728*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
1729*5c51f124SMoriah Waterland 			    gettext(ERR_KEYSTORE_NOTRUST));
1730*5c51f124SMoriah Waterland 			ret = WEB_VERIFY_SETUP;
1731*5c51f124SMoriah Waterland 			goto cleanup;
1732*5c51f124SMoriah Waterland 		}
1733*5c51f124SMoriah Waterland 
1734*5c51f124SMoriah Waterland 		/*
1735*5c51f124SMoriah Waterland 		 * write out all CA certs to temp file.  libwanboot should
1736*5c51f124SMoriah Waterland 		 * have an interface for giving it a list of trusted certs
1737*5c51f124SMoriah Waterland 		 * through an in-memory structure, but currently that does
1738*5c51f124SMoriah Waterland 		 * not exist
1739*5c51f124SMoriah Waterland 		 */
1740*5c51f124SMoriah Waterland 		if ((path = write_ca_file(err, ps->dwnld_dir, sec_cas,
1741*5c51f124SMoriah Waterland 		    WEB_CA_PHRASE)) == NULL) {
1742*5c51f124SMoriah Waterland 			ret = WEB_VERIFY_SETUP;
1743*5c51f124SMoriah Waterland 			goto cleanup;
1744*5c51f124SMoriah Waterland 		}
1745*5c51f124SMoriah Waterland 
1746*5c51f124SMoriah Waterland 		ps->certfile = path;
1747*5c51f124SMoriah Waterland 		if (http_set_password(ps->hps, WEB_CA_PHRASE) != 0) {
1748*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
1749*5c51f124SMoriah Waterland 			    gettext(ERR_HTTPS_PASSWD));
1750*5c51f124SMoriah Waterland 			ret = WEB_VERIFY_SETUP;
1751*5c51f124SMoriah Waterland 			goto cleanup;
1752*5c51f124SMoriah Waterland 		}
1753*5c51f124SMoriah Waterland 
1754*5c51f124SMoriah Waterland 		if (http_set_certificate_authority_file(path) != 0) {
1755*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
1756*5c51f124SMoriah Waterland 			    gettext(ERR_HTTPS_CA));
1757*5c51f124SMoriah Waterland 			ret = WEB_VERIFY_SETUP;
1758*5c51f124SMoriah Waterland 			goto cleanup;
1759*5c51f124SMoriah Waterland 		}
1760*5c51f124SMoriah Waterland 	}
1761*5c51f124SMoriah Waterland 
1762*5c51f124SMoriah Waterland 	if (http_srv_connect(ps->hps) != 0) {
1763*5c51f124SMoriah Waterland 		while ((errcode = http_get_lasterr(ps->hps, &errsrc)) != 0) {
1764*5c51f124SMoriah Waterland 			/* Have an error - is it EINTR? */
1765*5c51f124SMoriah Waterland 			if (errsrc == ERRSRC_SYSTEM) {
1766*5c51f124SMoriah Waterland 				my_errno = errcode;
1767*5c51f124SMoriah Waterland 				break;
1768*5c51f124SMoriah Waterland 			} else if (libhttperr == NULL) {
1769*5c51f124SMoriah Waterland 				/* save the first non-system error message */
1770*5c51f124SMoriah Waterland 				libhttperr = http_errorstr(errsrc, errcode);
1771*5c51f124SMoriah Waterland 			}
1772*5c51f124SMoriah Waterland 		}
1773*5c51f124SMoriah Waterland 		switch (my_errno) {
1774*5c51f124SMoriah Waterland 		case EINTR:
1775*5c51f124SMoriah Waterland 		case ETIMEDOUT:
1776*5c51f124SMoriah Waterland 				/* Timed out.  Try, try again */
1777*5c51f124SMoriah Waterland 			ret = WEB_TIMEOUT;
1778*5c51f124SMoriah Waterland 			break;
1779*5c51f124SMoriah Waterland 		case ECONNREFUSED:
1780*5c51f124SMoriah Waterland 			ret = WEB_CONNREFUSED;
1781*5c51f124SMoriah Waterland 			break;
1782*5c51f124SMoriah Waterland 		case EHOSTDOWN:
1783*5c51f124SMoriah Waterland 			ret = WEB_HOSTDOWN;
1784*5c51f124SMoriah Waterland 			break;
1785*5c51f124SMoriah Waterland 		default:
1786*5c51f124SMoriah Waterland 				/* some other fatal error */
1787*5c51f124SMoriah Waterland 			ret = WEB_NOCONNECT;
1788*5c51f124SMoriah Waterland 			if (libhttperr == NULL) {
1789*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_WEB,
1790*5c51f124SMoriah Waterland 				    gettext(ERR_INIT_CONN),
1791*5c51f124SMoriah Waterland 				    ps->url.hport.hostname);
1792*5c51f124SMoriah Waterland 			} else {
1793*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_WEB,
1794*5c51f124SMoriah Waterland 				    gettext(ERR_HTTP), libhttperr);
1795*5c51f124SMoriah Waterland 			}
1796*5c51f124SMoriah Waterland 			break;
1797*5c51f124SMoriah Waterland 		}
1798*5c51f124SMoriah Waterland 	}
1799*5c51f124SMoriah Waterland cleanup:
1800*5c51f124SMoriah Waterland 	return (ret);
1801*5c51f124SMoriah Waterland }
1802*5c51f124SMoriah Waterland 
1803*5c51f124SMoriah Waterland /*
1804*5c51f124SMoriah Waterland  * Name:		write_ca_file
1805*5c51f124SMoriah Waterland  * Description:	Writes out a PKCS12 file containing all trusted certs
1806*5c51f124SMoriah Waterland  *		found in keystore recorded in static 'ps' structure
1807*5c51f124SMoriah Waterland  *
1808*5c51f124SMoriah Waterland  *		This routine is used because the libwanboot library's
1809*5c51f124SMoriah Waterland  *		HTTPS routines cannot accept trusted certificates
1810*5c51f124SMoriah Waterland  *		through an in-memory structure, when initiating an
1811*5c51f124SMoriah Waterland  *		SSL connection.  They must be in a PKCS12, which is
1812*5c51f124SMoriah Waterland  *		admittedly a poor interface.
1813*5c51f124SMoriah Waterland  *
1814*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
1815*5c51f124SMoriah Waterland  *     		tmpdir - Directory to write certificate file in
1816*5c51f124SMoriah Waterland  *		cacerts - Certs to write out
1817*5c51f124SMoriah Waterland  *		passwd - password used to encrypt certs
1818*5c51f124SMoriah Waterland  *
1819*5c51f124SMoriah Waterland  * Returns :	path to resulting file, if successfullly written,
1820*5c51f124SMoriah Waterland  *		otherwise NULL.
1821*5c51f124SMoriah Waterland  */
1822*5c51f124SMoriah Waterland static char
1823*5c51f124SMoriah Waterland *write_ca_file(PKG_ERR *err, char *tmpdir, STACK_OF(X509) *cacerts,
1824*5c51f124SMoriah Waterland     char *passwd)
1825*5c51f124SMoriah Waterland {
1826*5c51f124SMoriah Waterland 	int fd, len;
1827*5c51f124SMoriah Waterland 	FILE *fp;
1828*5c51f124SMoriah Waterland 	PKCS12	*p12 = NULL;
1829*5c51f124SMoriah Waterland 	char *ret = NULL;
1830*5c51f124SMoriah Waterland 	static char tmp_file[PATH_MAX] = "";
1831*5c51f124SMoriah Waterland 	struct stat buf;
1832*5c51f124SMoriah Waterland 
1833*5c51f124SMoriah Waterland 	if (!path_valid(tmpdir)) {
1834*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
1835*5c51f124SMoriah Waterland 		goto cleanup;
1836*5c51f124SMoriah Waterland 	}
1837*5c51f124SMoriah Waterland 
1838*5c51f124SMoriah Waterland 	/* mkstemp replaces XXXXXX with a unique string */
1839*5c51f124SMoriah Waterland 	if (((len = snprintf(tmp_file, PATH_MAX, "%s/%sXXXXXX", tmpdir,
1840*5c51f124SMoriah Waterland 	    "cert")) < 0) ||
1841*5c51f124SMoriah Waterland 	    (len >= PATH_MAX)) {
1842*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
1843*5c51f124SMoriah Waterland 		goto cleanup;
1844*5c51f124SMoriah Waterland 	}
1845*5c51f124SMoriah Waterland 
1846*5c51f124SMoriah Waterland 	if ((fd = mkstemp(tmp_file)) == -1) {
1847*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
1848*5c51f124SMoriah Waterland 		goto cleanup;
1849*5c51f124SMoriah Waterland 	}
1850*5c51f124SMoriah Waterland 
1851*5c51f124SMoriah Waterland 	if (fstat(fd, &buf) == -1) {
1852*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
1853*5c51f124SMoriah Waterland 		goto cleanup;
1854*5c51f124SMoriah Waterland 	}
1855*5c51f124SMoriah Waterland 
1856*5c51f124SMoriah Waterland 	if (!S_ISREG(buf.st_mode)) {
1857*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
1858*5c51f124SMoriah Waterland 		goto cleanup;
1859*5c51f124SMoriah Waterland 	}
1860*5c51f124SMoriah Waterland 
1861*5c51f124SMoriah Waterland 	if ((fp = fdopen(fd, "w")) == NULL) {
1862*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
1863*5c51f124SMoriah Waterland 		goto cleanup;
1864*5c51f124SMoriah Waterland 	}
1865*5c51f124SMoriah Waterland 
1866*5c51f124SMoriah Waterland 	if ((p12 = sunw_PKCS12_create(passwd, NULL, NULL, cacerts)) == NULL) {
1867*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB,
1868*5c51f124SMoriah Waterland 		    gettext(ERR_KEYSTORE_FORM), tmp_file);
1869*5c51f124SMoriah Waterland 		goto cleanup;
1870*5c51f124SMoriah Waterland 	}
1871*5c51f124SMoriah Waterland 
1872*5c51f124SMoriah Waterland 	if (i2d_PKCS12_fp(fp, p12) == 0) {
1873*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB,
1874*5c51f124SMoriah Waterland 		    gettext(ERR_KEYSTORE_FORM), tmp_file);
1875*5c51f124SMoriah Waterland 		goto cleanup;
1876*5c51f124SMoriah Waterland 	}
1877*5c51f124SMoriah Waterland 
1878*5c51f124SMoriah Waterland 	(void) fflush(fp);
1879*5c51f124SMoriah Waterland 	(void) fclose(fp);
1880*5c51f124SMoriah Waterland 	(void) close(fd);
1881*5c51f124SMoriah Waterland 	fp = NULL;
1882*5c51f124SMoriah Waterland 	fd = -1;
1883*5c51f124SMoriah Waterland 	ret = tmp_file;
1884*5c51f124SMoriah Waterland 
1885*5c51f124SMoriah Waterland cleanup:
1886*5c51f124SMoriah Waterland 	if (p12 != NULL)
1887*5c51f124SMoriah Waterland 		PKCS12_free(p12);
1888*5c51f124SMoriah Waterland 	if (fp != NULL)
1889*5c51f124SMoriah Waterland 		(void) fclose(fp);
1890*5c51f124SMoriah Waterland 	if (fd != -1) {
1891*5c51f124SMoriah Waterland 		(void) close(fd);
1892*5c51f124SMoriah Waterland 		(void) unlink(tmp_file);
1893*5c51f124SMoriah Waterland 	}
1894*5c51f124SMoriah Waterland 
1895*5c51f124SMoriah Waterland 	return (ret);
1896*5c51f124SMoriah Waterland }
1897*5c51f124SMoriah Waterland 
1898*5c51f124SMoriah Waterland /*
1899*5c51f124SMoriah Waterland  * Name:		web_send_request
1900*5c51f124SMoriah Waterland  * Description:	Sends an HTTP request for a file to the
1901*5c51f124SMoriah Waterland  *		web server being communicated with in the static
1902*5c51f124SMoriah Waterland  *		'ps' structure
1903*5c51f124SMoriah Waterland  *
1904*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
1905*5c51f124SMoriah Waterland  *		request_type - HTTP_REQ_TYPE_HEAD to send an HTTP HEAD request,
1906*5c51f124SMoriah Waterland  *		or HTTP_REQ_TYPE_GET to send an HTTP GET request
1907*5c51f124SMoriah Waterland  *		cp -
1908*5c51f124SMoriah Waterland  * Returns :   	WEB_OK - request sent successfully
1909*5c51f124SMoriah Waterland  *		WEB_CONNREFUSED - Connection was refused to web site
1910*5c51f124SMoriah Waterland  *		WEB_HOSTDOWN - Host was not responding to request
1911*5c51f124SMoriah Waterland  *		WEB_NOCONNECT - Some other connection failure
1912*5c51f124SMoriah Waterland  */
1913*5c51f124SMoriah Waterland static WebStatus
1914*5c51f124SMoriah Waterland web_send_request(PKG_ERR *err, int request_type, int cp, int ep)
1915*5c51f124SMoriah Waterland {
1916*5c51f124SMoriah Waterland 	WebStatus ret = WEB_OK;
1917*5c51f124SMoriah Waterland 	ulong_t		errcode;
1918*5c51f124SMoriah Waterland 	uint_t		errsrc;
1919*5c51f124SMoriah Waterland 	int		my_errno = 0;
1920*5c51f124SMoriah Waterland 	const char		*libhttperr = NULL;
1921*5c51f124SMoriah Waterland 	switch (request_type) {
1922*5c51f124SMoriah Waterland 	case HTTP_REQ_TYPE_HEAD:
1923*5c51f124SMoriah Waterland 		if ((http_head_request(ps->hps, ps->url.abspath)) != 0) {
1924*5c51f124SMoriah Waterland 			while ((errcode = http_get_lasterr(ps->hps,
1925*5c51f124SMoriah Waterland 			    &errsrc)) != 0) {
1926*5c51f124SMoriah Waterland 				/* Have an error - is it EINTR? */
1927*5c51f124SMoriah Waterland 			    if (errsrc == ERRSRC_SYSTEM) {
1928*5c51f124SMoriah Waterland 				    my_errno = errcode;
1929*5c51f124SMoriah Waterland 				    break;
1930*5c51f124SMoriah Waterland 			    } else if (libhttperr == NULL) {
1931*5c51f124SMoriah Waterland 				    /* save first non-system error message */
1932*5c51f124SMoriah Waterland 				    libhttperr =
1933*5c51f124SMoriah Waterland 					http_errorstr(errsrc, errcode);
1934*5c51f124SMoriah Waterland 			    }
1935*5c51f124SMoriah Waterland 			}
1936*5c51f124SMoriah Waterland 			switch (my_errno) {
1937*5c51f124SMoriah Waterland 			    case EINTR:
1938*5c51f124SMoriah Waterland 			case ETIMEDOUT:
1939*5c51f124SMoriah Waterland 				/* Timed out.  Try, try again */
1940*5c51f124SMoriah Waterland 				ret = WEB_TIMEOUT;
1941*5c51f124SMoriah Waterland 				break;
1942*5c51f124SMoriah Waterland 			case ECONNREFUSED:
1943*5c51f124SMoriah Waterland 				ret = WEB_CONNREFUSED;
1944*5c51f124SMoriah Waterland 				break;
1945*5c51f124SMoriah Waterland 			case EHOSTDOWN:
1946*5c51f124SMoriah Waterland 				ret = WEB_HOSTDOWN;
1947*5c51f124SMoriah Waterland 				break;
1948*5c51f124SMoriah Waterland 			default:
1949*5c51f124SMoriah Waterland 				/* some other fatal error */
1950*5c51f124SMoriah Waterland 				ret = WEB_NOCONNECT;
1951*5c51f124SMoriah Waterland 				if (libhttperr == NULL) {
1952*5c51f124SMoriah Waterland 					pkgerr_add(err, PKGERR_WEB,
1953*5c51f124SMoriah Waterland 					    gettext(ERR_INIT_CONN),
1954*5c51f124SMoriah Waterland 					    ps->url.hport.hostname);
1955*5c51f124SMoriah Waterland 				} else {
1956*5c51f124SMoriah Waterland 					pkgerr_add(err, PKGERR_WEB,
1957*5c51f124SMoriah Waterland 					    gettext(ERR_HTTP), libhttperr);
1958*5c51f124SMoriah Waterland 				}
1959*5c51f124SMoriah Waterland 				break;
1960*5c51f124SMoriah Waterland 			}
1961*5c51f124SMoriah Waterland 			goto cleanup;
1962*5c51f124SMoriah Waterland 			}
1963*5c51f124SMoriah Waterland 		break;
1964*5c51f124SMoriah Waterland 
1965*5c51f124SMoriah Waterland 	case HTTP_REQ_TYPE_GET:
1966*5c51f124SMoriah Waterland 		if (cp && ep) {
1967*5c51f124SMoriah Waterland 			if (http_get_range_request(ps->hps, ps->url.abspath,
1968*5c51f124SMoriah Waterland 			    cp, ep - cp) != 0) {
1969*5c51f124SMoriah Waterland 				while ((errcode = http_get_lasterr(ps->hps,
1970*5c51f124SMoriah Waterland 				    &errsrc)) != 0) {
1971*5c51f124SMoriah Waterland 					/* Have an error - is it EINTR? */
1972*5c51f124SMoriah Waterland 					if (errsrc == ERRSRC_SYSTEM) {
1973*5c51f124SMoriah Waterland 						my_errno = errcode;
1974*5c51f124SMoriah Waterland 						break;
1975*5c51f124SMoriah Waterland 					} else {
1976*5c51f124SMoriah Waterland 						/*
1977*5c51f124SMoriah Waterland 						 * save first non-system
1978*5c51f124SMoriah Waterland 						 * error message
1979*5c51f124SMoriah Waterland 						 */
1980*5c51f124SMoriah Waterland 						libhttperr =
1981*5c51f124SMoriah Waterland 						    http_errorstr(errsrc,
1982*5c51f124SMoriah Waterland 							errcode);
1983*5c51f124SMoriah Waterland 					}
1984*5c51f124SMoriah Waterland 				}
1985*5c51f124SMoriah Waterland 				switch (my_errno) {
1986*5c51f124SMoriah Waterland 				case EINTR:
1987*5c51f124SMoriah Waterland 				case ETIMEDOUT:
1988*5c51f124SMoriah Waterland 					/* Timed out.  Try, try again */
1989*5c51f124SMoriah Waterland 					ret = WEB_TIMEOUT;
1990*5c51f124SMoriah Waterland 					break;
1991*5c51f124SMoriah Waterland 				case ECONNREFUSED:
1992*5c51f124SMoriah Waterland 					ret = WEB_CONNREFUSED;
1993*5c51f124SMoriah Waterland 					break;
1994*5c51f124SMoriah Waterland 				case EHOSTDOWN:
1995*5c51f124SMoriah Waterland 					ret = WEB_HOSTDOWN;
1996*5c51f124SMoriah Waterland 					break;
1997*5c51f124SMoriah Waterland 				default:
1998*5c51f124SMoriah Waterland 					/* some other fatal error */
1999*5c51f124SMoriah Waterland 					ret = WEB_NOCONNECT;
2000*5c51f124SMoriah Waterland 					if (libhttperr == NULL) {
2001*5c51f124SMoriah Waterland 						pkgerr_add(err, PKGERR_WEB,
2002*5c51f124SMoriah Waterland 						    gettext(ERR_INIT_CONN),
2003*5c51f124SMoriah Waterland 						    ps->url.hport.hostname);
2004*5c51f124SMoriah Waterland 					} else {
2005*5c51f124SMoriah Waterland 						pkgerr_add(err, PKGERR_WEB,
2006*5c51f124SMoriah Waterland 						    gettext(ERR_HTTP),
2007*5c51f124SMoriah Waterland 						    libhttperr);
2008*5c51f124SMoriah Waterland 					}
2009*5c51f124SMoriah Waterland 					break;
2010*5c51f124SMoriah Waterland 				}
2011*5c51f124SMoriah Waterland 				goto cleanup;
2012*5c51f124SMoriah Waterland 			}
2013*5c51f124SMoriah Waterland 
2014*5c51f124SMoriah Waterland 			if (!web_eval_headers(err)) {
2015*5c51f124SMoriah Waterland 				ret = WEB_NOCONNECT;
2016*5c51f124SMoriah Waterland 				goto cleanup;
2017*5c51f124SMoriah Waterland 			}
2018*5c51f124SMoriah Waterland 		} else {
2019*5c51f124SMoriah Waterland 			if ((http_get_request(ps->hps, ps->url.abspath))
2020*5c51f124SMoriah Waterland 					!= 0) {
2021*5c51f124SMoriah Waterland 				while ((errcode = http_get_lasterr(ps->hps,
2022*5c51f124SMoriah Waterland 				    &errsrc)) != 0) {
2023*5c51f124SMoriah Waterland 					/* Have an error - is it EINTR? */
2024*5c51f124SMoriah Waterland 					if (errsrc == ERRSRC_SYSTEM) {
2025*5c51f124SMoriah Waterland 						my_errno = errcode;
2026*5c51f124SMoriah Waterland 						break;
2027*5c51f124SMoriah Waterland 					} else {
2028*5c51f124SMoriah Waterland 						/*
2029*5c51f124SMoriah Waterland 						 * save the first non-system
2030*5c51f124SMoriah Waterland 						 * error message
2031*5c51f124SMoriah Waterland 						 */
2032*5c51f124SMoriah Waterland 						libhttperr =
2033*5c51f124SMoriah Waterland 						    http_errorstr(errsrc,
2034*5c51f124SMoriah Waterland 							errcode);
2035*5c51f124SMoriah Waterland 					}
2036*5c51f124SMoriah Waterland 				}
2037*5c51f124SMoriah Waterland 				switch (my_errno) {
2038*5c51f124SMoriah Waterland 				case EINTR:
2039*5c51f124SMoriah Waterland 				case ETIMEDOUT:
2040*5c51f124SMoriah Waterland 					/* Timed out.  Try, try again */
2041*5c51f124SMoriah Waterland 					ret = WEB_TIMEOUT;
2042*5c51f124SMoriah Waterland 					break;
2043*5c51f124SMoriah Waterland 				case ECONNREFUSED:
2044*5c51f124SMoriah Waterland 					ret = WEB_CONNREFUSED;
2045*5c51f124SMoriah Waterland 					break;
2046*5c51f124SMoriah Waterland 				case EHOSTDOWN:
2047*5c51f124SMoriah Waterland 					ret = WEB_HOSTDOWN;
2048*5c51f124SMoriah Waterland 					break;
2049*5c51f124SMoriah Waterland 				default:
2050*5c51f124SMoriah Waterland 					/* some other fatal error */
2051*5c51f124SMoriah Waterland 					ret = WEB_NOCONNECT;
2052*5c51f124SMoriah Waterland 					if (libhttperr == NULL) {
2053*5c51f124SMoriah Waterland 						pkgerr_add(err, PKGERR_WEB,
2054*5c51f124SMoriah Waterland 						    gettext(ERR_INIT_CONN),
2055*5c51f124SMoriah Waterland 						    ps->url.hport.hostname);
2056*5c51f124SMoriah Waterland 					} else {
2057*5c51f124SMoriah Waterland 						pkgerr_add(err, PKGERR_WEB,
2058*5c51f124SMoriah Waterland 						    gettext(ERR_HTTP),
2059*5c51f124SMoriah Waterland 						    libhttperr);
2060*5c51f124SMoriah Waterland 					}
2061*5c51f124SMoriah Waterland 					break;
2062*5c51f124SMoriah Waterland 				}
2063*5c51f124SMoriah Waterland 				goto cleanup;
2064*5c51f124SMoriah Waterland 			}
2065*5c51f124SMoriah Waterland 
2066*5c51f124SMoriah Waterland 			if (!web_eval_headers(err)) {
2067*5c51f124SMoriah Waterland 				ret = WEB_NOCONNECT;
2068*5c51f124SMoriah Waterland 				goto cleanup;
2069*5c51f124SMoriah Waterland 			}
2070*5c51f124SMoriah Waterland 		}
2071*5c51f124SMoriah Waterland 		break;
2072*5c51f124SMoriah Waterland 	default:
2073*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
2074*5c51f124SMoriah Waterland 		    __FILE__, __LINE__);
2075*5c51f124SMoriah Waterland 	}
2076*5c51f124SMoriah Waterland 
2077*5c51f124SMoriah Waterland cleanup:
2078*5c51f124SMoriah Waterland 	return (ret);
2079*5c51f124SMoriah Waterland }
2080*5c51f124SMoriah Waterland 
2081*5c51f124SMoriah Waterland /*
2082*5c51f124SMoriah Waterland  * Name:		web_eval_headers
2083*5c51f124SMoriah Waterland  * Description:	Evaluates HTTP headers returned during an HTTP request.
2084*5c51f124SMoriah Waterland  *		This must be called before calling
2085*5c51f124SMoriah Waterland  *		http_get_header_value().
2086*5c51f124SMoriah Waterland  *
2087*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
2088*5c51f124SMoriah Waterland  *
2089*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
2090*5c51f124SMoriah Waterland  */
2091*5c51f124SMoriah Waterland static boolean_t
2092*5c51f124SMoriah Waterland web_eval_headers(PKG_ERR *err)
2093*5c51f124SMoriah Waterland {
2094*5c51f124SMoriah Waterland 	const char *http_err;
2095*5c51f124SMoriah Waterland 	ulong_t herr;
2096*5c51f124SMoriah Waterland 	uint_t errsrc;
2097*5c51f124SMoriah Waterland 
2098*5c51f124SMoriah Waterland 	if (http_process_headers(ps->hps, &ps->resp) != 0) {
2099*5c51f124SMoriah Waterland 		if ((ps->resp != NULL) && (ps->resp->statusmsg != NULL)) {
2100*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
2101*5c51f124SMoriah Waterland 			    ps->resp->statusmsg);
2102*5c51f124SMoriah Waterland 		}
2103*5c51f124SMoriah Waterland 
2104*5c51f124SMoriah Waterland 		herr = http_get_lasterr(ps->hps, &errsrc);
2105*5c51f124SMoriah Waterland 		http_err = http_errorstr(errsrc, herr);
2106*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
2107*5c51f124SMoriah Waterland 		    http_err);
2108*5c51f124SMoriah Waterland 		return (B_FALSE);
2109*5c51f124SMoriah Waterland 	}
2110*5c51f124SMoriah Waterland 	return (B_TRUE);
2111*5c51f124SMoriah Waterland }
2112*5c51f124SMoriah Waterland 
2113*5c51f124SMoriah Waterland /*
2114*5c51f124SMoriah Waterland  * Name:		web_get_file
2115*5c51f124SMoriah Waterland  * Description:	Downloads the file URL from the website, all of
2116*5c51f124SMoriah Waterland  *		which are recorded in the static 'ps' struct
2117*5c51f124SMoriah Waterland  *
2118*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
2119*5c51f124SMoriah Waterland  *		dwnld_dir - Directory to download file into
2120*5c51f124SMoriah Waterland  *		device - Where to store path to resulting
2121*5c51f124SMoriah Waterland  *			file
2122*5c51f124SMoriah Waterland  *		nointeract - if non-zero, do not output
2123*5c51f124SMoriah Waterland  *		progress
2124*5c51f124SMoriah Waterland  *		fname - name of downloaded file link in the dwnld_dir
2125*5c51f124SMoriah Waterland  *
2126*5c51f124SMoriah Waterland  * Returns :   	WEB_OK - download successful
2127*5c51f124SMoriah Waterland  *		WEB_CONNREFUSED - Connection was refused to web site
2128*5c51f124SMoriah Waterland  *		WEB_HOSTDOWN - Host was not responding to request
2129*5c51f124SMoriah Waterland  *		WEB_GET_FAIL - Unable to initialize download
2130*5c51f124SMoriah Waterland  *		state (temp file creation, header parsing, etc)
2131*5c51f124SMoriah Waterland  *		WEB_NOCONNECT - Some other connection failure
2132*5c51f124SMoriah Waterland  */
2133*5c51f124SMoriah Waterland static WebStatus
2134*5c51f124SMoriah Waterland web_get_file(PKG_ERR *err, char *dwnld_dir, int nointeract, char **fname)
2135*5c51f124SMoriah Waterland {
2136*5c51f124SMoriah Waterland 	int		i, fd;
2137*5c51f124SMoriah Waterland 	int		n = 0;
2138*5c51f124SMoriah Waterland 	ulong_t		abs_pos = 0;
2139*5c51f124SMoriah Waterland 	char		*head_val = NULL;
2140*5c51f124SMoriah Waterland 	char		*lastmod_val = NULL;
2141*5c51f124SMoriah Waterland 	char		*bname = NULL;
2142*5c51f124SMoriah Waterland 	struct stat	status;
2143*5c51f124SMoriah Waterland 	WebStatus	ret = WEB_OK;
2144*5c51f124SMoriah Waterland 	WebStatus	req_ret;
2145*5c51f124SMoriah Waterland 	ulong_t		errcode;
2146*5c51f124SMoriah Waterland 	uint_t		errsrc;
2147*5c51f124SMoriah Waterland 	int		my_errno = 0;
2148*5c51f124SMoriah Waterland 	const char	*libhttperr = NULL;
2149*5c51f124SMoriah Waterland 	char		*disp;
2150*5c51f124SMoriah Waterland 	char		tmp_file[PATH_MAX];
2151*5c51f124SMoriah Waterland 	int		len;
2152*5c51f124SMoriah Waterland 
2153*5c51f124SMoriah Waterland 	ps->data.prev_cont_length =
2154*5c51f124SMoriah Waterland 	ps->data.content_length =
2155*5c51f124SMoriah Waterland 	ps->data.cur_pos = 0;
2156*5c51f124SMoriah Waterland 
2157*5c51f124SMoriah Waterland 	if ((head_val = http_get_header_value(ps->hps,
2158*5c51f124SMoriah Waterland 	    CONTENT_LENGTH_HDR)) != NULL) {
2159*5c51f124SMoriah Waterland 		ps->data.content_length = atol(head_val);
2160*5c51f124SMoriah Waterland 	} else {
2161*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_HEAD_VAL),
2162*5c51f124SMoriah Waterland 		    CONTENT_LENGTH_HDR);
2163*5c51f124SMoriah Waterland 		ret = WEB_GET_FAIL;
2164*5c51f124SMoriah Waterland 		goto cleanup;
2165*5c51f124SMoriah Waterland 	}
2166*5c51f124SMoriah Waterland 
2167*5c51f124SMoriah Waterland 	free(head_val);
2168*5c51f124SMoriah Waterland 	head_val = NULL;
2169*5c51f124SMoriah Waterland 
2170*5c51f124SMoriah Waterland 	if ((head_val = http_get_header_value(ps->hps,
2171*5c51f124SMoriah Waterland 	    CONTENT_DISPOSITION_HDR)) != NULL) {
2172*5c51f124SMoriah Waterland 		/* "inline; parm=val; parm=val */
2173*5c51f124SMoriah Waterland 		if ((disp = strtok(head_val, "; \t\n\f\r")) != NULL) {
2174*5c51f124SMoriah Waterland 			/* disp = "inline" */
2175*5c51f124SMoriah Waterland 			while ((disp = strtok(NULL, "; \t\n\f\r")) != NULL) {
2176*5c51f124SMoriah Waterland 				/* disp = "parm=val" */
2177*5c51f124SMoriah Waterland 				if (ci_strneq(disp, "filename=", 9)) {
2178*5c51f124SMoriah Waterland 					bname = xstrdup(basename(disp + 9));
2179*5c51f124SMoriah Waterland 					trim(bname);
2180*5c51f124SMoriah Waterland 					dequote(bname);
2181*5c51f124SMoriah Waterland 				}
2182*5c51f124SMoriah Waterland 			}
2183*5c51f124SMoriah Waterland 		}
2184*5c51f124SMoriah Waterland 		free(head_val);
2185*5c51f124SMoriah Waterland 		head_val = NULL;
2186*5c51f124SMoriah Waterland 	}
2187*5c51f124SMoriah Waterland 
2188*5c51f124SMoriah Waterland 	if (bname == NULL) {
2189*5c51f124SMoriah Waterland 		/*
2190*5c51f124SMoriah Waterland 		 * couldn't determine filename from header value,
2191*5c51f124SMoriah Waterland 		 * so take basename of URL
2192*5c51f124SMoriah Waterland 		 */
2193*5c51f124SMoriah Waterland 		if ((bname = get_endof_string(ps->url.abspath, '/')) == NULL) {
2194*5c51f124SMoriah Waterland 			/* URL is bad */
2195*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_PARSE,
2196*5c51f124SMoriah Waterland 			    gettext(ERR_PARSE_URL), ps->url.abspath);
2197*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2198*5c51f124SMoriah Waterland 			goto cleanup;
2199*5c51f124SMoriah Waterland 		}
2200*5c51f124SMoriah Waterland 	}
2201*5c51f124SMoriah Waterland 
2202*5c51f124SMoriah Waterland 	*fname = bname;
2203*5c51f124SMoriah Waterland 
2204*5c51f124SMoriah Waterland 	if ((head_val = http_get_header_value(ps->hps, LAST_MODIFIED_HDR))
2205*5c51f124SMoriah Waterland 			!= NULL) {
2206*5c51f124SMoriah Waterland 
2207*5c51f124SMoriah Waterland 		if ((lastmod_val = condense_lastmodified(head_val)) == NULL) {
2208*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_BAD_HEAD_VAL),
2209*5c51f124SMoriah Waterland 			    LAST_MODIFIED_HDR, head_val);
2210*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2211*5c51f124SMoriah Waterland 			goto cleanup;
2212*5c51f124SMoriah Waterland 		}
2213*5c51f124SMoriah Waterland 		free(head_val);
2214*5c51f124SMoriah Waterland 		head_val = NULL;
2215*5c51f124SMoriah Waterland 
2216*5c51f124SMoriah Waterland 		if ((ps->uniqfile = get_unique_filename(dwnld_dir,
2217*5c51f124SMoriah Waterland 		    lastmod_val)) == NULL) {
2218*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPEN_TMP));
2219*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2220*5c51f124SMoriah Waterland 			goto cleanup;
2221*5c51f124SMoriah Waterland 		}
2222*5c51f124SMoriah Waterland 
2223*5c51f124SMoriah Waterland 		free(lastmod_val);
2224*5c51f124SMoriah Waterland 		lastmod_val = NULL;
2225*5c51f124SMoriah Waterland 
2226*5c51f124SMoriah Waterland 		if ((fd = open(ps->uniqfile,
2227*5c51f124SMoriah Waterland 		    O_NONBLOCK|O_RDWR|O_APPEND|O_CREAT|O_EXCL,
2228*5c51f124SMoriah Waterland 		    640)) == -1) {
2229*5c51f124SMoriah Waterland 
2230*5c51f124SMoriah Waterland 			/*
2231*5c51f124SMoriah Waterland 			 * A partial downloaded file
2232*5c51f124SMoriah Waterland 			 * already exists, so open it.
2233*5c51f124SMoriah Waterland 			 */
2234*5c51f124SMoriah Waterland 			if ((fd = open(ps->uniqfile,
2235*5c51f124SMoriah Waterland 			    O_NONBLOCK|O_RDWR|O_APPEND)) != -1) {
2236*5c51f124SMoriah Waterland 				if (fstat(fd, &status) == -1 ||
2237*5c51f124SMoriah Waterland 				    !S_ISREG(status.st_mode)) {
2238*5c51f124SMoriah Waterland 					pkgerr_add(err, PKGERR_WEB,
2239*5c51f124SMoriah Waterland 					    gettext(ERR_DWNLD_NO_CONT),
2240*5c51f124SMoriah Waterland 					    ps->uniqfile);
2241*5c51f124SMoriah Waterland 					ret = WEB_GET_FAIL;
2242*5c51f124SMoriah Waterland 					goto cleanup;
2243*5c51f124SMoriah Waterland 				} else {
2244*5c51f124SMoriah Waterland 					echo_out(nointeract,
2245*5c51f124SMoriah Waterland 					    gettext(MSG_DWNLD_PART),
2246*5c51f124SMoriah Waterland 					    ps->uniqfile,
2247*5c51f124SMoriah Waterland 					    status.st_size);
2248*5c51f124SMoriah Waterland 					ps->data.prev_cont_length =
2249*5c51f124SMoriah Waterland 					    status.st_size;
2250*5c51f124SMoriah Waterland 				}
2251*5c51f124SMoriah Waterland 			} else {
2252*5c51f124SMoriah Waterland 				/* unable to open partial file */
2253*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_WEB,
2254*5c51f124SMoriah Waterland 				    gettext(ERR_DWNLD_NO_CONT),
2255*5c51f124SMoriah Waterland 				    ps->uniqfile);
2256*5c51f124SMoriah Waterland 				ret = WEB_GET_FAIL;
2257*5c51f124SMoriah Waterland 				goto cleanup;
2258*5c51f124SMoriah Waterland 			}
2259*5c51f124SMoriah Waterland 		}
2260*5c51f124SMoriah Waterland 	} else {
2261*5c51f124SMoriah Waterland 		/*
2262*5c51f124SMoriah Waterland 		 * no "Last-Modified" header, so this file is not eligible for
2263*5c51f124SMoriah Waterland 		 * spooling and "resuming last download" operations
2264*5c51f124SMoriah Waterland 		 */
2265*5c51f124SMoriah Waterland 		ps->spool = B_FALSE;
2266*5c51f124SMoriah Waterland 
2267*5c51f124SMoriah Waterland 		/* mkstemp replaces XXXXXX with a unique string */
2268*5c51f124SMoriah Waterland 		if (((len = snprintf(tmp_file, PATH_MAX,
2269*5c51f124SMoriah Waterland 		    "%s/%sXXXXXX", dwnld_dir, "stream")) < 0) ||
2270*5c51f124SMoriah Waterland 		    (len >= PATH_MAX)) {
2271*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
2272*5c51f124SMoriah Waterland 			    gettext(MSG_NOTEMP), dwnld_dir);
2273*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2274*5c51f124SMoriah Waterland 			goto cleanup;
2275*5c51f124SMoriah Waterland 		}
2276*5c51f124SMoriah Waterland 
2277*5c51f124SMoriah Waterland 		if ((fd = mkstemp(tmp_file)) == -1) {
2278*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
2279*5c51f124SMoriah Waterland 			    gettext(MSG_NOTMPFIL), tmp_file);
2280*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2281*5c51f124SMoriah Waterland 			goto cleanup;
2282*5c51f124SMoriah Waterland 		}
2283*5c51f124SMoriah Waterland 
2284*5c51f124SMoriah Waterland 		if (fstat(fd, &status) == -1 ||
2285*5c51f124SMoriah Waterland 		    !S_ISREG(status.st_mode)) {
2286*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
2287*5c51f124SMoriah Waterland 			    gettext(ERR_DWNLD_NO_CONT),
2288*5c51f124SMoriah Waterland 			    ps->uniqfile);
2289*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2290*5c51f124SMoriah Waterland 			goto cleanup;
2291*5c51f124SMoriah Waterland 		}
2292*5c51f124SMoriah Waterland 
2293*5c51f124SMoriah Waterland 		ps->data.prev_cont_length = 0;
2294*5c51f124SMoriah Waterland 		ps->uniqfile = xstrdup(tmp_file);
2295*5c51f124SMoriah Waterland 	}
2296*5c51f124SMoriah Waterland 
2297*5c51f124SMoriah Waterland 	/* File has already been completely downloaded */
2298*5c51f124SMoriah Waterland 	if (ps->data.prev_cont_length == ps->data.content_length) {
2299*5c51f124SMoriah Waterland 		echo_out(nointeract, gettext(MSG_DWNLD_PREV), ps->uniqfile);
2300*5c51f124SMoriah Waterland 		ps->data.cur_pos = ps->data.prev_cont_length;
2301*5c51f124SMoriah Waterland 		if (!make_link(dwnld_dir, bname)) {
2302*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
2303*5c51f124SMoriah Waterland 			    dwnld_dir);
2304*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2305*5c51f124SMoriah Waterland 			goto cleanup;
2306*5c51f124SMoriah Waterland 		}
2307*5c51f124SMoriah Waterland 		/* we're done, so cleanup and return success */
2308*5c51f124SMoriah Waterland 		goto cleanup;
2309*5c51f124SMoriah Waterland 	} else if (ps->data.prev_cont_length != 0) {
2310*5c51f124SMoriah Waterland 		ps->data.cur_pos = ps->data.prev_cont_length;
2311*5c51f124SMoriah Waterland 	}
2312*5c51f124SMoriah Waterland 
2313*5c51f124SMoriah Waterland 	if (!ck_dwnld_dir_space(err, dwnld_dir,
2314*5c51f124SMoriah Waterland 	    (ps->data.prev_cont_length != 0) ?
2315*5c51f124SMoriah Waterland 	    (ps->data.content_length - ps->data.cur_pos) :
2316*5c51f124SMoriah Waterland 	    ps->data.content_length)) {
2317*5c51f124SMoriah Waterland 		ret = WEB_GET_FAIL;
2318*5c51f124SMoriah Waterland 		goto cleanup;
2319*5c51f124SMoriah Waterland 	}
2320*5c51f124SMoriah Waterland 
2321*5c51f124SMoriah Waterland 	if ((req_ret = web_send_request(err, HTTP_REQ_TYPE_GET,
2322*5c51f124SMoriah Waterland 	    ps->data.cur_pos, ps->data.content_length)) != WEB_OK) {
2323*5c51f124SMoriah Waterland 		ret = req_ret;
2324*5c51f124SMoriah Waterland 		goto cleanup;
2325*5c51f124SMoriah Waterland 	}
2326*5c51f124SMoriah Waterland 
2327*5c51f124SMoriah Waterland 	if (ps->data.prev_cont_length != 0)
2328*5c51f124SMoriah Waterland 		echo_out(nointeract, gettext(MSG_DWNLD_CONT));
2329*5c51f124SMoriah Waterland 	else
2330*5c51f124SMoriah Waterland 		echo_out(nointeract, gettext(MSG_DWNLD));
2331*5c51f124SMoriah Waterland 
2332*5c51f124SMoriah Waterland 	progress_setup(nointeract, ps->data.content_length);
2333*5c51f124SMoriah Waterland 
2334*5c51f124SMoriah Waterland 	/* Download the file a BLOCK at a time */
2335*5c51f124SMoriah Waterland 	while (ps->data.cur_pos < ps->data.content_length) {
2336*5c51f124SMoriah Waterland 		progress_report(nointeract, abs_pos);
2337*5c51f124SMoriah Waterland 		i = ((ps->data.content_length - ps->data.cur_pos) < BLOCK) ?
2338*5c51f124SMoriah Waterland 		    (ps->data.content_length - ps->data.cur_pos)
2339*5c51f124SMoriah Waterland 				: BLOCK;
2340*5c51f124SMoriah Waterland 		if ((n = http_read_body(ps->hps, ps->content, i)) <= 0) {
2341*5c51f124SMoriah Waterland 			while ((errcode = http_get_lasterr(ps->hps,
2342*5c51f124SMoriah Waterland 			    &errsrc)) != 0) {
2343*5c51f124SMoriah Waterland 				/* Have an error - is it EINTR? */
2344*5c51f124SMoriah Waterland 				if (errsrc == ERRSRC_SYSTEM) {
2345*5c51f124SMoriah Waterland 					my_errno = errcode;
2346*5c51f124SMoriah Waterland 					break;
2347*5c51f124SMoriah Waterland 				} else {
2348*5c51f124SMoriah Waterland 					/*
2349*5c51f124SMoriah Waterland 					 * save first non-system
2350*5c51f124SMoriah Waterland 					 * error message
2351*5c51f124SMoriah Waterland 					 */
2352*5c51f124SMoriah Waterland 					libhttperr =
2353*5c51f124SMoriah Waterland 					    http_errorstr(errsrc, errcode);
2354*5c51f124SMoriah Waterland 				}
2355*5c51f124SMoriah Waterland 			}
2356*5c51f124SMoriah Waterland 			switch (my_errno) {
2357*5c51f124SMoriah Waterland 			case EINTR:
2358*5c51f124SMoriah Waterland 			case ETIMEDOUT:
2359*5c51f124SMoriah Waterland 				/* Timed out.  Try, try again */
2360*5c51f124SMoriah Waterland 				ret = WEB_TIMEOUT;
2361*5c51f124SMoriah Waterland 				break;
2362*5c51f124SMoriah Waterland 			case ECONNREFUSED:
2363*5c51f124SMoriah Waterland 				ret = WEB_CONNREFUSED;
2364*5c51f124SMoriah Waterland 				break;
2365*5c51f124SMoriah Waterland 			case EHOSTDOWN:
2366*5c51f124SMoriah Waterland 				ret = WEB_HOSTDOWN;
2367*5c51f124SMoriah Waterland 				break;
2368*5c51f124SMoriah Waterland 			default:
2369*5c51f124SMoriah Waterland 				/* some other fatal error */
2370*5c51f124SMoriah Waterland 				ret = WEB_NOCONNECT;
2371*5c51f124SMoriah Waterland 				if (libhttperr == NULL) {
2372*5c51f124SMoriah Waterland 					pkgerr_add(err, PKGERR_WEB,
2373*5c51f124SMoriah Waterland 					    gettext(ERR_INIT_CONN),
2374*5c51f124SMoriah Waterland 					    ps->url.hport.hostname);
2375*5c51f124SMoriah Waterland 				} else {
2376*5c51f124SMoriah Waterland 					pkgerr_add(err, PKGERR_WEB,
2377*5c51f124SMoriah Waterland 					    gettext(ERR_HTTP), libhttperr);
2378*5c51f124SMoriah Waterland 				}
2379*5c51f124SMoriah Waterland 				break;
2380*5c51f124SMoriah Waterland 			}
2381*5c51f124SMoriah Waterland 			goto cleanup;
2382*5c51f124SMoriah Waterland 		}
2383*5c51f124SMoriah Waterland 		if ((n = write(fd, ps->content, n)) == 0) {
2384*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB, gettext(ERR_WRITE),
2385*5c51f124SMoriah Waterland 			    ps->uniqfile, strerror(errno));
2386*5c51f124SMoriah Waterland 			ret = WEB_GET_FAIL;
2387*5c51f124SMoriah Waterland 			goto cleanup;
2388*5c51f124SMoriah Waterland 		}
2389*5c51f124SMoriah Waterland 		ps->data.cur_pos += n;
2390*5c51f124SMoriah Waterland 		abs_pos += n;
2391*5c51f124SMoriah Waterland 	}
2392*5c51f124SMoriah Waterland 
2393*5c51f124SMoriah Waterland 	progress_finish(nointeract);
2394*5c51f124SMoriah Waterland 	echo_out(nointeract, gettext(MSG_DWNLD_COMPLETE));
2395*5c51f124SMoriah Waterland 
2396*5c51f124SMoriah Waterland 	if (!make_link(dwnld_dir, bname)) {
2397*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
2398*5c51f124SMoriah Waterland 		    dwnld_dir);
2399*5c51f124SMoriah Waterland 		ret = WEB_GET_FAIL;
2400*5c51f124SMoriah Waterland 		goto cleanup;
2401*5c51f124SMoriah Waterland 	}
2402*5c51f124SMoriah Waterland 
2403*5c51f124SMoriah Waterland cleanup:
2404*5c51f124SMoriah Waterland 	sync();
2405*5c51f124SMoriah Waterland 	if (fd != -1) {
2406*5c51f124SMoriah Waterland 		(void) close(fd);
2407*5c51f124SMoriah Waterland 	}
2408*5c51f124SMoriah Waterland 
2409*5c51f124SMoriah Waterland 	if (head_val != NULL)
2410*5c51f124SMoriah Waterland 		free(head_val);
2411*5c51f124SMoriah Waterland 
2412*5c51f124SMoriah Waterland 	if (lastmod_val != NULL)
2413*5c51f124SMoriah Waterland 		free(lastmod_val);
2414*5c51f124SMoriah Waterland 
2415*5c51f124SMoriah Waterland 	return (ret);
2416*5c51f124SMoriah Waterland }
2417*5c51f124SMoriah Waterland 
2418*5c51f124SMoriah Waterland /*
2419*5c51f124SMoriah Waterland  * Name:		make_link
2420*5c51f124SMoriah Waterland  * Description:	Create new link to file being downloaded
2421*5c51f124SMoriah Waterland  *
2422*5c51f124SMoriah Waterland  * Arguments:	dwnld_dir - directory in which downloaded file exists
2423*5c51f124SMoriah Waterland  *		bname - name of link
2424*5c51f124SMoriah Waterland  *
2425*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
2426*5c51f124SMoriah Waterland  */
2427*5c51f124SMoriah Waterland static boolean_t
2428*5c51f124SMoriah Waterland make_link(char *dwnld_dir, char *bname)
2429*5c51f124SMoriah Waterland {
2430*5c51f124SMoriah Waterland 	int len;
2431*5c51f124SMoriah Waterland 
2432*5c51f124SMoriah Waterland 	if ((ps->link = (char *)xmalloc(PATH_MAX)) == NULL)
2433*5c51f124SMoriah Waterland 		return (B_FALSE);
2434*5c51f124SMoriah Waterland 	if (((len = snprintf(ps->link, PATH_MAX, "%s/%s",
2435*5c51f124SMoriah Waterland 	    dwnld_dir, bname)) < 0) ||
2436*5c51f124SMoriah Waterland 	    len >= PATH_MAX)
2437*5c51f124SMoriah Waterland 		return (B_FALSE);
2438*5c51f124SMoriah Waterland 
2439*5c51f124SMoriah Waterland 	(void) link(ps->uniqfile, ps->link);
2440*5c51f124SMoriah Waterland 
2441*5c51f124SMoriah Waterland 	return (B_TRUE);
2442*5c51f124SMoriah Waterland }
2443*5c51f124SMoriah Waterland 
2444*5c51f124SMoriah Waterland /*
2445*5c51f124SMoriah Waterland  * Name:		get_startof_string
2446*5c51f124SMoriah Waterland  * Description:	searches string for token, returns a newly-allocated
2447*5c51f124SMoriah Waterland  *		substring of the given string up to, but not
2448*5c51f124SMoriah Waterland  *		including, token.  for example
2449*5c51f124SMoriah Waterland  *		get_startof_string("abcd", 'c') will return "ab"
2450*5c51f124SMoriah Waterland  *
2451*5c51f124SMoriah Waterland  * Arguments:	path - path to split
2452*5c51f124SMoriah Waterland  *     		token - character to split on
2453*5c51f124SMoriah Waterland  *
2454*5c51f124SMoriah Waterland  * Returns :	substring of 'path', up to, but not including,
2455*5c51f124SMoriah Waterland  *		token, if token appears in path.  Otherwise,
2456*5c51f124SMoriah Waterland  *		returns NULL.
2457*5c51f124SMoriah Waterland  */
2458*5c51f124SMoriah Waterland char *
2459*5c51f124SMoriah Waterland get_startof_string(char *path, char token)
2460*5c51f124SMoriah Waterland {
2461*5c51f124SMoriah Waterland 	char *p, *p2;
2462*5c51f124SMoriah Waterland 
2463*5c51f124SMoriah Waterland 	if (path == NULL)
2464*5c51f124SMoriah Waterland 		return (NULL);
2465*5c51f124SMoriah Waterland 
2466*5c51f124SMoriah Waterland 	p = xstrdup(path);
2467*5c51f124SMoriah Waterland 
2468*5c51f124SMoriah Waterland 	p2 = strchr(p, token);
2469*5c51f124SMoriah Waterland 	if (p2 == NULL) {
2470*5c51f124SMoriah Waterland 		free(p);
2471*5c51f124SMoriah Waterland 		return (NULL);
2472*5c51f124SMoriah Waterland 	} else {
2473*5c51f124SMoriah Waterland 		*p2 = '\0';
2474*5c51f124SMoriah Waterland 		return (p);
2475*5c51f124SMoriah Waterland 	}
2476*5c51f124SMoriah Waterland }
2477*5c51f124SMoriah Waterland 
2478*5c51f124SMoriah Waterland /*
2479*5c51f124SMoriah Waterland  * Name:		get_endof_string
2480*5c51f124SMoriah Waterland  * Description:	searches string for token, returns a
2481*5c51f124SMoriah Waterland  *		newly-allocated substring of the given string,
2482*5c51f124SMoriah Waterland  *		starting at character following token, to end of
2483*5c51f124SMoriah Waterland  *		string.
2484*5c51f124SMoriah Waterland  *
2485*5c51f124SMoriah Waterland  *		for example get_end_string("abcd", 'c')
2486*5c51f124SMoriah Waterland  *		will return "d"
2487*5c51f124SMoriah Waterland  *
2488*5c51f124SMoriah Waterland  * Arguments:	path - path to split
2489*5c51f124SMoriah Waterland  *     		token - character to split on
2490*5c51f124SMoriah Waterland  *
2491*5c51f124SMoriah Waterland  * Returns :	substring of 'path', beginning at character
2492*5c51f124SMoriah Waterland  *		following token, to end of string, if
2493*5c51f124SMoriah Waterland  *		token appears in path.  Otherwise,
2494*5c51f124SMoriah Waterland  * returns NULL.
2495*5c51f124SMoriah Waterland  */
2496*5c51f124SMoriah Waterland char *
2497*5c51f124SMoriah Waterland get_endof_string(char *path, char token)
2498*5c51f124SMoriah Waterland {
2499*5c51f124SMoriah Waterland 	char *p, *p2;
2500*5c51f124SMoriah Waterland 
2501*5c51f124SMoriah Waterland 	if (path == NULL)
2502*5c51f124SMoriah Waterland 		return (NULL);
2503*5c51f124SMoriah Waterland 
2504*5c51f124SMoriah Waterland 	p = xstrdup(path);
2505*5c51f124SMoriah Waterland 
2506*5c51f124SMoriah Waterland 	if ((p2 = strrchr(p, token)) == NULL) {
2507*5c51f124SMoriah Waterland 		return (NULL);
2508*5c51f124SMoriah Waterland 	}
2509*5c51f124SMoriah Waterland 
2510*5c51f124SMoriah Waterland 	return (p2 + 1);
2511*5c51f124SMoriah Waterland }
2512*5c51f124SMoriah Waterland 
2513*5c51f124SMoriah Waterland /*
2514*5c51f124SMoriah Waterland  * Name:		progress_setup
2515*5c51f124SMoriah Waterland  * Description:	Initialize session for reporting progress
2516*5c51f124SMoriah Waterland  *
2517*5c51f124SMoriah Waterland  * Arguments:	nointeract - if non-zero, do not do anything
2518*5c51f124SMoriah Waterland  *		ulong_t - size of job to report progress for
2519*5c51f124SMoriah Waterland  *
2520*5c51f124SMoriah Waterland  * Returns :	none
2521*5c51f124SMoriah Waterland  */
2522*5c51f124SMoriah Waterland static void
2523*5c51f124SMoriah Waterland progress_setup(int nointeract, ulong_t size_of_load)
2524*5c51f124SMoriah Waterland {
2525*5c51f124SMoriah Waterland 	ulong_t divisor;
2526*5c51f124SMoriah Waterland 	ulong_t term_width = TERM_WIDTH;
2527*5c51f124SMoriah Waterland 
2528*5c51f124SMoriah Waterland 	if (nointeract)
2529*5c51f124SMoriah Waterland 		return;
2530*5c51f124SMoriah Waterland 
2531*5c51f124SMoriah Waterland 	if (size_of_load > MED_DWNLD && size_of_load < LARGE_DWNLD)
2532*5c51f124SMoriah Waterland 		divisor = MED_DIVISOR;
2533*5c51f124SMoriah Waterland 	else if (size_of_load > LARGE_DWNLD) {
2534*5c51f124SMoriah Waterland 		term_width = TERM_WIDTH - 8;
2535*5c51f124SMoriah Waterland 		divisor = LARGE_DIVISOR;
2536*5c51f124SMoriah Waterland 	} else
2537*5c51f124SMoriah Waterland 		divisor = SMALL_DIVISOR;
2538*5c51f124SMoriah Waterland 
2539*5c51f124SMoriah Waterland 	const_increment = size_of_load / term_width;
2540*5c51f124SMoriah Waterland 	const_divider = size_of_load / divisor;
2541*5c51f124SMoriah Waterland 	const_completed = 100 / divisor;
2542*5c51f124SMoriah Waterland }
2543*5c51f124SMoriah Waterland 
2544*5c51f124SMoriah Waterland /*
2545*5c51f124SMoriah Waterland  * Name:		progress_report
2546*5c51f124SMoriah Waterland  * Description:	Report progress for current progress context,
2547*5c51f124SMoriah Waterland  *		to stderr
2548*5c51f124SMoriah Waterland  *
2549*5c51f124SMoriah Waterland  * Arguments:	nointeract - if non-zero, do not do anything
2550*5c51f124SMoriah Waterland  *		position - how far along in the job to report.
2551*5c51f124SMoriah Waterland  *		This should be <= size used during progress_setup
2552*5c51f124SMoriah Waterland  *
2553*5c51f124SMoriah Waterland  * Returns :	none
2554*5c51f124SMoriah Waterland  */
2555*5c51f124SMoriah Waterland static void
2556*5c51f124SMoriah Waterland progress_report(int nointeract, ulong_t position)
2557*5c51f124SMoriah Waterland {
2558*5c51f124SMoriah Waterland 	static ulong_t increment;
2559*5c51f124SMoriah Waterland 	static ulong_t divider;
2560*5c51f124SMoriah Waterland 
2561*5c51f124SMoriah Waterland 	if (nointeract)
2562*5c51f124SMoriah Waterland 		return;
2563*5c51f124SMoriah Waterland 
2564*5c51f124SMoriah Waterland 	if (position == 0) {
2565*5c51f124SMoriah Waterland 		increment = const_increment;
2566*5c51f124SMoriah Waterland 		divider = const_divider;
2567*5c51f124SMoriah Waterland 	}
2568*5c51f124SMoriah Waterland 	if (position > increment && position < divider) {
2569*5c51f124SMoriah Waterland 		(void) putc('.', stderr);
2570*5c51f124SMoriah Waterland 		increment += const_increment;
2571*5c51f124SMoriah Waterland 	} else if (position > divider) {
2572*5c51f124SMoriah Waterland 		completed += const_completed;
2573*5c51f124SMoriah Waterland 		(void) fprintf(stderr, "%ld%c", completed, '%');
2574*5c51f124SMoriah Waterland 		increment += const_increment;
2575*5c51f124SMoriah Waterland 		divider += const_divider;
2576*5c51f124SMoriah Waterland 	}
2577*5c51f124SMoriah Waterland }
2578*5c51f124SMoriah Waterland 
2579*5c51f124SMoriah Waterland /*
2580*5c51f124SMoriah Waterland  * Name:		progress_finish
2581*5c51f124SMoriah Waterland  * Description:	Finalize session for reporting progress.
2582*5c51f124SMoriah Waterland  *		"100%" is reported to screen
2583*5c51f124SMoriah Waterland  *
2584*5c51f124SMoriah Waterland  * Arguments:	nointeract - if non-zero, do not do anything
2585*5c51f124SMoriah Waterland  *
2586*5c51f124SMoriah Waterland  * Returns :	none
2587*5c51f124SMoriah Waterland  */
2588*5c51f124SMoriah Waterland static void
2589*5c51f124SMoriah Waterland progress_finish(int nointeract)
2590*5c51f124SMoriah Waterland {
2591*5c51f124SMoriah Waterland 	if (nointeract)
2592*5c51f124SMoriah Waterland 		return;
2593*5c51f124SMoriah Waterland 
2594*5c51f124SMoriah Waterland 	(void) fprintf(stderr, "%d%c\n", 100, '%');
2595*5c51f124SMoriah Waterland }
2596*5c51f124SMoriah Waterland 
2597*5c51f124SMoriah Waterland /*
2598*5c51f124SMoriah Waterland  * Name:		init_session
2599*5c51f124SMoriah Waterland  * Description:	Initializes static 'ps' structure with default
2600*5c51f124SMoriah Waterland  *		values
2601*5c51f124SMoriah Waterland  *
2602*5c51f124SMoriah Waterland  * Arguments:	none
2603*5c51f124SMoriah Waterland  *
2604*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
2605*5c51f124SMoriah Waterland  */
2606*5c51f124SMoriah Waterland static boolean_t
2607*5c51f124SMoriah Waterland init_session(void)
2608*5c51f124SMoriah Waterland {
2609*5c51f124SMoriah Waterland 	if ((ps = (WEB_SESSION *)
2610*5c51f124SMoriah Waterland 		xmalloc(sizeof (WEB_SESSION))) == NULL) {
2611*5c51f124SMoriah Waterland 		return (B_FALSE);
2612*5c51f124SMoriah Waterland 	}
2613*5c51f124SMoriah Waterland 	(void) memset(ps, 0, sizeof (*ps));
2614*5c51f124SMoriah Waterland 
2615*5c51f124SMoriah Waterland 	if ((ps->content = (char *)xmalloc(BLOCK)) == NULL) {
2616*5c51f124SMoriah Waterland 		return (B_FALSE);
2617*5c51f124SMoriah Waterland 	}
2618*5c51f124SMoriah Waterland 
2619*5c51f124SMoriah Waterland 	(void) memset(ps->content, 0, BLOCK);
2620*5c51f124SMoriah Waterland 
2621*5c51f124SMoriah Waterland 	ps->data.cur_pos = 0UL;
2622*5c51f124SMoriah Waterland 	ps->data.content_length = 0UL;
2623*5c51f124SMoriah Waterland 	ps->url.https = B_FALSE;
2624*5c51f124SMoriah Waterland 	ps->uniqfile = NULL;
2625*5c51f124SMoriah Waterland 	ps->link = NULL;
2626*5c51f124SMoriah Waterland 	ps->dwnld_dir = NULL;
2627*5c51f124SMoriah Waterland 	ps->spool = B_TRUE;
2628*5c51f124SMoriah Waterland 	ps->errstr = NULL;
2629*5c51f124SMoriah Waterland 	ps->keystore = NULL;
2630*5c51f124SMoriah Waterland 
2631*5c51f124SMoriah Waterland 	return (B_TRUE);
2632*5c51f124SMoriah Waterland }
2633*5c51f124SMoriah Waterland 
2634*5c51f124SMoriah Waterland /*
2635*5c51f124SMoriah Waterland  * Name:		ck_downld_dir_space
2636*5c51f124SMoriah Waterland  * Description:	Verify enough space exists in directory to hold file
2637*5c51f124SMoriah Waterland  *
2638*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
2639*5c51f124SMoriah Waterland  *     		dwnld_dir - Directory to check available space in
2640*5c51f124SMoriah Waterland  *		bytes_needed - How many bytes are need
2641*5c51f124SMoriah Waterland  *
2642*5c51f124SMoriah Waterland  * Returns :	B_TRUE - enough space exists in dwnld_dir to hold
2643*5c51f124SMoriah Waterland  *		bytes_needed bytes, otherwise B_FALSE
2644*5c51f124SMoriah Waterland  */
2645*5c51f124SMoriah Waterland static boolean_t
2646*5c51f124SMoriah Waterland ck_dwnld_dir_space(PKG_ERR *err, char *dwnld_dir, ulong_t bytes_needed)
2647*5c51f124SMoriah Waterland {
2648*5c51f124SMoriah Waterland 	u_longlong_t bytes_avail;
2649*5c51f124SMoriah Waterland 	u_longlong_t block_pad;
2650*5c51f124SMoriah Waterland 	struct statvfs64 status;
2651*5c51f124SMoriah Waterland 
2652*5c51f124SMoriah Waterland 	if (statvfs64(dwnld_dir, &status)) {
2653*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_TMPDIR), dwnld_dir);
2654*5c51f124SMoriah Waterland 		return (B_FALSE);
2655*5c51f124SMoriah Waterland 	}
2656*5c51f124SMoriah Waterland 
2657*5c51f124SMoriah Waterland 	block_pad = (status.f_frsize ? status.f_frsize : status.f_bsize);
2658*5c51f124SMoriah Waterland 	bytes_avail = status.f_bavail * block_pad;
2659*5c51f124SMoriah Waterland 
2660*5c51f124SMoriah Waterland 	if ((((u_longlong_t)bytes_needed) + block_pad) > bytes_avail) {
2661*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_WEB, gettext(ERR_DISK_SPACE),
2662*5c51f124SMoriah Waterland 		    dwnld_dir,
2663*5c51f124SMoriah Waterland 		    (((u_longlong_t)bytes_needed) + block_pad) / 1024ULL,
2664*5c51f124SMoriah Waterland 		    bytes_avail / 1024ULL);
2665*5c51f124SMoriah Waterland 		return (B_FALSE);
2666*5c51f124SMoriah Waterland 	}
2667*5c51f124SMoriah Waterland 
2668*5c51f124SMoriah Waterland 	return (B_TRUE);
2669*5c51f124SMoriah Waterland }
2670*5c51f124SMoriah Waterland 
2671*5c51f124SMoriah Waterland /*
2672*5c51f124SMoriah Waterland  * Description:
2673*5c51f124SMoriah Waterland  *    This function returns a unique file name based on the parts of the
2674*5c51f124SMoriah Waterland  *    URI. This is done to enable partially downloaded files to be resumed.
2675*5c51f124SMoriah Waterland  * Arguments:
2676*5c51f124SMoriah Waterland  *    dir - The directory that should contain the filename.
2677*5c51f124SMoriah Waterland  *    last_modified - A string representing the date of last modification,
2678*5c51f124SMoriah Waterland  *	used as part of generating unique name
2679*5c51f124SMoriah Waterland  * Returns:
2680*5c51f124SMoriah Waterland  *    A valid filename or NULL.
2681*5c51f124SMoriah Waterland  */
2682*5c51f124SMoriah Waterland 
2683*5c51f124SMoriah Waterland static char *
2684*5c51f124SMoriah Waterland get_unique_filename(char *dir, char *last_modified)
2685*5c51f124SMoriah Waterland {
2686*5c51f124SMoriah Waterland 	char *buf, *buf2, *beg_str;
2687*5c51f124SMoriah Waterland 	int len;
2688*5c51f124SMoriah Waterland 
2689*5c51f124SMoriah Waterland 	if ((buf = (char *)xmalloc(PATH_MAX)) == NULL) {
2690*5c51f124SMoriah Waterland 		return (NULL);
2691*5c51f124SMoriah Waterland 	}
2692*5c51f124SMoriah Waterland 	if ((buf2 = (char *)xmalloc(PATH_MAX)) == NULL) {
2693*5c51f124SMoriah Waterland 		return (NULL);
2694*5c51f124SMoriah Waterland 	}
2695*5c51f124SMoriah Waterland 
2696*5c51f124SMoriah Waterland 	/* prepare strings for being cat'ed onto */
2697*5c51f124SMoriah Waterland 	buf[0] = buf2[0] = '\0';
2698*5c51f124SMoriah Waterland 	/*
2699*5c51f124SMoriah Waterland 	 * No validation of the path is done here. We just construct the path
2700*5c51f124SMoriah Waterland 	 * and it must be validated later
2701*5c51f124SMoriah Waterland 	 */
2702*5c51f124SMoriah Waterland 
2703*5c51f124SMoriah Waterland 	if (dir) {
2704*5c51f124SMoriah Waterland 		if (((len = snprintf(buf2, PATH_MAX, "%s/", dir)) < 0) ||
2705*5c51f124SMoriah Waterland 		    (len >= PATH_MAX))
2706*5c51f124SMoriah Waterland 			return (NULL);
2707*5c51f124SMoriah Waterland 	} else {
2708*5c51f124SMoriah Waterland 		return (NULL);
2709*5c51f124SMoriah Waterland 	}
2710*5c51f124SMoriah Waterland 
2711*5c51f124SMoriah Waterland 	if (ps->url.abspath)
2712*5c51f124SMoriah Waterland 		if (strlcat(buf, ps->url.abspath, PATH_MAX) >= PATH_MAX)
2713*5c51f124SMoriah Waterland 			return (NULL);
2714*5c51f124SMoriah Waterland 	if (ps->url.hport.hostname)
2715*5c51f124SMoriah Waterland 		if (isdigit((int)ps->url.hport.hostname[0])) {
2716*5c51f124SMoriah Waterland 			if (strlcat(buf, ps->url.hport.hostname, PATH_MAX)
2717*5c51f124SMoriah Waterland 					>= PATH_MAX)
2718*5c51f124SMoriah Waterland 				return (NULL);
2719*5c51f124SMoriah Waterland 		} else {
2720*5c51f124SMoriah Waterland 			if ((beg_str =
2721*5c51f124SMoriah Waterland 				get_startof_string(ps->url.hport.hostname, '.'))
2722*5c51f124SMoriah Waterland 					!= NULL)
2723*5c51f124SMoriah Waterland 				if (strlcat(buf, beg_str, PATH_MAX) >= PATH_MAX)
2724*5c51f124SMoriah Waterland 					return (NULL);
2725*5c51f124SMoriah Waterland 		}
2726*5c51f124SMoriah Waterland 	if (last_modified != NULL)
2727*5c51f124SMoriah Waterland 		if (strlcat(buf, last_modified, PATH_MAX) >= PATH_MAX)
2728*5c51f124SMoriah Waterland 			return (NULL);
2729*5c51f124SMoriah Waterland 
2730*5c51f124SMoriah Waterland 	if ((buf = replace_token(buf, '/', '_')) != NULL) {
2731*5c51f124SMoriah Waterland 		if (strlcat(buf2, buf, PATH_MAX) >= PATH_MAX) {
2732*5c51f124SMoriah Waterland 			return (NULL);
2733*5c51f124SMoriah Waterland 		} else {
2734*5c51f124SMoriah Waterland 			if (buf) free(buf);
2735*5c51f124SMoriah Waterland 			return (buf2);
2736*5c51f124SMoriah Waterland 		}
2737*5c51f124SMoriah Waterland 	} else {
2738*5c51f124SMoriah Waterland 		if (buf) free(buf);
2739*5c51f124SMoriah Waterland 		if (buf2) free(buf2);
2740*5c51f124SMoriah Waterland 		return (NULL);
2741*5c51f124SMoriah Waterland 	}
2742*5c51f124SMoriah Waterland }
2743*5c51f124SMoriah Waterland 
2744*5c51f124SMoriah Waterland /*
2745*5c51f124SMoriah Waterland  * Description:
2746*5c51f124SMoriah Waterland  *    Removes token(s) consisting of one character from any path.
2747*5c51f124SMoriah Waterland  * Arguments:
2748*5c51f124SMoriah Waterland  *    path  - The path to search for the token in.
2749*5c51f124SMoriah Waterland  *    token - The token to search for
2750*5c51f124SMoriah Waterland  * Returns:
2751*5c51f124SMoriah Waterland  *    The path with all tokens removed or NULL.
2752*5c51f124SMoriah Waterland  */
2753*5c51f124SMoriah Waterland static char *
2754*5c51f124SMoriah Waterland replace_token(char *path, char oldtoken, char newtoken)
2755*5c51f124SMoriah Waterland {
2756*5c51f124SMoriah Waterland 	char *newpath, *p;
2757*5c51f124SMoriah Waterland 
2758*5c51f124SMoriah Waterland 	if ((path == NULL) || (oldtoken == '\0') || (newtoken == '\0')) {
2759*5c51f124SMoriah Waterland 		return (NULL);
2760*5c51f124SMoriah Waterland 	}
2761*5c51f124SMoriah Waterland 
2762*5c51f124SMoriah Waterland 	newpath = xstrdup(path);
2763*5c51f124SMoriah Waterland 
2764*5c51f124SMoriah Waterland 	for (p = newpath; *p != '\0'; p++) {
2765*5c51f124SMoriah Waterland 		if (*p == oldtoken) {
2766*5c51f124SMoriah Waterland 			*p = newtoken;
2767*5c51f124SMoriah Waterland 		}
2768*5c51f124SMoriah Waterland 	}
2769*5c51f124SMoriah Waterland 
2770*5c51f124SMoriah Waterland 	return (newpath);
2771*5c51f124SMoriah Waterland }
2772*5c51f124SMoriah Waterland 
2773*5c51f124SMoriah Waterland /*
2774*5c51f124SMoriah Waterland  * Name:        trim
2775*5c51f124SMoriah Waterland  * Description: Trims whitespace from a string
2776*5c51f124SMoriah Waterland  *              has been registered)
2777*5c51f124SMoriah Waterland  * Scope:       private
2778*5c51f124SMoriah Waterland  * Arguments:   string  - string to trim.  It is assumed
2779*5c51f124SMoriah Waterland  *              this string is writable up to it's entire
2780*5c51f124SMoriah Waterland  *              length.
2781*5c51f124SMoriah Waterland  * Returns:     none
2782*5c51f124SMoriah Waterland  */
2783*5c51f124SMoriah Waterland static void
2784*5c51f124SMoriah Waterland trim(char *str)
2785*5c51f124SMoriah Waterland {
2786*5c51f124SMoriah Waterland 	int len, i;
2787*5c51f124SMoriah Waterland 	if (str == NULL) {
2788*5c51f124SMoriah Waterland 		return;
2789*5c51f124SMoriah Waterland 	}
2790*5c51f124SMoriah Waterland 
2791*5c51f124SMoriah Waterland 	len = strlen(str);
2792*5c51f124SMoriah Waterland 	/* strip from front */
2793*5c51f124SMoriah Waterland 	while (isspace(*str)) {
2794*5c51f124SMoriah Waterland 		for (i = 0; i < len; i++) {
2795*5c51f124SMoriah Waterland 			str[i] = str[i+1];
2796*5c51f124SMoriah Waterland 		}
2797*5c51f124SMoriah Waterland 	}
2798*5c51f124SMoriah Waterland 
2799*5c51f124SMoriah Waterland 	/* strip from back */
2800*5c51f124SMoriah Waterland 	len = strlen(str);
2801*5c51f124SMoriah Waterland 	while (isspace(str[len-1])) {
2802*5c51f124SMoriah Waterland 		len--;
2803*5c51f124SMoriah Waterland 	}
2804*5c51f124SMoriah Waterland 	str[len] = '\0';
2805*5c51f124SMoriah Waterland }
2806*5c51f124SMoriah Waterland 
2807*5c51f124SMoriah Waterland /*
2808*5c51f124SMoriah Waterland  * Description:
2809*5c51f124SMoriah Waterland  *    Resolves double quotes
2810*5c51f124SMoriah Waterland  * Arguments:
2811*5c51f124SMoriah Waterland  *    str  - The string to resolve
2812*5c51f124SMoriah Waterland  * Returns:
2813*5c51f124SMoriah Waterland  *    None
2814*5c51f124SMoriah Waterland  */
2815*5c51f124SMoriah Waterland static void
2816*5c51f124SMoriah Waterland dequote(char *str)
2817*5c51f124SMoriah Waterland {
2818*5c51f124SMoriah Waterland 	char *cp;
2819*5c51f124SMoriah Waterland 
2820*5c51f124SMoriah Waterland 	if ((str == NULL) || (str[0] != '"')) {
2821*5c51f124SMoriah Waterland 		/* no quotes */
2822*5c51f124SMoriah Waterland 		return;
2823*5c51f124SMoriah Waterland 	}
2824*5c51f124SMoriah Waterland 
2825*5c51f124SMoriah Waterland 	/* remove first quote */
2826*5c51f124SMoriah Waterland 	memmove(str, str + 1, strlen(str) - 1);
2827*5c51f124SMoriah Waterland 
2828*5c51f124SMoriah Waterland 	/*
2829*5c51f124SMoriah Waterland 	 * scan string looking for ending quote.
2830*5c51f124SMoriah Waterland 	 * escaped quotes like \" don't count
2831*5c51f124SMoriah Waterland 	 */
2832*5c51f124SMoriah Waterland 	cp = str;
2833*5c51f124SMoriah Waterland 
2834*5c51f124SMoriah Waterland 	while (*cp != '\0') {
2835*5c51f124SMoriah Waterland 		switch (*cp) {
2836*5c51f124SMoriah Waterland 		case '\\':
2837*5c51f124SMoriah Waterland 			/* found an escaped character */
2838*5c51f124SMoriah Waterland 			/* make sure end of string is not '\' */
2839*5c51f124SMoriah Waterland 			if (*++cp != '\0') {
2840*5c51f124SMoriah Waterland 				cp++;
2841*5c51f124SMoriah Waterland 			}
2842*5c51f124SMoriah Waterland 			break;
2843*5c51f124SMoriah Waterland 
2844*5c51f124SMoriah Waterland 		case '"':
2845*5c51f124SMoriah Waterland 			*cp = '\0';
2846*5c51f124SMoriah Waterland 			break;
2847*5c51f124SMoriah Waterland 		default:
2848*5c51f124SMoriah Waterland 			cp++;
2849*5c51f124SMoriah Waterland 		}
2850*5c51f124SMoriah Waterland 	}
2851*5c51f124SMoriah Waterland }
2852*5c51f124SMoriah Waterland 
2853*5c51f124SMoriah Waterland /*
2854*5c51f124SMoriah Waterland  * Name:		get_ENV_proxy
2855*5c51f124SMoriah Waterland  * Description:	Retrieves setting of proxy env variable
2856*5c51f124SMoriah Waterland  *
2857*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
2858*5c51f124SMoriah Waterland  *		proxy - where to store proxy
2859*5c51f124SMoriah Waterland  *
2860*5c51f124SMoriah Waterland  * Returns :	B_TRUE - http proxy was found and valid, stored in proxy
2861*5c51f124SMoriah Waterland  *		B_FALSE - error, errors recorded in err
2862*5c51f124SMoriah Waterland  */
2863*5c51f124SMoriah Waterland static boolean_t
2864*5c51f124SMoriah Waterland get_ENV_proxy(PKG_ERR *err, char **proxy)
2865*5c51f124SMoriah Waterland {
2866*5c51f124SMoriah Waterland 	char *buf;
2867*5c51f124SMoriah Waterland 
2868*5c51f124SMoriah Waterland 	if ((buf = getenv("HTTPPROXY")) != NULL) {
2869*5c51f124SMoriah Waterland 		if (!path_valid(buf)) {
2870*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
2871*5c51f124SMoriah Waterland 			    gettext(ERR_ILL_ENV), "HTTPPROXY", buf);
2872*5c51f124SMoriah Waterland 			return (B_FALSE);
2873*5c51f124SMoriah Waterland 		} else {
2874*5c51f124SMoriah Waterland 			*proxy = buf;
2875*5c51f124SMoriah Waterland 			return (B_TRUE);
2876*5c51f124SMoriah Waterland 		}
2877*5c51f124SMoriah Waterland 	} else {
2878*5c51f124SMoriah Waterland 		/* try the other env variable */
2879*5c51f124SMoriah Waterland 		if ((buf = getenv("http_proxy")) != NULL) {
2880*5c51f124SMoriah Waterland 			if (!path_valid(buf)) {
2881*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_WEB,
2882*5c51f124SMoriah Waterland 				    gettext(ERR_ILL_ENV), "http_proxy", buf);
2883*5c51f124SMoriah Waterland 				return (B_FALSE);
2884*5c51f124SMoriah Waterland 			}
2885*5c51f124SMoriah Waterland 			if (!strneq(buf, "http://", 7)) {
2886*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_WEB,
2887*5c51f124SMoriah Waterland 				    gettext(ERR_ILL_ENV), "http_proxy", buf);
2888*5c51f124SMoriah Waterland 				return (B_FALSE);
2889*5c51f124SMoriah Waterland 			}
2890*5c51f124SMoriah Waterland 
2891*5c51f124SMoriah Waterland 			/* skip over the http:// part of the proxy "url" */
2892*5c51f124SMoriah Waterland 			    *proxy = buf + 7;
2893*5c51f124SMoriah Waterland 			    return (B_TRUE);
2894*5c51f124SMoriah Waterland 		}
2895*5c51f124SMoriah Waterland 	}
2896*5c51f124SMoriah Waterland 
2897*5c51f124SMoriah Waterland 	/* either the env variable(s) were set and valid, or not set */
2898*5c51f124SMoriah Waterland 	return (B_TRUE);
2899*5c51f124SMoriah Waterland }
2900*5c51f124SMoriah Waterland 
2901*5c51f124SMoriah Waterland /*
2902*5c51f124SMoriah Waterland  * Name:		get_ENV_proxyport
2903*5c51f124SMoriah Waterland  * Description:	Retrieves setting of PROXYPORT env variable
2904*5c51f124SMoriah Waterland  *
2905*5c51f124SMoriah Waterland  * Arguments:	err - where to record any errors.
2906*5c51f124SMoriah Waterland  *		port - where to store resulting port
2907*5c51f124SMoriah Waterland  *
2908*5c51f124SMoriah Waterland  * Returns :	B_TRUE - string found in PROXYPORT variable, converted
2909*5c51f124SMoriah Waterland  *		to decimal integer, if it exists
2910*5c51f124SMoriah Waterland  *		and is valid.  Or, PROXYPORT not set, port set to 1.
2911*5c51f124SMoriah Waterland  *		B_FALSE - env variable set, but invalid
2912*5c51f124SMoriah Waterland  *			(not a number for example)
2913*5c51f124SMoriah Waterland  */
2914*5c51f124SMoriah Waterland static boolean_t
2915*5c51f124SMoriah Waterland get_ENV_proxyport(PKG_ERR *err, ushort_t *port)
2916*5c51f124SMoriah Waterland {
2917*5c51f124SMoriah Waterland 	char *buf;
2918*5c51f124SMoriah Waterland 	ushort_t	newport;
2919*5c51f124SMoriah Waterland 	buf = getenv("HTTPPROXYPORT");
2920*5c51f124SMoriah Waterland 	if (buf != NULL) {
2921*5c51f124SMoriah Waterland 		if (!path_valid(buf)) {
2922*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
2923*5c51f124SMoriah Waterland 			    gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
2924*5c51f124SMoriah Waterland 			return (B_FALSE);
2925*5c51f124SMoriah Waterland 		}
2926*5c51f124SMoriah Waterland 		if ((newport = atoi(buf)) == 0) {
2927*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_WEB,
2928*5c51f124SMoriah Waterland 			    gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
2929*5c51f124SMoriah Waterland 			return (B_FALSE);
2930*5c51f124SMoriah Waterland 		}
2931*5c51f124SMoriah Waterland 		*port = newport;
2932*5c51f124SMoriah Waterland 		return (B_TRUE);
2933*5c51f124SMoriah Waterland 	} else {
2934*5c51f124SMoriah Waterland 		*port = 1;
2935*5c51f124SMoriah Waterland 		return (B_TRUE);
2936*5c51f124SMoriah Waterland 	}
2937*5c51f124SMoriah Waterland }
2938*5c51f124SMoriah Waterland 
2939*5c51f124SMoriah Waterland /*
2940*5c51f124SMoriah Waterland  * Name:		remove_dwnld_file
2941*5c51f124SMoriah Waterland  * Description:	Removes newly-downloaded file if completely downloaded.
2942*5c51f124SMoriah Waterland  *
2943*5c51f124SMoriah Waterland  * Arguments:	path - path to file to remove
2944*5c51f124SMoriah Waterland  *
2945*5c51f124SMoriah Waterland  * Returns :	B_TRUE - success, B_FALSE otherwise
2946*5c51f124SMoriah Waterland  *		if it's '0' (not OK) we simply return it, since the
2947*5c51f124SMoriah Waterland  *		verification operation has already determined that the
2948*5c51f124SMoriah Waterland  *		cert is invalid.  if 'ok' is non-zero, then we do our
2949*5c51f124SMoriah Waterland  *		checks, and return 0 or 1 based on if the cert is
2950*5c51f124SMoriah Waterland  *		invalid or valid.
2951*5c51f124SMoriah Waterland  */
2952*5c51f124SMoriah Waterland static boolean_t
2953*5c51f124SMoriah Waterland remove_dwnld_file(char *path)
2954*5c51f124SMoriah Waterland {
2955*5c51f124SMoriah Waterland 	if (path && path != NULL) {
2956*5c51f124SMoriah Waterland 		/*
2957*5c51f124SMoriah Waterland 		 * Only remove the downloaded file if it has been completely
2958*5c51f124SMoriah Waterland 		 * downloaded, or is not eligible for spooling
2959*5c51f124SMoriah Waterland 		 */
2960*5c51f124SMoriah Waterland 		if ((!ps->spool) ||
2961*5c51f124SMoriah Waterland 		    (ps->data.cur_pos  >= ps->data.content_length)) {
2962*5c51f124SMoriah Waterland 			(void) unlink(path);
2963*5c51f124SMoriah Waterland 		}
2964*5c51f124SMoriah Waterland 	} else {
2965*5c51f124SMoriah Waterland 		return (B_FALSE);
2966*5c51f124SMoriah Waterland 	}
2967*5c51f124SMoriah Waterland 	return (B_TRUE);
2968*5c51f124SMoriah Waterland }
2969*5c51f124SMoriah Waterland 
2970*5c51f124SMoriah Waterland /*
2971*5c51f124SMoriah Waterland  * Name:		condense_lastmodifided
2972*5c51f124SMoriah Waterland  * Description:	generates a substring of a last-modified string,
2973*5c51f124SMoriah Waterland  *		and removes colons.
2974*5c51f124SMoriah Waterland  *
2975*5c51f124SMoriah Waterland  * Arguments:	last_modified - string of the form
2976*5c51f124SMoriah Waterland  *		"Wed, 23 Oct 2002 21:59:45 GMT"
2977*5c51f124SMoriah Waterland  *
2978*5c51f124SMoriah Waterland  * Returns :
2979*5c51f124SMoriah Waterland  *		new string, consisting of hours/minutes/seconds only,
2980*5c51f124SMoriah Waterland  *		sans any colons.
2981*5c51f124SMoriah Waterland  */
2982*5c51f124SMoriah Waterland char *
2983*5c51f124SMoriah Waterland condense_lastmodified(char *last_modified)
2984*5c51f124SMoriah Waterland {
2985*5c51f124SMoriah Waterland 	char *p, *p2;
2986*5c51f124SMoriah Waterland 
2987*5c51f124SMoriah Waterland 	/*
2988*5c51f124SMoriah Waterland 	 * Last-Modified: Wed, 23 Oct 2002 21:59:45 GMT
2989*5c51f124SMoriah Waterland 	 * Strip the hours, minutes and seconds, without the ':'s, from
2990*5c51f124SMoriah Waterland 	 * the above string, void of the ':".
2991*5c51f124SMoriah Waterland 	 */
2992*5c51f124SMoriah Waterland 
2993*5c51f124SMoriah Waterland 	if (last_modified == NULL)
2994*5c51f124SMoriah Waterland 		return (NULL);
2995*5c51f124SMoriah Waterland 
2996*5c51f124SMoriah Waterland 	if ((p = xstrdup(last_modified)) == NULL)
2997*5c51f124SMoriah Waterland 		return (NULL);
2998*5c51f124SMoriah Waterland 	p2 = (strstr(p, ":") - 2);
2999*5c51f124SMoriah Waterland 	p2[8] = '\0';
3000*5c51f124SMoriah Waterland 	return (replace_token(p2, ':', '_'));
3001*5c51f124SMoriah Waterland }
3002*5c51f124SMoriah Waterland 
3003*5c51f124SMoriah Waterland /*
3004*5c51f124SMoriah Waterland  * Name:		backoff
3005*5c51f124SMoriah Waterland  * Description:	sleeps for a certain # of seconds after a network
3006*5c51f124SMoriah Waterland  *		failure.
3007*5c51f124SMoriah Waterland  * Scope:	public
3008*5c51f124SMoriah Waterland  * Arguments:	none
3009*5c51f124SMoriah Waterland  * Returns:	none
3010*5c51f124SMoriah Waterland  */
3011*5c51f124SMoriah Waterland void
3012*5c51f124SMoriah Waterland backoff()
3013*5c51f124SMoriah Waterland {
3014*5c51f124SMoriah Waterland 	static boolean_t initted = B_FALSE;
3015*5c51f124SMoriah Waterland 	int backoff;
3016*5c51f124SMoriah Waterland 	long seed;
3017*5c51f124SMoriah Waterland 
3018*5c51f124SMoriah Waterland 	if (!initted) {
3019*5c51f124SMoriah Waterland 		/* seed the rng */
3020*5c51f124SMoriah Waterland 		(void) _get_random_info(&seed, sizeof (seed));
3021*5c51f124SMoriah Waterland 		srand48(seed);
3022*5c51f124SMoriah Waterland 		initted = B_TRUE;
3023*5c51f124SMoriah Waterland 	}
3024*5c51f124SMoriah Waterland 
3025*5c51f124SMoriah Waterland 	backoff = drand48() * (double)cur_backoff;
3026*5c51f124SMoriah Waterland 	(void) sleep(backoff);
3027*5c51f124SMoriah Waterland 	if (cur_backoff < MAX_BACKOFF) {
3028*5c51f124SMoriah Waterland 		/*
3029*5c51f124SMoriah Waterland 		 * increase maximum time we might wait
3030*5c51f124SMoriah Waterland 		 * next time so as to fall off over
3031*5c51f124SMoriah Waterland 		 * time.
3032*5c51f124SMoriah Waterland 		 */
3033*5c51f124SMoriah Waterland 		cur_backoff *= BACKOFF_FACTOR;
3034*5c51f124SMoriah Waterland 	}
3035*5c51f124SMoriah Waterland }
3036*5c51f124SMoriah Waterland 
3037*5c51f124SMoriah Waterland /*
3038*5c51f124SMoriah Waterland  * Name:		reset_backoff
3039*5c51f124SMoriah Waterland  * Description:	notifies the backoff service that whatever was
3040*5c51f124SMoriah Waterland  *		being backoff succeeded.
3041*5c51f124SMoriah Waterland  * Scope:	public
3042*5c51f124SMoriah Waterland  * Arguments:	none
3043*5c51f124SMoriah Waterland  * Returns:	none
3044*5c51f124SMoriah Waterland  */
3045*5c51f124SMoriah Waterland void
3046*5c51f124SMoriah Waterland reset_backoff()
3047*5c51f124SMoriah Waterland {
3048*5c51f124SMoriah Waterland 	cur_backoff = MIN_BACKOFF;
3049*5c51f124SMoriah Waterland }
3050*5c51f124SMoriah Waterland 
3051*5c51f124SMoriah Waterland /*
3052*5c51f124SMoriah Waterland  * Name:	_get_random_info
3053*5c51f124SMoriah Waterland  * Description:	generate an amount of random bits.  Currently
3054*5c51f124SMoriah Waterland  *		only a small amount (a long long) can be
3055*5c51f124SMoriah Waterland  *		generated at one time.
3056*5c51f124SMoriah Waterland  * Scope:	private
3057*5c51f124SMoriah Waterland  * Arguments:	buf	- [RO, *RW] (char *)
3058*5c51f124SMoriah Waterland  *			  Buffer to copy bits into
3059*5c51f124SMoriah Waterland  *		size	- amount to copy
3060*5c51f124SMoriah Waterland  * Returns:	B_TRUE on success, B_FALSE otherwise.  The buffer is filled
3061*5c51f124SMoriah Waterland  *		with the amount of bytes of random data specified.
3062*5c51f124SMoriah Waterland  */
3063*5c51f124SMoriah Waterland static boolean_t
3064*5c51f124SMoriah Waterland _get_random_info(void *buf, int size)
3065*5c51f124SMoriah Waterland {
3066*5c51f124SMoriah Waterland 	struct timeval tv;
3067*5c51f124SMoriah Waterland 	typedef struct {
3068*5c51f124SMoriah Waterland 		long low_time;
3069*5c51f124SMoriah Waterland 		long hostid;
3070*5c51f124SMoriah Waterland 	} randomness;
3071*5c51f124SMoriah Waterland 	randomness r;
3072*5c51f124SMoriah Waterland 
3073*5c51f124SMoriah Waterland 	/* if the RANDOM file exists, use it */
3074*5c51f124SMoriah Waterland 	if (access(RANDOM, R_OK) == 0) {
3075*5c51f124SMoriah Waterland 		if ((RAND_load_file(RANDOM, 1024 * 1024)) > 0) {
3076*5c51f124SMoriah Waterland 			if (RAND_bytes((uchar_t *)buf, size) == 1) {
3077*5c51f124SMoriah Waterland 				/* success */
3078*5c51f124SMoriah Waterland 				return (B_TRUE);
3079*5c51f124SMoriah Waterland 			}
3080*5c51f124SMoriah Waterland 		}
3081*5c51f124SMoriah Waterland 	}
3082*5c51f124SMoriah Waterland 
3083*5c51f124SMoriah Waterland 	/* couldn't use RANDOM file, so fallback to time of day and hostid */
3084*5c51f124SMoriah Waterland 	(void) gettimeofday(&tv, (struct timezone *)0);
3085*5c51f124SMoriah Waterland 
3086*5c51f124SMoriah Waterland 	/* Wouldn't it be nice if we could hash these */
3087*5c51f124SMoriah Waterland 	r.low_time = tv.tv_usec;
3088*5c51f124SMoriah Waterland 	r.hostid = gethostid();
3089*5c51f124SMoriah Waterland 
3090*5c51f124SMoriah Waterland 	if (sizeof (r) < size) {
3091*5c51f124SMoriah Waterland 		/*
3092*5c51f124SMoriah Waterland 		 * Can't copy correctly
3093*5c51f124SMoriah Waterland 		 */
3094*5c51f124SMoriah Waterland 		return (B_FALSE);
3095*5c51f124SMoriah Waterland 	}
3096*5c51f124SMoriah Waterland 	(void) memcpy(buf, &r, size);
3097*5c51f124SMoriah Waterland 	return (B_TRUE);
3098*5c51f124SMoriah Waterland }
3099*5c51f124SMoriah Waterland 
3100*5c51f124SMoriah Waterland /*
3101*5c51f124SMoriah Waterland  * Name:		pkg_passphrase_cb
3102*5c51f124SMoriah Waterland  * Description:	Default callback that applications can use when
3103*5c51f124SMoriah Waterland  *		a passphrase is needed.  This routine collects
3104*5c51f124SMoriah Waterland  *		a passphrase from the user using the given
3105*5c51f124SMoriah Waterland  *		passphrase retrieval method set with
3106*5c51f124SMoriah Waterland  *		set_passphrase_passarg().  If the method
3107*5c51f124SMoriah Waterland  *		indicates an interactive prompt, then the
3108*5c51f124SMoriah Waterland  *		prompt set with set_passphrase_prompt()
3109*5c51f124SMoriah Waterland  *		is displayed.
3110*5c51f124SMoriah Waterland  *
3111*5c51f124SMoriah Waterland  * Arguments:	buf	- Buffer to copy passphrase into
3112*5c51f124SMoriah Waterland  *		size	- Max amount to copy to buf
3113*5c51f124SMoriah Waterland  *		rw	- Whether this passphrase is needed
3114*5c51f124SMoriah Waterland  *			to read something off disk, or
3115*5c51f124SMoriah Waterland  *			write something to disk.  Applications
3116*5c51f124SMoriah Waterland  *			typically want to ask twice when getting
3117*5c51f124SMoriah Waterland  *			a passphrase for writing something.
3118*5c51f124SMoriah Waterland  *		data	- application-specific data.  In this
3119*5c51f124SMoriah Waterland  *			callback, data is a pointer to
3120*5c51f124SMoriah Waterland  *			a keystore_passphrase_data structure.
3121*5c51f124SMoriah Waterland  *
3122*5c51f124SMoriah Waterland  * Returns:	Length of passphrase collected, or -1 on error.
3123*5c51f124SMoriah Waterland  *		Errors recorded in 'err' object in the *data.
3124*5c51f124SMoriah Waterland  */
3125*5c51f124SMoriah Waterland int
3126*5c51f124SMoriah Waterland pkg_passphrase_cb(char *buf, int size, int rw, void *data)
3127*5c51f124SMoriah Waterland {
3128*5c51f124SMoriah Waterland 	BIO		*pwdbio = NULL;
3129*5c51f124SMoriah Waterland 	char		passphrase_copy[MAX_PHRASELEN + 1];
3130*5c51f124SMoriah Waterland 	PKG_ERR		*err;
3131*5c51f124SMoriah Waterland 	int		passlen;
3132*5c51f124SMoriah Waterland 	char		*ws;
3133*5c51f124SMoriah Waterland 	char		prompt_copy[MAX_VERIFY_MSGLEN];
3134*5c51f124SMoriah Waterland 	char		*passphrase;
3135*5c51f124SMoriah Waterland 	char		*arg;
3136*5c51f124SMoriah Waterland 
3137*5c51f124SMoriah Waterland 	err = ((keystore_passphrase_data *)data)->err;
3138*5c51f124SMoriah Waterland 
3139*5c51f124SMoriah Waterland 	if (passarg == NULL) {
3140*5c51f124SMoriah Waterland 		arg = "console";
3141*5c51f124SMoriah Waterland 	} else {
3142*5c51f124SMoriah Waterland 		arg = passarg;
3143*5c51f124SMoriah Waterland 	}
3144*5c51f124SMoriah Waterland 
3145*5c51f124SMoriah Waterland 	/* default method of collecting password is by prompting */
3146*5c51f124SMoriah Waterland 	if (ci_streq(arg, "console")) {
3147*5c51f124SMoriah Waterland 		if ((passphrase = getpassphrase(prompt)) == NULL) {
3148*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_BADPASS,
3149*5c51f124SMoriah Waterland 			    gettext(MSG_NOPASS), arg);
3150*5c51f124SMoriah Waterland 			return (-1);
3151*5c51f124SMoriah Waterland 		}
3152*5c51f124SMoriah Waterland 
3153*5c51f124SMoriah Waterland 		if (rw) {
3154*5c51f124SMoriah Waterland 			/*
3155*5c51f124SMoriah Waterland 			 * if the password is being supplied for
3156*5c51f124SMoriah Waterland 			 * writing something to disk, verify it first
3157*5c51f124SMoriah Waterland 			 */
3158*5c51f124SMoriah Waterland 
3159*5c51f124SMoriah Waterland 			/* make a copy (getpassphrase overwrites) */
3160*5c51f124SMoriah Waterland 			strlcpy(passphrase_copy, passphrase,
3161*5c51f124SMoriah Waterland 			    MAX_PHRASELEN + 1);
3162*5c51f124SMoriah Waterland 
3163*5c51f124SMoriah Waterland 			if (((passlen = snprintf(prompt_copy,
3164*5c51f124SMoriah Waterland 					MAX_VERIFY_MSGLEN, "%s: %s",
3165*5c51f124SMoriah Waterland 					gettext(MSG_PASSWD_AGAIN),
3166*5c51f124SMoriah Waterland 					prompt)) < 0) ||
3167*5c51f124SMoriah Waterland 			    (passlen >= (MAX_PHRASELEN + 1))) {
3168*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_BADPASS,
3169*5c51f124SMoriah Waterland 				    gettext(MSG_NOPASS), arg);
3170*5c51f124SMoriah Waterland 				return (-1);
3171*5c51f124SMoriah Waterland 			}
3172*5c51f124SMoriah Waterland 
3173*5c51f124SMoriah Waterland 			if ((passphrase =
3174*5c51f124SMoriah Waterland 			    getpassphrase(prompt_copy)) == NULL) {
3175*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_BADPASS,
3176*5c51f124SMoriah Waterland 				    gettext(MSG_NOPASS), arg);
3177*5c51f124SMoriah Waterland 				return (-1);
3178*5c51f124SMoriah Waterland 			}
3179*5c51f124SMoriah Waterland 
3180*5c51f124SMoriah Waterland 			if (!streq(passphrase_copy, passphrase)) {
3181*5c51f124SMoriah Waterland 				pkgerr_add(err, PKGERR_READ,
3182*5c51f124SMoriah Waterland 				    gettext(MSG_PASSWD_NOMATCH));
3183*5c51f124SMoriah Waterland 				return (-1);
3184*5c51f124SMoriah Waterland 			}
3185*5c51f124SMoriah Waterland 		}
3186*5c51f124SMoriah Waterland 	} else if (ci_strneq(arg, "pass:", 5)) {
3187*5c51f124SMoriah Waterland 		passphrase = arg + 5;
3188*5c51f124SMoriah Waterland 	} else if (ci_strneq(arg, "env:", 4)) {
3189*5c51f124SMoriah Waterland 		passphrase = getenv(arg + 4);
3190*5c51f124SMoriah Waterland 	} else if (ci_strneq(arg, "file:", 5)) {
3191*5c51f124SMoriah Waterland 
3192*5c51f124SMoriah Waterland 		/* open file for reading */
3193*5c51f124SMoriah Waterland 		if ((pwdbio = BIO_new_file(arg + 5, "r")) == NULL) {
3194*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_EXIST,
3195*5c51f124SMoriah Waterland 			    gettext(MSG_PASSWD_FILE), arg + 5);
3196*5c51f124SMoriah Waterland 			return (-1);
3197*5c51f124SMoriah Waterland 		}
3198*5c51f124SMoriah Waterland 
3199*5c51f124SMoriah Waterland 		/* read first line */
3200*5c51f124SMoriah Waterland 		if (((passlen = BIO_gets(pwdbio, buf, size)) < 1) ||
3201*5c51f124SMoriah Waterland 		    (passlen > size)) {
3202*5c51f124SMoriah Waterland 			pkgerr_add(err, PKGERR_READ, gettext(MSG_PASSWD_FILE),
3203*5c51f124SMoriah Waterland 			    arg + 5);
3204*5c51f124SMoriah Waterland 			return (-1);
3205*5c51f124SMoriah Waterland 		}
3206*5c51f124SMoriah Waterland 		BIO_free_all(pwdbio);
3207*5c51f124SMoriah Waterland 		pwdbio = NULL;
3208*5c51f124SMoriah Waterland 
3209*5c51f124SMoriah Waterland 		if (passlen == size) {
3210*5c51f124SMoriah Waterland 			/*
3211*5c51f124SMoriah Waterland 			 * password was maximum length, so there is
3212*5c51f124SMoriah Waterland 			 * no null terminator. null-terminate it
3213*5c51f124SMoriah Waterland 			 */
3214*5c51f124SMoriah Waterland 			buf[size - 1] = '\0';
3215*5c51f124SMoriah Waterland 		}
3216*5c51f124SMoriah Waterland 
3217*5c51f124SMoriah Waterland 		/* first newline found is end of passwd, so nuke it */
3218*5c51f124SMoriah Waterland 		if ((ws = strchr(buf, '\n')) != NULL) {
3219*5c51f124SMoriah Waterland 			*ws = '\0';
3220*5c51f124SMoriah Waterland 		}
3221*5c51f124SMoriah Waterland 		return (strlen(buf));
3222*5c51f124SMoriah Waterland 	} else {
3223*5c51f124SMoriah Waterland 		/* unrecognized passphrase */
3224*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_BADPASS,
3225*5c51f124SMoriah Waterland 		    gettext(MSG_BADPASSARG), arg);
3226*5c51f124SMoriah Waterland 		return (-1);
3227*5c51f124SMoriah Waterland 	}
3228*5c51f124SMoriah Waterland 
3229*5c51f124SMoriah Waterland 	if (passphrase == NULL) {
3230*5c51f124SMoriah Waterland 		/* unable to collect passwd from given source */
3231*5c51f124SMoriah Waterland 		pkgerr_add(err, PKGERR_BADPASS,
3232*5c51f124SMoriah Waterland 		    gettext(MSG_NOPASS), arg);
3233*5c51f124SMoriah Waterland 		return (-1);
3234*5c51f124SMoriah Waterland 	}
3235*5c51f124SMoriah Waterland 
3236*5c51f124SMoriah Waterland 	strlcpy(buf, passphrase, size);
3237*5c51f124SMoriah Waterland 	return (strlen(buf));
3238*5c51f124SMoriah Waterland }
3239