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
49extern int errno;
50
51#define	OCSP_BUFSIZE 1024
52
53typedef 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 */
64static 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 */
105static int
106connect_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
126static KMF_RETURN
127send_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\
136Content-Type: application/ocsp-request\r\n\
137Content-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
175exit:
176	(void) close(filefd);
177	return (ret);
178}
179
180
181/*
182 * Perform a write that can handle EINTR.
183 */
184static int
185looping_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 */
215static KMF_RETURN
216get_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
475out:
476	free(buf);
477	return (ret);
478}
479
480KMF_RETURN
481kmf_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
540out:
541	(void) close(sock);
542	return (ret);
543}
544
545static KMF_RETURN
546send_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\
554Host: %s:%d\r\n\
555Accept: */*\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
574static KMF_RETURN
575download_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
651out:
652	if (uriptr != NULL)
653		xmlFreeURI(uriptr);
654
655	if (sock != -1)
656		(void) close(sock);
657
658	return (ret);
659}
660
661
662KMF_RETURN
663kmf_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
722out:
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
736KMF_RETURN
737kmf_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
797out:
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
810KMF_RETURN
811kmf_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
959out:
960	(void) unlink(ocsp_reqname);
961	(void) unlink(ocsp_respname);
962
963	if (uriptr != NULL)
964		xmlFreeURI(uriptr);
965
966	return (ret);
967}
968