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