1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * File: CLIENT.C
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <poll.h>
35 #include <sys/errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <libgen.h>
45 #include <kmfapi.h>
46 #include <kmfapiP.h>
47 #include <libxml2/libxml/uri.h>
48 
49 extern int errno;
50 
51 #define	OCSP_BUFSIZE 1024
52 
53 typedef enum {
54 	KMF_RESPONSE_OCSP = 1,
55 	KMF_RESPONSE_FILE = 2
56 } KMF_RESPONSE_TYPE;
57 
58 #define	TEMP_TEMPLATE	"temp.XXXXXX"
59 
60 /*
61  * This function will establish a socket to the host on the specified port.
62  * If succeed, it return a socket descriptor; otherwise, return -1.
63  */
64 static int init_socket(char *host, short port)
65 {
66 	struct sockaddr_in sin;
67 	struct hostent *hp, hrec;
68 	int sockfd, opt, herrno;
69 	char hostbuf[BUFSIZ];
70 
71 	sin.sin_family = PF_INET;
72 	sin.sin_port = htons(port);
73 	if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
74 		if ((hp = gethostbyname_r(host, &hrec, hostbuf,
75 		    sizeof (hostbuf), &herrno)) == NULL) {
76 			return (-1);
77 		}
78 		(void) memcpy((char *)&sin.sin_addr, hp->h_addr,
79 		    hp->h_length);
80 	}
81 
82 	if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
83 		return (-1);
84 	}
85 
86 	opt = 1;
87 	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
88 	    sizeof (opt)) < 0) {
89 		(void) close(sockfd);
90 		return (-1);
91 	}
92 
93 	if (connect(sockfd, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
94 		(void) close(sockfd);
95 		return (-1);
96 	}
97 
98 	return (sockfd);
99 }
100 
101 /*
102  * This function will connect to host on the port.
103  * If succeed, return a socket descriptor; otherwise, return 0.
104  */
105 static int
106 connect_to_server(char *host, short port)
107 {
108 	int retry = 1;
109 	int sd = 0;
110 
111 	while (retry) {
112 		if ((sd = init_socket(host, port)) == -1) {
113 			if (errno == ECONNREFUSED) {
114 				retry = 1;
115 				(void) sleep(1);
116 			} else {
117 				retry = 0;
118 			}
119 		} else	{
120 			retry = 0;
121 		}
122 	}
123 	return (sd);
124 }
125 
126 static KMF_RETURN
127 send_ocsp_request(int sock, char *reqfile, char *hostname)
128 {
129 	KMF_RETURN ret = KMF_OK;
130 	int filefd, bytes, n, total = 0;
131 	char buf[OCSP_BUFSIZE];
132 	struct stat s;
133 	char req_header[256];
134 	static char req_format[] =
135 "POST %s HTTP/1.0\r\n\
136 Content-Type: application/ocsp-request\r\n\
137 Content-Length: %d\r\n\r\n";
138 
139 	if ((filefd = open(reqfile, O_RDONLY)) == -1) {
140 		ret = KMF_ERR_OPEN_FILE;
141 		return (ret);
142 	}
143 
144 	/* open the request file */
145 	if (fstat(filefd, &s) < 0) {
146 		ret = KMF_ERR_OPEN_FILE;
147 		return (ret);
148 	}
149 
150 
151 	/* Send http header */
152 	if (hostname != NULL) {
153 		(void) snprintf(req_header, 256, req_format, hostname,
154 		    s.st_size);
155 	} else {
156 		(void) snprintf(req_header, 256, req_format, "/", s.st_size);
157 	}
158 	bytes = strlen(req_header);
159 
160 	if ((n = write(sock, req_header, bytes)) < 0) {
161 		ret = KMF_ERR_SEND_REQUEST;
162 		goto exit;
163 	}
164 
165 	/* Send the request content */
166 	while ((bytes = read(filefd, buf, OCSP_BUFSIZE)) > 0) {
167 		if ((n = write(sock, buf, bytes)) < 0) {
168 			ret = KMF_ERR_SEND_REQUEST;
169 			goto exit;
170 		}
171 		total += n;
172 		(void) memset(buf, 0, sizeof (buf));
173 	}
174 
175 exit:
176 	(void) close(filefd);
177 	return (ret);
178 }
179 
180 
181 /*
182  * Perform a write that can handle EINTR.
183  */
184 static int
185 looping_write(int fd, void *buf, int len)
186 {
187 	char *p = buf;
188 	int cc, len2 = 0;
189 
190 	if (len == 0)
191 		return (0);
192 	do {
193 		cc = write(fd, p, len);
194 		if (cc < 0) {
195 			if (errno == EINTR)
196 				continue;
197 			return (cc);
198 		} else if (cc == 0) {
199 			return (len2);
200 		} else {
201 			p += cc;
202 			len2 += cc;
203 			len -= cc;
204 		}
205 	} while (len > 0);
206 
207 	return (len2);
208 }
209 
210 /*
211  * This function will get the response from the server, check the http status
212  * line, and write the response content to a file.  If this is a OCSP response,
213  * it will check the content type also.
214  */
215 static KMF_RETURN
216 get_encoded_response(int sock, KMF_RESPONSE_TYPE resptype, int filefd,
217     unsigned int maxsecs)
218 {
219 	int ret = KMF_OK;
220 	char *buf = NULL;
221 	int buflen = 0;
222 	int offset = 0;
223 	int search_offset;
224 	const int buf_incre = OCSP_BUFSIZE; /* 1 KB at a time */
225 	const int maxBufSize = 8 * buf_incre; /* 8 KB max */
226 	const char *CRLF = "\r\n";
227 	const char *headerEndMark = "\r\n\r\n";
228 	const char *httpprotocol = "HTTP/";
229 	const int CRLFlen = strlen(CRLF);
230 	const int marklen = strlen(headerEndMark);
231 	const int httplen = strlen(httpprotocol);
232 	char *headerEnd = NULL;
233 	boolean_t EOS = B_FALSE;
234 	const char *httpcode = NULL;
235 	const char *contenttype = NULL;
236 	int contentlength = 0;
237 	int bytes = 0;
238 	char *statusLineEnd = NULL;
239 	char *space = NULL;
240 	char *nextHeader = NULL;
241 	struct pollfd pfd;
242 	int sock_flag;
243 	int poll_ret;
244 	boolean_t timeout = B_FALSE;
245 
246 	/* set O_NONBLOCK flag on socket */
247 	if ((sock_flag = fcntl(sock, F_GETFL, 0)) == -1) {
248 		return (KMF_ERR_RECV_RESPONSE);
249 	}
250 	sock_flag |= O_NONBLOCK;
251 	if (fcntl(sock, F_SETFL, sock_flag) == -1) {
252 		return (KMF_ERR_RECV_RESPONSE);
253 	}
254 
255 	/* set up poll */
256 	pfd.fd = sock;
257 	pfd.events = POLLIN;
258 
259 	/*
260 	 * First read HTTP status line and headers.  We will read up to at
261 	 * least the end of the HTTP headers
262 	 */
263 	do {
264 		if ((buflen - offset) < buf_incre) {
265 			buflen += buf_incre;
266 			buf = realloc(buf, buflen + 1);
267 			if (buf == NULL) {
268 				ret = KMF_ERR_MEMORY;
269 				goto out;
270 			}
271 		}
272 
273 		pfd.revents = 0;
274 		poll_ret = poll(&pfd, 1, maxsecs * MILLISEC);
275 		if (poll_ret == 0) {
276 			timeout = B_TRUE;
277 			break;
278 		} else if (poll_ret < 0) {
279 			ret = KMF_ERR_RECV_RESPONSE;
280 			goto out;
281 		} else {
282 			if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
283 				ret = KMF_ERR_RECV_RESPONSE;
284 				goto out;
285 			}
286 		}
287 
288 		bytes = read(sock, buf + offset,  buf_incre);
289 		if (bytes < 0) {
290 			if (errno == EWOULDBLOCK) { /* no data this time */
291 				continue;
292 			} else {
293 				ret = KMF_ERR_RECV_RESPONSE;
294 				goto out;
295 			}
296 		} else if (bytes == 0) { /* no more data */
297 			EOS = B_TRUE;
298 		} else { /* bytes > 0 */
299 			search_offset = (offset - marklen) > 0 ?
300 			    offset - marklen : 0;
301 			offset += bytes;
302 			*(buf + offset) = '\0'; /* NULL termination */
303 
304 			headerEnd = strstr((const char *)buf + search_offset,
305 			    headerEndMark);
306 		}
307 
308 	} while ((!headerEnd) && (EOS == B_FALSE) && (buflen < maxBufSize));
309 
310 	if (timeout == B_TRUE) {
311 		ret = KMF_ERR_RECV_TIMEOUT;
312 		goto out;
313 	} else if (headerEnd == NULL) {
314 		/* could not find the end of headers */
315 		ret = KMF_ERR_BAD_HTTP_RESPONSE;
316 		goto out;
317 	}
318 
319 	/*
320 	 * Parse the HTTP status line, which will look like this:
321 	 * "HTTP/1.1 200 OK".
322 	 */
323 	statusLineEnd = strstr((const char *)buf, CRLF);
324 	if (statusLineEnd == NULL) {
325 		ret = KMF_ERR_BAD_HTTP_RESPONSE;
326 		goto out;
327 	}
328 	*statusLineEnd = '\0';
329 
330 	space = strchr((const char *)buf, ' ');
331 	if (space == NULL ||
332 	    (strncasecmp((const char *)buf, httpprotocol, httplen) != 0)) {
333 		ret = KMF_ERR_BAD_HTTP_RESPONSE;
334 		goto out;
335 	}
336 
337 	/*
338 	 * Check the HTTP status code. If it is not 200, the HTTP response
339 	 * is not good.
340 	 */
341 	httpcode = space + 1;
342 	space = strchr(httpcode, ' ');
343 	if (space == NULL) {
344 		ret = KMF_ERR_BAD_HTTP_RESPONSE;
345 		goto out;
346 	}
347 
348 	*space = 0;
349 	if (strcmp(httpcode, "200") != 0) {
350 		ret = KMF_ERR_BAD_HTTP_RESPONSE;
351 		goto out;
352 	}
353 
354 	/*
355 	 * Parse the HTTP headers in the buffer.  Save content-type and
356 	 * content-length only.
357 	 */
358 	nextHeader = statusLineEnd + CRLFlen;
359 	*headerEnd = '\0'; /* terminate */
360 	do {
361 		char *thisHeaderEnd = NULL;
362 		char *value = NULL;
363 		char *colon = strchr(nextHeader, ':');
364 
365 		if (colon == NULL) {
366 			ret = KMF_ERR_BAD_HTTP_RESPONSE;
367 			goto out;
368 		}
369 		*colon = '\0';
370 
371 		value = colon + 1;
372 		if (*value != ' ') {
373 			ret = KMF_ERR_BAD_HTTP_RESPONSE;
374 			goto out;
375 		}
376 		value++;
377 
378 		thisHeaderEnd  = strstr(value, CRLF);
379 		if (thisHeaderEnd != NULL)
380 			*thisHeaderEnd  = '\0';
381 
382 		if (strcasecmp(nextHeader, "content-type") == 0) {
383 			contenttype = value;
384 		} else if (strcasecmp(nextHeader, "content-length") == 0) {
385 			contentlength = atoi(value);
386 		}
387 
388 		if (thisHeaderEnd != NULL) {
389 			nextHeader = thisHeaderEnd + CRLFlen;
390 		} else {
391 			nextHeader = NULL;
392 		}
393 
394 	} while (nextHeader && (nextHeader < (headerEnd + CRLFlen)));
395 
396 	/* Check the contenttype if this is an OCSP response */
397 	if (resptype == KMF_RESPONSE_OCSP) {
398 		if (contenttype == NULL) {
399 			ret = KMF_ERR_BAD_HTTP_RESPONSE;
400 			goto out;
401 		} else if (strcasecmp(contenttype,
402 		    "application/ocsp-response") != 0) {
403 			ret = KMF_ERR_BAD_HTTP_RESPONSE;
404 			goto out;
405 		}
406 	}
407 
408 	/* Now we are ready to read the body of the response */
409 	offset = offset - (int)(headerEnd - (const char *)buf) - marklen;
410 	if (offset) {
411 		/* move all data to the beginning of the buffer */
412 		(void) memmove(buf, headerEnd + marklen, offset);
413 	}
414 
415 	/* resize buffer to only what's needed to hold the current response */
416 	buflen = (1 + (offset-1) / buf_incre) * buf_incre;
417 
418 	while ((EOS == B_FALSE) &&
419 	    ((contentlength == 0) || (offset < contentlength)) &&
420 	    (buflen < maxBufSize)) {
421 		/* we still need to receive more content data */
422 		if ((buflen - offset) < buf_incre) {
423 			buflen += buf_incre;
424 			buf = realloc(buf, buflen + 1);
425 			if (buf == NULL) {
426 				ret = KMF_ERR_MEMORY;
427 				goto out;
428 			}
429 		}
430 
431 		pfd.revents = 0;
432 		poll_ret = poll(&pfd, 1, maxsecs * MILLISEC);
433 		if (poll_ret == 0) {
434 			timeout = B_TRUE;
435 			break;
436 		} else if (poll_ret < 0) {
437 			ret = KMF_ERR_RECV_RESPONSE;
438 			goto out;
439 		} else {
440 			if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
441 				ret = KMF_ERR_RECV_RESPONSE;
442 				goto out;
443 			}
444 		}
445 
446 		bytes = read(sock, buf + offset,  buf_incre);
447 		if (bytes < 0) {
448 			if (errno == EWOULDBLOCK) {
449 				continue;
450 			} else {
451 				ret = KMF_ERR_RECV_RESPONSE;
452 				goto out;
453 			}
454 		} else if (bytes == 0) { /* no more data */
455 			EOS = B_TRUE;
456 		} else {
457 			offset += bytes;
458 		}
459 	}
460 
461 	if (timeout == B_TRUE) {
462 		ret = KMF_ERR_RECV_TIMEOUT;
463 		goto out;
464 	} else if (((contentlength != 0) && (offset < contentlength)) ||
465 	    offset == 0) {
466 		ret = KMF_ERR_BAD_HTTP_RESPONSE;
467 		goto out;
468 	}
469 
470 	/* write to the file */
471 	if (looping_write(filefd, buf, offset) != offset) {
472 		ret = KMF_ERR_WRITE_FILE;
473 	}
474 
475 out:
476 	free(buf);
477 	return (ret);
478 }
479 
480 KMF_RETURN
481 kmf_get_encoded_ocsp_response(KMF_HANDLE_T handle,
482     char *reqfile, char *hostname,
483     int port, char *proxy, int proxy_port, char *respfile,
484     unsigned int maxsecs)
485 {
486 	KMF_RETURN ret = KMF_OK;
487 	int sock, respfd;
488 	char http_hostname[256];
489 	int final_proxy_port, final_port;
490 
491 	CLEAR_ERROR(handle, ret);
492 	if (ret != KMF_OK)
493 		return (ret);
494 
495 	if (hostname == NULL || reqfile == NULL || respfile == NULL) {
496 		return (KMF_ERR_BAD_PARAMETER);
497 	}
498 
499 	final_proxy_port = (proxy_port == 0 || proxy_port == -1) ?
500 	    80 : proxy_port;
501 	final_port = (port == 0 || port == -1) ? 80 : port;
502 
503 	/* Connect to server */
504 	if (proxy != NULL) {
505 		sock = connect_to_server(proxy, final_proxy_port);
506 	} else {
507 		sock = connect_to_server(hostname, final_port);
508 	}
509 
510 	if (sock == -1) {
511 		return (KMF_ERR_CONNECT_SERVER);
512 	}
513 
514 	/* Send the OCSP request */
515 	if (proxy != NULL) {
516 		(void) snprintf(http_hostname, sizeof (http_hostname),
517 		    "http://%s:%d", hostname, final_port);
518 		ret = send_ocsp_request(sock, reqfile, http_hostname);
519 	} else {
520 		ret = send_ocsp_request(sock, reqfile, NULL);
521 	}
522 
523 	if (ret != KMF_OK) {
524 		goto out;
525 	}
526 
527 	/* Retrieve the OCSP response */
528 	if (maxsecs == 0) {
529 		maxsecs = 30; /* default poll time limit is 30 seconds */
530 	}
531 
532 	if ((respfd = open(respfile, O_CREAT |O_RDWR | O_EXCL, 0600)) == -1) {
533 		ret = KMF_ERR_OPEN_FILE;
534 	} else {
535 		ret = get_encoded_response(sock, KMF_RESPONSE_OCSP,
536 		    respfd, maxsecs);
537 		(void) close(respfd);
538 	}
539 
540 out:
541 	(void) close(sock);
542 	return (ret);
543 }
544 
545 static KMF_RETURN
546 send_download_request(int sock, char *hostname, int port, boolean_t is_proxy,
547     char *loc)
548 {
549 	KMF_RETURN ret = KMF_OK;
550 	char url[256];
551 	char req_header[1024];
552 	static char req_format[] =
553 "GET %s HTTP/1.0\r\n\
554 Host: %s:%d\r\n\
555 Accept: */*\r\n\r\n";
556 
557 	if (is_proxy) {
558 		(void) snprintf(url, sizeof (url), "http://%s:%d/%s",
559 		    hostname, port, loc);
560 	} else {
561 		(void) snprintf(url, sizeof (url), "/%s", loc);
562 	}
563 
564 	(void) snprintf(req_header, sizeof (req_header), req_format, url,
565 	    hostname, port);
566 
567 	if (write(sock, req_header, strlen(req_header)) < 0) {
568 		ret = KMF_ERR_SEND_REQUEST;
569 	}
570 
571 	return (ret);
572 }
573 
574 static KMF_RETURN
575 download_file(char *uri, char *proxy, int proxy_port,
576     unsigned int maxsecs, int filefd)
577 {
578 	KMF_RETURN ret = KMF_OK;
579 	xmlURIPtr   uriptr;
580 	int sock;
581 	boolean_t is_proxy;
582 	int final_proxy_port;
583 	char *hostname = NULL;
584 	char *path = NULL;
585 	int port;
586 
587 	if (uri == NULL || filefd == -1)
588 		return (KMF_ERR_BAD_PARAMETER);
589 
590 	/* Parse URI */
591 	uriptr = xmlParseURI(uri);
592 	if (uriptr == NULL) {
593 		ret = KMF_ERR_BAD_URI;
594 		goto out;
595 	}
596 
597 	if (uriptr->scheme == NULL ||
598 	    strncasecmp(uriptr->scheme, "http", 4) != 0) {
599 		ret = KMF_ERR_BAD_URI;  /* we support http only */
600 		goto out;
601 	}
602 
603 	/* get the host name */
604 	hostname = uriptr->server;
605 	if (hostname == NULL) {
606 		ret = KMF_ERR_BAD_URI;
607 		goto out;
608 	}
609 
610 	/* get the port number */
611 	port = uriptr->port;
612 	if (port == 0) {
613 		port = 80;
614 	}
615 
616 	/* Get the path */
617 	path = uriptr->path;
618 	if (path == NULL) {
619 		ret = KMF_ERR_BAD_URI;
620 		goto out;
621 	}
622 
623 	/* Connect to server */
624 	if (proxy != NULL) {
625 		final_proxy_port = (proxy_port == 0 || proxy_port == -1) ?
626 		    80 : proxy_port;
627 		is_proxy = B_TRUE;
628 		sock = connect_to_server(proxy, final_proxy_port);
629 	} else {
630 		is_proxy = B_FALSE;
631 		sock = connect_to_server(hostname, port);
632 	}
633 	if (sock == -1) {
634 		ret = KMF_ERR_CONNECT_SERVER;
635 		goto out;
636 	}
637 
638 	/* Send the request */
639 	ret = send_download_request(sock, hostname, port, is_proxy, path);
640 	if (ret != KMF_OK) {
641 		goto out;
642 	}
643 
644 	/* Retrieve the response */
645 	ret = get_encoded_response(sock, KMF_RESPONSE_FILE, filefd,
646 	    maxsecs == 0 ? 30 : maxsecs);
647 	if (ret != KMF_OK) {
648 		goto out;
649 	}
650 
651 out:
652 	if (uriptr != NULL)
653 		xmlFreeURI(uriptr);
654 
655 	if (sock != -1)
656 		(void) close(sock);
657 
658 	return (ret);
659 }
660 
661 
662 KMF_RETURN
663 kmf_download_crl(KMF_HANDLE_T handle, char *uri, char *proxy, int proxy_port,
664     unsigned int maxsecs, char *crlfile, KMF_ENCODE_FORMAT *pformat)
665 {
666 	KMF_RETURN ret = KMF_OK;
667 	char *filename = NULL;
668 	char tempfn[MAXPATHLEN];
669 	boolean_t temp_created = B_FALSE;
670 	mode_t old_mode;
671 	int fd = -1, tmpfd = -1;
672 
673 	CLEAR_ERROR(handle, ret);
674 	if (ret != KMF_OK)
675 		return (ret);
676 
677 	if (uri == NULL || crlfile == NULL || pformat == NULL)
678 		return (KMF_ERR_BAD_PARAMETER);
679 
680 	if ((fd = open(crlfile, O_CREAT |O_RDWR | O_EXCL, 0644)) == -1)
681 		return (KMF_ERR_OPEN_FILE);
682 
683 	/*
684 	 * Download the file and save it to a temp file. To make rename()
685 	 * happy, the temp file needs to be created in the same directory as
686 	 * the target file.
687 	 */
688 	if ((filename = strdup(crlfile)) == NULL) {
689 		ret = KMF_ERR_MEMORY;
690 		goto out;
691 	}
692 	(void) snprintf(tempfn, MAXPATHLEN, "%s/%s", dirname(filename),
693 	    TEMP_TEMPLATE);
694 	old_mode = umask(077);
695 	tmpfd = mkstemp(tempfn);
696 	(void) umask(old_mode);
697 	if (tmpfd == -1) {
698 		ret = KMF_ERR_INTERNAL;
699 		goto out;
700 	} else {
701 		temp_created = B_TRUE;
702 	}
703 
704 	ret = download_file(uri, proxy, proxy_port, maxsecs, tmpfd);
705 	(void) close(tmpfd);
706 	if (ret != KMF_OK) {
707 		goto out;
708 	}
709 
710 	/* Check if it is a CRL file and get its format */
711 	if (kmf_is_crl_file(handle, tempfn, pformat) != KMF_OK) {
712 		ret = KMF_ERR_BAD_CRLFILE;
713 		goto out;
714 	}
715 
716 	/* Finally, change the temp filename to the target crlfile */
717 	if (rename(tempfn, crlfile) == -1) {
718 		ret = KMF_ERR_WRITE_FILE;
719 		goto out;
720 	}
721 
722 out:
723 	if (filename != NULL)
724 		free(filename);
725 
726 	if (ret != KMF_OK && temp_created == B_TRUE)
727 		(void) unlink(tempfn);
728 
729 	if (fd != -1)
730 		(void) close(fd);
731 
732 	return (ret);
733 }
734 
735 
736 KMF_RETURN
737 kmf_download_cert(KMF_HANDLE_T handle, char *uri, char *proxy, int proxy_port,
738     unsigned int maxsecs, char *certfile, KMF_ENCODE_FORMAT *pformat)
739 {
740 	KMF_RETURN ret = KMF_OK;
741 	char *filename = NULL;
742 	char tempfn[MAXPATHLEN];
743 	boolean_t temp_created = B_FALSE;
744 	mode_t old_mode;
745 	int fd = -1, tmpfd = -1;
746 
747 	CLEAR_ERROR(handle, ret);
748 	if (ret != KMF_OK)
749 		return (ret);
750 
751 	if (uri == NULL || certfile == NULL || pformat == NULL)
752 		return (KMF_ERR_BAD_PARAMETER);
753 
754 	if ((fd = open(certfile, O_CREAT |O_RDWR | O_EXCL, 0644)) == -1)
755 		return (KMF_ERR_OPEN_FILE);
756 
757 	/*
758 	 * Download the file and save it to a temp file. To make rename()
759 	 * happy, the temp file needs to be created in the same directory as
760 	 * the target file.
761 	 */
762 	if ((filename = strdup(certfile)) == NULL) {
763 		ret = KMF_ERR_MEMORY;
764 		goto out;
765 	}
766 	(void) snprintf(tempfn, MAXPATHLEN, "%s/%s", dirname(filename),
767 	    TEMP_TEMPLATE);
768 
769 	old_mode = umask(077);
770 	tmpfd = mkstemp(tempfn);
771 	(void) umask(old_mode);
772 	if (tmpfd == -1) {
773 		ret = KMF_ERR_INTERNAL;
774 		goto out;
775 	} else {
776 		temp_created = B_TRUE;
777 	}
778 
779 	ret = download_file(uri, proxy, proxy_port, maxsecs, tmpfd);
780 	(void) close(tmpfd);
781 	if (ret != KMF_OK) {
782 		goto out;
783 	}
784 
785 	/* Check if it is a Cert file and get its format */
786 	if (kmf_is_cert_file(handle, tempfn, pformat) != KMF_OK) {
787 		ret = KMF_ERR_BAD_CERTFILE;
788 		goto out;
789 	}
790 
791 	/* Finally, change the temp filename to the target filename */
792 	if (rename(tempfn, certfile) == -1) {
793 		ret = KMF_ERR_WRITE_FILE;
794 		goto out;
795 	}
796 
797 out:
798 	if (filename != NULL)
799 		free(filename);
800 
801 	if (ret != KMF_OK && temp_created == B_TRUE)
802 		(void) unlink(tempfn);
803 
804 	if (fd != -1)
805 		(void) close(fd);
806 
807 	return (ret);
808 }
809 
810 KMF_RETURN
811 kmf_get_ocsp_for_cert(KMF_HANDLE_T handle,
812 	KMF_DATA *user_cert,
813 	KMF_DATA *ta_cert,
814 	KMF_DATA *response)
815 {
816 	KMF_POLICY_RECORD *policy;
817 	KMF_RETURN ret = KMF_OK;
818 	char *hostname = NULL, *host_uri = NULL, *proxyname = NULL;
819 	char *proxy_port_s = NULL;
820 	int host_port = 0, proxy_port = 0;
821 	char ocsp_reqname[MAXPATHLEN];
822 	char ocsp_respname[MAXPATHLEN];
823 	KMF_X509EXT_AUTHINFOACCESS aia;
824 	int i;
825 	boolean_t found = B_FALSE;
826 	KMF_X509EXT_ACCESSDESC *access_info;
827 	xmlURIPtr   uriptr = NULL;
828 	KMF_ATTRIBUTE attrlist[10];
829 	int numattr = 0;
830 
831 	CLEAR_ERROR(handle, ret);
832 	if (ret != KMF_OK)
833 		return (ret);
834 
835 	if (user_cert == NULL || ta_cert == NULL || response == NULL)
836 		return (KMF_ERR_BAD_PARAMETER);
837 
838 	policy = handle->policy;
839 
840 	/* Create an OCSP request  */
841 	kmf_set_attr_at_index(attrlist, numattr,
842 	    KMF_ISSUER_CERT_DATA_ATTR, ta_cert,
843 	    sizeof (KMF_DATA));
844 	numattr++;
845 
846 	kmf_set_attr_at_index(attrlist, numattr,
847 	    KMF_USER_CERT_DATA_ATTR, user_cert,
848 	    sizeof (KMF_DATA));
849 	numattr++;
850 
851 	/*
852 	 * Create temporary files to hold the OCSP request & response data.
853 	 */
854 	(void) strlcpy(ocsp_reqname, OCSPREQ_TEMPNAME,
855 	    sizeof (ocsp_reqname));
856 	if (mkstemp(ocsp_reqname) == -1) {
857 		return (KMF_ERR_INTERNAL);
858 	}
859 
860 	(void) strlcpy(ocsp_respname, OCSPRESP_TEMPNAME,
861 	    sizeof (ocsp_respname));
862 	if (mkstemp(ocsp_respname) == -1) {
863 		return (KMF_ERR_INTERNAL);
864 	}
865 
866 	kmf_set_attr_at_index(attrlist, numattr,
867 	    KMF_OCSP_REQUEST_FILENAME_ATTR, ocsp_respname,
868 	    strlen(ocsp_respname));
869 	numattr++;
870 
871 	ret = kmf_create_ocsp_request(handle, numattr, attrlist);
872 	if (ret != KMF_OK) {
873 		goto out;
874 	}
875 
876 	if (policy->VAL_OCSP_BASIC.uri_from_cert == 0) {
877 		if (policy->VAL_OCSP_BASIC.responderURI == NULL) {
878 			ret = KMF_ERR_OCSP_POLICY;
879 			goto out;
880 		}
881 		host_uri = policy->VAL_OCSP_BASIC.responderURI;
882 
883 	} else {
884 		/*
885 		 * Get the responder URI from certificate
886 		 * Authority Information Access
887 		 * thru OID_PKIX_AD_OCSP
888 		 */
889 		ret = kmf_get_cert_auth_info_access(user_cert, &aia);
890 		if (ret != KMF_OK) {
891 			goto out;
892 		}
893 
894 		for (i = 0; i < aia.numberOfAccessDescription; i++) {
895 			access_info = &aia.AccessDesc[i];
896 			if (IsEqualOid(&access_info->AccessMethod,
897 			    (KMF_OID *)&KMFOID_PkixAdOcsp)) {
898 				host_uri =
899 				    (char *)access_info->AccessLocation.Data;
900 				found = B_TRUE;
901 				break;
902 			}
903 		}
904 
905 		if (!found) {
906 			ret = KMF_ERR_OCSP_POLICY;
907 			goto out;
908 		}
909 	}
910 
911 	/* Parse the URI string; get the hostname and port */
912 	uriptr = xmlParseURI(host_uri);
913 	if (uriptr == NULL) {
914 		ret = KMF_ERR_BAD_URI;
915 		goto out;
916 	}
917 
918 	if (strncasecmp(uriptr->scheme, "http", 4) != 0) {
919 		ret = KMF_ERR_BAD_URI;  /* we support http only */
920 		goto out;
921 	}
922 
923 	hostname = uriptr->server;
924 	if (hostname == NULL) {
925 		ret = KMF_ERR_BAD_URI;
926 		goto out;
927 	}
928 
929 	host_port = uriptr->port;
930 	if (host_port == 0)
931 		host_port = 80;
932 
933 	/* get the proxy info */
934 	if (policy->VAL_OCSP_BASIC.proxy != NULL) {
935 		char *last;
936 		proxyname =
937 		    strtok_r(policy->VAL_OCSP_BASIC.proxy, ":", &last);
938 		proxy_port_s = strtok_r(NULL, "\0", &last);
939 		if (proxy_port_s != NULL) {
940 			proxy_port = strtol(proxy_port_s, NULL, 0);
941 		} else {
942 			proxy_port = 8080; /* default */
943 		}
944 	}
945 
946 	/*
947 	 * Send the request to an OCSP responder and receive an
948 	 * OCSP response.
949 	 */
950 	ret = kmf_get_encoded_ocsp_response(handle, ocsp_reqname,
951 	    hostname, host_port,  proxyname, proxy_port,
952 	    ocsp_respname, 30);
953 	if (ret != KMF_OK) {
954 		goto out;
955 	}
956 
957 	ret = kmf_read_input_file(handle, ocsp_respname, response);
958 
959 out:
960 	(void) unlink(ocsp_reqname);
961 	(void) unlink(ocsp_respname);
962 
963 	if (uriptr != NULL)
964 		xmlFreeURI(uriptr);
965 
966 	return (ret);
967 }
968