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