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 2006 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_GetEncodedOCSPResponse(KMF_HANDLE_T handle, char *reqfile, char *hostname,
482     int port, char *proxy, int proxy_port, char *respfile,
483     unsigned int maxsecs)
484 {
485 	KMF_RETURN ret = KMF_OK;
486 	int sock, respfd;
487 	char http_hostname[256];
488 	int final_proxy_port, final_port;
489 
490 	CLEAR_ERROR(handle, ret);
491 	if (ret != KMF_OK)
492 		return (ret);
493 
494 	if (hostname == NULL || reqfile == NULL || respfile == NULL) {
495 		return (KMF_ERR_BAD_PARAMETER);
496 	}
497 
498 	final_proxy_port = (proxy_port == 0 || proxy_port == -1) ?
499 	    80 : proxy_port;
500 	final_port = (port == 0 || port == -1) ? 80 : port;
501 
502 	/* Connect to server */
503 	if (proxy != NULL) {
504 		sock = connect_to_server(proxy, final_proxy_port);
505 	} else {
506 		sock = connect_to_server(hostname, final_port);
507 	}
508 
509 	if (sock == -1) {
510 		return (KMF_ERR_CONNECT_SERVER);
511 	}
512 
513 	/* Send the OCSP request */
514 	if (proxy != NULL) {
515 		(void) snprintf(http_hostname, sizeof (http_hostname),
516 		    "http://%s:%d", hostname, final_port);
517 		ret = send_ocsp_request(sock, reqfile, http_hostname);
518 	} else {
519 		ret = send_ocsp_request(sock, reqfile, NULL);
520 	}
521 
522 	if (ret != KMF_OK) {
523 		goto out;
524 	}
525 
526 	/* Retrieve the OCSP response */
527 	if (maxsecs == 0) {
528 		maxsecs = 30; /* default poll time limit is 30 seconds */
529 	}
530 
531 	if ((respfd = open(respfile, O_CREAT |O_RDWR | O_EXCL, 0600)) == -1) {
532 		ret = KMF_ERR_OPEN_FILE;
533 	} else {
534 		ret = get_encoded_response(sock, KMF_RESPONSE_OCSP,
535 			respfd, maxsecs);
536 		(void) close(respfd);
537 	}
538 
539 out:
540 	(void) close(sock);
541 	return (ret);
542 }
543 
544 static KMF_RETURN
545 send_download_request(int sock, char *hostname, int port, boolean_t is_proxy,
546     char *loc)
547 {
548 	KMF_RETURN ret = KMF_OK;
549 	char url[256];
550 	char req_header[1024];
551 	static char req_format[] =
552 "GET %s HTTP/1.0\r\n\
553 Host: %s:%d\r\n\
554 Accept: */*\r\n\r\n";
555 
556 	if (is_proxy) {
557 		(void) snprintf(url, sizeof (url), "http://%s:%d/%s",
558 		    hostname, port, loc);
559 	} else {
560 		(void) snprintf(url, sizeof (url), "/%s", loc);
561 	}
562 
563 	(void) snprintf(req_header, sizeof (req_header), req_format, url,
564 	    hostname, port);
565 
566 	if (write(sock, req_header, strlen(req_header)) < 0) {
567 		ret = KMF_ERR_SEND_REQUEST;
568 	}
569 
570 	return (ret);
571 }
572 
573 static KMF_RETURN
574 download_file(char *uri, char *proxy, int proxy_port,
575     unsigned int maxsecs, int filefd)
576 {
577 	KMF_RETURN ret = KMF_OK;
578 	xmlURIPtr   uriptr;
579 	int sock;
580 	boolean_t is_proxy;
581 	int final_proxy_port;
582 	char *hostname = NULL;
583 	char *path = NULL;
584 	int port;
585 
586 	if (uri == NULL || filefd == -1)
587 		return (KMF_ERR_BAD_PARAMETER);
588 
589 	/* Parse URI */
590 	uriptr = xmlParseURI(uri);
591 	if (uriptr == NULL) {
592 		ret = KMF_ERR_BAD_URI;
593 		goto out;
594 	}
595 
596 	if (uriptr->scheme == NULL ||
597 	    strncasecmp(uriptr->scheme, "http", 4) != 0) {
598 		ret = KMF_ERR_BAD_URI;  /* we support http only */
599 		goto out;
600 	}
601 
602 	/* get the host name */
603 	hostname = uriptr->server;
604 	if (hostname == NULL) {
605 		ret = KMF_ERR_BAD_URI;
606 		goto out;
607 	}
608 
609 	/* get the port number */
610 	port = uriptr->port;
611 	if (port == 0) {
612 		port = 80;
613 	}
614 
615 	/* Get the path */
616 	path = uriptr->path;
617 	if (path == NULL) {
618 		ret = KMF_ERR_BAD_URI;
619 		goto out;
620 	}
621 
622 	/* Connect to server */
623 	if (proxy != NULL) {
624 		final_proxy_port = (proxy_port == 0 || proxy_port == -1) ?
625 			80 : proxy_port;
626 		is_proxy = B_TRUE;
627 		sock = connect_to_server(proxy, final_proxy_port);
628 	} else {
629 		is_proxy = B_FALSE;
630 		sock = connect_to_server(hostname, port);
631 	}
632 	if (sock == -1) {
633 		ret = KMF_ERR_CONNECT_SERVER;
634 		goto out;
635 	}
636 
637 	/* Send the request */
638 	ret = send_download_request(sock, hostname, port, is_proxy, path);
639 	if (ret != KMF_OK) {
640 		goto out;
641 	}
642 
643 	/* Retrieve the response */
644 	ret = get_encoded_response(sock, KMF_RESPONSE_FILE, filefd,
645 	    maxsecs == 0 ? 30 : maxsecs);
646 	if (ret != KMF_OK) {
647 		goto out;
648 	}
649 
650 out:
651 	if (uriptr != NULL)
652 		xmlFreeURI(uriptr);
653 
654 	if (sock != -1)
655 		(void) close(sock);
656 
657 	return (ret);
658 }
659 
660 
661 KMF_RETURN
662 KMF_DownloadCRL(KMF_HANDLE_T handle, char *uri, char *proxy, int proxy_port,
663     unsigned int maxsecs, char *crlfile, KMF_ENCODE_FORMAT *pformat)
664 {
665 	KMF_RETURN ret = KMF_OK;
666 	char *filename = NULL;
667 	char tempfn[MAXPATHLEN];
668 	boolean_t temp_created = B_FALSE;
669 	mode_t old_mode;
670 	int fd = -1, tmpfd = -1;
671 
672 	CLEAR_ERROR(handle, ret);
673 	if (ret != KMF_OK)
674 		return (ret);
675 
676 	if (uri == NULL || crlfile == NULL || pformat == NULL)
677 		return (KMF_ERR_BAD_PARAMETER);
678 
679 	if ((fd = open(crlfile, O_CREAT |O_RDWR | O_EXCL, 0644)) == -1)
680 		return (KMF_ERR_OPEN_FILE);
681 
682 	/*
683 	 * Download the file and save it to a temp file. To make rename()
684 	 * happy, the temp file needs to be created in the same directory as
685 	 * the target file.
686 	 */
687 	if ((filename = strdup(crlfile)) == NULL) {
688 		ret = KMF_ERR_MEMORY;
689 		goto out;
690 	}
691 	(void) snprintf(tempfn, MAXPATHLEN, "%s/%s", dirname(filename),
692 	    TEMP_TEMPLATE);
693 	old_mode = umask(077);
694 	tmpfd = mkstemp(tempfn);
695 	(void) umask(old_mode);
696 	if (tmpfd == -1) {
697 		ret = KMF_ERR_INTERNAL;
698 		goto out;
699 	} else {
700 		temp_created = B_TRUE;
701 	}
702 
703 	ret = download_file(uri, proxy, proxy_port, maxsecs, tmpfd);
704 	(void) close(tmpfd);
705 	if (ret != KMF_OK) {
706 		goto out;
707 	}
708 
709 	/* Check if it is a CRL file and get its format */
710 	if (KMF_IsCRLFile(handle, tempfn, pformat) != KMF_OK) {
711 		ret = KMF_ERR_BAD_CRLFILE;
712 		goto out;
713 	}
714 
715 	/* Finally, change the temp filename to the target crlfile */
716 	if (rename(tempfn, crlfile) == -1) {
717 		ret = KMF_ERR_WRITE_FILE;
718 		goto out;
719 	}
720 
721 out:
722 	if (filename != NULL)
723 		free(filename);
724 
725 	if (ret != KMF_OK && temp_created == B_TRUE)
726 		(void) unlink(tempfn);
727 
728 	if (fd != -1)
729 		(void) close(fd);
730 
731 	return (ret);
732 }
733 
734 
735 KMF_RETURN
736 KMF_DownloadCert(KMF_HANDLE_T handle, char *uri, char *proxy, int proxy_port,
737     unsigned int maxsecs, char *certfile, KMF_ENCODE_FORMAT *pformat)
738 {
739 	KMF_RETURN ret = KMF_OK;
740 	char *filename = NULL;
741 	char tempfn[MAXPATHLEN];
742 	boolean_t temp_created = B_FALSE;
743 	mode_t old_mode;
744 	int fd = -1, tmpfd = -1;
745 
746 	CLEAR_ERROR(handle, ret);
747 	if (ret != KMF_OK)
748 		return (ret);
749 
750 	if (uri == NULL || certfile == NULL || pformat == NULL)
751 		return (KMF_ERR_BAD_PARAMETER);
752 
753 	if ((fd = open(certfile, O_CREAT |O_RDWR | O_EXCL, 0644)) == -1)
754 		return (KMF_ERR_OPEN_FILE);
755 
756 	/*
757 	 * Download the file and save it to a temp file. To make rename()
758 	 * happy, the temp file needs to be created in the same directory as
759 	 * the target file.
760 	 */
761 	if ((filename = strdup(certfile)) == NULL) {
762 		ret = KMF_ERR_MEMORY;
763 		goto out;
764 	}
765 	(void) snprintf(tempfn, MAXPATHLEN, "%s/%s", dirname(filename),
766 	    TEMP_TEMPLATE);
767 
768 	old_mode = umask(077);
769 	tmpfd = mkstemp(tempfn);
770 	(void) umask(old_mode);
771 	if (tmpfd == -1) {
772 		ret = KMF_ERR_INTERNAL;
773 		goto out;
774 	} else {
775 		temp_created = B_TRUE;
776 	}
777 
778 	ret = download_file(uri, proxy, proxy_port, maxsecs, tmpfd);
779 	(void) close(tmpfd);
780 	if (ret != KMF_OK) {
781 		goto out;
782 	}
783 
784 	/* Check if it is a Cert file and get its format */
785 	if (KMF_IsCertFile(handle, tempfn, pformat) != KMF_OK) {
786 		ret = KMF_ERR_BAD_CERTFILE;
787 		goto out;
788 	}
789 
790 	/* Finally, change the temp filename to the target filename */
791 	if (rename(tempfn, certfile) == -1) {
792 		ret = KMF_ERR_WRITE_FILE;
793 		goto out;
794 	}
795 
796 out:
797 	if (filename != NULL)
798 		free(filename);
799 
800 	if (ret != KMF_OK && temp_created == B_TRUE)
801 		(void) unlink(tempfn);
802 
803 	if (fd != -1)
804 		(void) close(fd);
805 
806 	return (ret);
807 }
808 
809 KMF_RETURN
810 KMF_GetOCSPForCert(KMF_HANDLE_T handle,
811 	KMF_DATA *user_cert,
812 	KMF_DATA *ta_cert,
813 	KMF_DATA *response)
814 {
815 	KMF_POLICY_RECORD *policy;
816 	KMF_RETURN ret = KMF_OK;
817 	KMF_OCSPREQUEST_PARAMS req_params;
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 
829 	CLEAR_ERROR(handle, ret);
830 	if (ret != KMF_OK)
831 		return (ret);
832 
833 	if (user_cert == NULL ||
834 		ta_cert == NULL || response == NULL)
835 		return (KMF_ERR_BAD_PARAMETER);
836 
837 	policy = handle->policy;
838 
839 	/* Create an OCSP request  */
840 	req_params.issuer_cert = ta_cert;
841 	req_params.user_cert = user_cert;
842 
843 	/*
844 	 * Create temporary files to hold the OCSP request & response data.
845 	 */
846 	(void) strlcpy(ocsp_reqname, OCSPREQ_TEMPNAME,
847 	    sizeof (ocsp_reqname));
848 	if (mkstemp(ocsp_reqname) == -1) {
849 		return (KMF_ERR_INTERNAL);
850 	}
851 
852 	(void) strlcpy(ocsp_respname, OCSPRESP_TEMPNAME,
853 	    sizeof (ocsp_respname));
854 	if (mkstemp(ocsp_respname) == -1) {
855 		return (KMF_ERR_INTERNAL);
856 	}
857 
858 	ret = KMF_CreateOCSPRequest(handle, &req_params, ocsp_reqname);
859 	if (ret != KMF_OK) {
860 		goto out;
861 	}
862 
863 	if (policy->VAL_OCSP_BASIC.uri_from_cert == 0) {
864 		if (policy->VAL_OCSP_BASIC.responderURI == NULL) {
865 			ret = KMF_ERR_OCSP_POLICY;
866 			goto out;
867 		}
868 		host_uri = policy->VAL_OCSP_BASIC.responderURI;
869 
870 	} else {
871 		/*
872 		 * Get the responder URI from certificate
873 		 * Authority Information Access
874 		 * thru OID_PKIX_AD_OCSP
875 		 */
876 		ret = KMF_GetCertAuthInfoAccessExt(user_cert, &aia);
877 		if (ret != KMF_OK) {
878 			goto out;
879 		}
880 
881 		for (i = 0; i < aia.numberOfAccessDescription; i++) {
882 			access_info = &aia.AccessDesc[i];
883 			if (IsEqualOid(&access_info->AccessMethod,
884 			    (KMF_OID *)&KMFOID_PkixAdOcsp)) {
885 				host_uri =
886 				    (char *)access_info->AccessLocation.Data;
887 				found = B_TRUE;
888 				break;
889 			}
890 		}
891 
892 		if (!found) {
893 			ret = KMF_ERR_OCSP_POLICY;
894 			goto out;
895 		}
896 	}
897 
898 	/* Parse the URI string; get the hostname and port */
899 	uriptr = xmlParseURI(host_uri);
900 	if (uriptr == NULL) {
901 		ret = KMF_ERR_BAD_URI;
902 		goto out;
903 	}
904 
905 	if (strncasecmp(uriptr->scheme, "http", 4) != 0) {
906 		ret = KMF_ERR_BAD_URI;  /* we support http only */
907 		goto out;
908 	}
909 
910 	hostname = uriptr->server;
911 	if (hostname == NULL) {
912 		ret = KMF_ERR_BAD_URI;
913 		goto out;
914 	}
915 
916 	host_port = uriptr->port;
917 	if (host_port == 0)
918 		host_port = 80;
919 
920 	/* get the proxy info */
921 	if (policy->VAL_OCSP_BASIC.proxy != NULL) {
922 		char *last;
923 		proxyname =
924 		    strtok_r(policy->VAL_OCSP_BASIC.proxy, ":", &last);
925 		proxy_port_s = strtok_r(NULL, "\0", &last);
926 		if (proxy_port_s != NULL) {
927 			proxy_port = strtol(proxy_port_s, NULL, 0);
928 		} else {
929 			proxy_port = 8080; /* default */
930 		}
931 	}
932 
933 	/*
934 	 * Send the request to an OCSP responder and receive an
935 	 * OCSP response.
936 	 */
937 	ret = KMF_GetEncodedOCSPResponse(handle, ocsp_reqname,
938 	    hostname, host_port,  proxyname, proxy_port,
939 	    ocsp_respname, 30);
940 	if (ret != KMF_OK) {
941 		goto out;
942 	}
943 
944 	ret = KMF_ReadInputFile(handle, ocsp_respname, response);
945 
946 out:
947 	(void) unlink(ocsp_reqname);
948 	(void) unlink(ocsp_respname);
949 
950 	if (uriptr != NULL)
951 		xmlFreeURI(uriptr);
952 
953 	return (ret);
954 }
955