1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * File: CLIENT.C
26 */
27
28 #pragma ident "%Z%%M% %I% %E% SMI"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <poll.h>
35 #include <sys/errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <libgen.h>
45 #include <kmfapi.h>
46 #include <kmfapiP.h>
47 #include <libxml2/libxml/uri.h>
48
49 extern int errno;
50
51 #define OCSP_BUFSIZE 1024
52
53 typedef enum {
54 KMF_RESPONSE_OCSP = 1,
55 KMF_RESPONSE_FILE = 2
56 } KMF_RESPONSE_TYPE;
57
58 #define TEMP_TEMPLATE "temp.XXXXXX"
59
60 /*
61 * This function will establish a socket to the host on the specified port.
62 * If succeed, it return a socket descriptor; otherwise, return -1.
63 */
init_socket(char * host,short port)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
connect_to_server(char * host,short port)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
send_ocsp_request(int sock,char * reqfile,char * hostname)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
looping_write(int fd,void * buf,int len)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
get_encoded_response(int sock,KMF_RESPONSE_TYPE resptype,int filefd,unsigned int maxsecs)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
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)481 kmf_get_encoded_ocsp_response(KMF_HANDLE_T handle,
482 char *reqfile, char *hostname,
483 int port, char *proxy, int proxy_port, char *respfile,
484 unsigned int maxsecs)
485 {
486 KMF_RETURN ret = KMF_OK;
487 int sock, respfd;
488 char http_hostname[256];
489 int final_proxy_port, final_port;
490
491 CLEAR_ERROR(handle, ret);
492 if (ret != KMF_OK)
493 return (ret);
494
495 if (hostname == NULL || reqfile == NULL || respfile == NULL) {
496 return (KMF_ERR_BAD_PARAMETER);
497 }
498
499 final_proxy_port = (proxy_port == 0 || proxy_port == -1) ?
500 80 : proxy_port;
501 final_port = (port == 0 || port == -1) ? 80 : port;
502
503 /* Connect to server */
504 if (proxy != NULL) {
505 sock = connect_to_server(proxy, final_proxy_port);
506 } else {
507 sock = connect_to_server(hostname, final_port);
508 }
509
510 if (sock == -1) {
511 return (KMF_ERR_CONNECT_SERVER);
512 }
513
514 /* Send the OCSP request */
515 if (proxy != NULL) {
516 (void) snprintf(http_hostname, sizeof (http_hostname),
517 "http://%s:%d", hostname, final_port);
518 ret = send_ocsp_request(sock, reqfile, http_hostname);
519 } else {
520 ret = send_ocsp_request(sock, reqfile, NULL);
521 }
522
523 if (ret != KMF_OK) {
524 goto out;
525 }
526
527 /* Retrieve the OCSP response */
528 if (maxsecs == 0) {
529 maxsecs = 30; /* default poll time limit is 30 seconds */
530 }
531
532 if ((respfd = open(respfile, O_CREAT |O_RDWR | O_EXCL, 0600)) == -1) {
533 ret = KMF_ERR_OPEN_FILE;
534 } else {
535 ret = get_encoded_response(sock, KMF_RESPONSE_OCSP,
536 respfd, maxsecs);
537 (void) close(respfd);
538 }
539
540 out:
541 (void) close(sock);
542 return (ret);
543 }
544
545 static KMF_RETURN
send_download_request(int sock,char * hostname,int port,boolean_t is_proxy,char * loc)546 send_download_request(int sock, char *hostname, int port, boolean_t is_proxy,
547 char *loc)
548 {
549 KMF_RETURN ret = KMF_OK;
550 char url[256];
551 char req_header[1024];
552 static char req_format[] =
553 "GET %s HTTP/1.0\r\n\
554 Host: %s:%d\r\n\
555 Accept: */*\r\n\r\n";
556
557 if (is_proxy) {
558 (void) snprintf(url, sizeof (url), "http://%s:%d/%s",
559 hostname, port, loc);
560 } else {
561 (void) snprintf(url, sizeof (url), "/%s", loc);
562 }
563
564 (void) snprintf(req_header, sizeof (req_header), req_format, url,
565 hostname, port);
566
567 if (write(sock, req_header, strlen(req_header)) < 0) {
568 ret = KMF_ERR_SEND_REQUEST;
569 }
570
571 return (ret);
572 }
573
574 static KMF_RETURN
download_file(char * uri,char * proxy,int proxy_port,unsigned int maxsecs,int filefd)575 download_file(char *uri, char *proxy, int proxy_port,
576 unsigned int maxsecs, int filefd)
577 {
578 KMF_RETURN ret = KMF_OK;
579 xmlURIPtr uriptr;
580 int sock;
581 boolean_t is_proxy;
582 int final_proxy_port;
583 char *hostname = NULL;
584 char *path = NULL;
585 int port;
586
587 if (uri == NULL || filefd == -1)
588 return (KMF_ERR_BAD_PARAMETER);
589
590 /* Parse URI */
591 uriptr = xmlParseURI(uri);
592 if (uriptr == NULL) {
593 ret = KMF_ERR_BAD_URI;
594 goto out;
595 }
596
597 if (uriptr->scheme == NULL ||
598 strncasecmp(uriptr->scheme, "http", 4) != 0) {
599 ret = KMF_ERR_BAD_URI; /* we support http only */
600 goto out;
601 }
602
603 /* get the host name */
604 hostname = uriptr->server;
605 if (hostname == NULL) {
606 ret = KMF_ERR_BAD_URI;
607 goto out;
608 }
609
610 /* get the port number */
611 port = uriptr->port;
612 if (port == 0) {
613 port = 80;
614 }
615
616 /* Get the path */
617 path = uriptr->path;
618 if (path == NULL) {
619 ret = KMF_ERR_BAD_URI;
620 goto out;
621 }
622
623 /* Connect to server */
624 if (proxy != NULL) {
625 final_proxy_port = (proxy_port == 0 || proxy_port == -1) ?
626 80 : proxy_port;
627 is_proxy = B_TRUE;
628 sock = connect_to_server(proxy, final_proxy_port);
629 } else {
630 is_proxy = B_FALSE;
631 sock = connect_to_server(hostname, port);
632 }
633 if (sock == -1) {
634 ret = KMF_ERR_CONNECT_SERVER;
635 goto out;
636 }
637
638 /* Send the request */
639 ret = send_download_request(sock, hostname, port, is_proxy, path);
640 if (ret != KMF_OK) {
641 goto out;
642 }
643
644 /* Retrieve the response */
645 ret = get_encoded_response(sock, KMF_RESPONSE_FILE, filefd,
646 maxsecs == 0 ? 30 : maxsecs);
647 if (ret != KMF_OK) {
648 goto out;
649 }
650
651 out:
652 if (uriptr != NULL)
653 xmlFreeURI(uriptr);
654
655 if (sock != -1)
656 (void) close(sock);
657
658 return (ret);
659 }
660
661
662 KMF_RETURN
kmf_download_crl(KMF_HANDLE_T handle,char * uri,char * proxy,int proxy_port,unsigned int maxsecs,char * crlfile,KMF_ENCODE_FORMAT * pformat)663 kmf_download_crl(KMF_HANDLE_T handle, char *uri, char *proxy, int proxy_port,
664 unsigned int maxsecs, char *crlfile, KMF_ENCODE_FORMAT *pformat)
665 {
666 KMF_RETURN ret = KMF_OK;
667 char *filename = NULL;
668 char tempfn[MAXPATHLEN];
669 boolean_t temp_created = B_FALSE;
670 mode_t old_mode;
671 int fd = -1, tmpfd = -1;
672
673 CLEAR_ERROR(handle, ret);
674 if (ret != KMF_OK)
675 return (ret);
676
677 if (uri == NULL || crlfile == NULL || pformat == NULL)
678 return (KMF_ERR_BAD_PARAMETER);
679
680 if ((fd = open(crlfile, O_CREAT |O_RDWR | O_EXCL, 0644)) == -1)
681 return (KMF_ERR_OPEN_FILE);
682
683 /*
684 * Download the file and save it to a temp file. To make rename()
685 * happy, the temp file needs to be created in the same directory as
686 * the target file.
687 */
688 if ((filename = strdup(crlfile)) == NULL) {
689 ret = KMF_ERR_MEMORY;
690 goto out;
691 }
692 (void) snprintf(tempfn, MAXPATHLEN, "%s/%s", dirname(filename),
693 TEMP_TEMPLATE);
694 old_mode = umask(077);
695 tmpfd = mkstemp(tempfn);
696 (void) umask(old_mode);
697 if (tmpfd == -1) {
698 ret = KMF_ERR_INTERNAL;
699 goto out;
700 } else {
701 temp_created = B_TRUE;
702 }
703
704 ret = download_file(uri, proxy, proxy_port, maxsecs, tmpfd);
705 (void) close(tmpfd);
706 if (ret != KMF_OK) {
707 goto out;
708 }
709
710 /* Check if it is a CRL file and get its format */
711 if (kmf_is_crl_file(handle, tempfn, pformat) != KMF_OK) {
712 ret = KMF_ERR_BAD_CRLFILE;
713 goto out;
714 }
715
716 /* Finally, change the temp filename to the target crlfile */
717 if (rename(tempfn, crlfile) == -1) {
718 ret = KMF_ERR_WRITE_FILE;
719 goto out;
720 }
721
722 out:
723 if (filename != NULL)
724 free(filename);
725
726 if (ret != KMF_OK && temp_created == B_TRUE)
727 (void) unlink(tempfn);
728
729 if (fd != -1)
730 (void) close(fd);
731
732 return (ret);
733 }
734
735
736 KMF_RETURN
kmf_download_cert(KMF_HANDLE_T handle,char * uri,char * proxy,int proxy_port,unsigned int maxsecs,char * certfile,KMF_ENCODE_FORMAT * pformat)737 kmf_download_cert(KMF_HANDLE_T handle, char *uri, char *proxy, int proxy_port,
738 unsigned int maxsecs, char *certfile, KMF_ENCODE_FORMAT *pformat)
739 {
740 KMF_RETURN ret = KMF_OK;
741 char *filename = NULL;
742 char tempfn[MAXPATHLEN];
743 boolean_t temp_created = B_FALSE;
744 mode_t old_mode;
745 int fd = -1, tmpfd = -1;
746
747 CLEAR_ERROR(handle, ret);
748 if (ret != KMF_OK)
749 return (ret);
750
751 if (uri == NULL || certfile == NULL || pformat == NULL)
752 return (KMF_ERR_BAD_PARAMETER);
753
754 if ((fd = open(certfile, O_CREAT |O_RDWR | O_EXCL, 0644)) == -1)
755 return (KMF_ERR_OPEN_FILE);
756
757 /*
758 * Download the file and save it to a temp file. To make rename()
759 * happy, the temp file needs to be created in the same directory as
760 * the target file.
761 */
762 if ((filename = strdup(certfile)) == NULL) {
763 ret = KMF_ERR_MEMORY;
764 goto out;
765 }
766 (void) snprintf(tempfn, MAXPATHLEN, "%s/%s", dirname(filename),
767 TEMP_TEMPLATE);
768
769 old_mode = umask(077);
770 tmpfd = mkstemp(tempfn);
771 (void) umask(old_mode);
772 if (tmpfd == -1) {
773 ret = KMF_ERR_INTERNAL;
774 goto out;
775 } else {
776 temp_created = B_TRUE;
777 }
778
779 ret = download_file(uri, proxy, proxy_port, maxsecs, tmpfd);
780 (void) close(tmpfd);
781 if (ret != KMF_OK) {
782 goto out;
783 }
784
785 /* Check if it is a Cert file and get its format */
786 if (kmf_is_cert_file(handle, tempfn, pformat) != KMF_OK) {
787 ret = KMF_ERR_BAD_CERTFILE;
788 goto out;
789 }
790
791 /* Finally, change the temp filename to the target filename */
792 if (rename(tempfn, certfile) == -1) {
793 ret = KMF_ERR_WRITE_FILE;
794 goto out;
795 }
796
797 out:
798 if (filename != NULL)
799 free(filename);
800
801 if (ret != KMF_OK && temp_created == B_TRUE)
802 (void) unlink(tempfn);
803
804 if (fd != -1)
805 (void) close(fd);
806
807 return (ret);
808 }
809
810 KMF_RETURN
kmf_get_ocsp_for_cert(KMF_HANDLE_T handle,KMF_DATA * user_cert,KMF_DATA * ta_cert,KMF_DATA * response)811 kmf_get_ocsp_for_cert(KMF_HANDLE_T handle,
812 KMF_DATA *user_cert,
813 KMF_DATA *ta_cert,
814 KMF_DATA *response)
815 {
816 KMF_POLICY_RECORD *policy;
817 KMF_RETURN ret = KMF_OK;
818 char *hostname = NULL, *host_uri = NULL, *proxyname = NULL;
819 char *proxy_port_s = NULL;
820 int host_port = 0, proxy_port = 0;
821 char ocsp_reqname[MAXPATHLEN];
822 char ocsp_respname[MAXPATHLEN];
823 KMF_X509EXT_AUTHINFOACCESS aia;
824 int i;
825 boolean_t found = B_FALSE;
826 KMF_X509EXT_ACCESSDESC *access_info;
827 xmlURIPtr uriptr = NULL;
828 KMF_ATTRIBUTE attrlist[10];
829 int numattr = 0;
830
831 CLEAR_ERROR(handle, ret);
832 if (ret != KMF_OK)
833 return (ret);
834
835 if (user_cert == NULL || ta_cert == NULL || response == NULL)
836 return (KMF_ERR_BAD_PARAMETER);
837
838 policy = handle->policy;
839
840 /* Create an OCSP request */
841 kmf_set_attr_at_index(attrlist, numattr,
842 KMF_ISSUER_CERT_DATA_ATTR, ta_cert,
843 sizeof (KMF_DATA));
844 numattr++;
845
846 kmf_set_attr_at_index(attrlist, numattr,
847 KMF_USER_CERT_DATA_ATTR, user_cert,
848 sizeof (KMF_DATA));
849 numattr++;
850
851 /*
852 * Create temporary files to hold the OCSP request & response data.
853 */
854 (void) strlcpy(ocsp_reqname, OCSPREQ_TEMPNAME,
855 sizeof (ocsp_reqname));
856 if (mkstemp(ocsp_reqname) == -1) {
857 return (KMF_ERR_INTERNAL);
858 }
859
860 (void) strlcpy(ocsp_respname, OCSPRESP_TEMPNAME,
861 sizeof (ocsp_respname));
862 if (mkstemp(ocsp_respname) == -1) {
863 return (KMF_ERR_INTERNAL);
864 }
865
866 kmf_set_attr_at_index(attrlist, numattr,
867 KMF_OCSP_REQUEST_FILENAME_ATTR, ocsp_respname,
868 strlen(ocsp_respname));
869 numattr++;
870
871 ret = kmf_create_ocsp_request(handle, numattr, attrlist);
872 if (ret != KMF_OK) {
873 goto out;
874 }
875
876 if (policy->VAL_OCSP_BASIC.uri_from_cert == 0) {
877 if (policy->VAL_OCSP_BASIC.responderURI == NULL) {
878 ret = KMF_ERR_OCSP_POLICY;
879 goto out;
880 }
881 host_uri = policy->VAL_OCSP_BASIC.responderURI;
882
883 } else {
884 /*
885 * Get the responder URI from certificate
886 * Authority Information Access
887 * thru OID_PKIX_AD_OCSP
888 */
889 ret = kmf_get_cert_auth_info_access(user_cert, &aia);
890 if (ret != KMF_OK) {
891 goto out;
892 }
893
894 for (i = 0; i < aia.numberOfAccessDescription; i++) {
895 access_info = &aia.AccessDesc[i];
896 if (IsEqualOid(&access_info->AccessMethod,
897 (KMF_OID *)&KMFOID_PkixAdOcsp)) {
898 host_uri =
899 (char *)access_info->AccessLocation.Data;
900 found = B_TRUE;
901 break;
902 }
903 }
904
905 if (!found) {
906 ret = KMF_ERR_OCSP_POLICY;
907 goto out;
908 }
909 }
910
911 /* Parse the URI string; get the hostname and port */
912 uriptr = xmlParseURI(host_uri);
913 if (uriptr == NULL) {
914 ret = KMF_ERR_BAD_URI;
915 goto out;
916 }
917
918 if (strncasecmp(uriptr->scheme, "http", 4) != 0) {
919 ret = KMF_ERR_BAD_URI; /* we support http only */
920 goto out;
921 }
922
923 hostname = uriptr->server;
924 if (hostname == NULL) {
925 ret = KMF_ERR_BAD_URI;
926 goto out;
927 }
928
929 host_port = uriptr->port;
930 if (host_port == 0)
931 host_port = 80;
932
933 /* get the proxy info */
934 if (policy->VAL_OCSP_BASIC.proxy != NULL) {
935 char *last;
936 proxyname =
937 strtok_r(policy->VAL_OCSP_BASIC.proxy, ":", &last);
938 proxy_port_s = strtok_r(NULL, "\0", &last);
939 if (proxy_port_s != NULL) {
940 proxy_port = strtol(proxy_port_s, NULL, 0);
941 } else {
942 proxy_port = 8080; /* default */
943 }
944 }
945
946 /*
947 * Send the request to an OCSP responder and receive an
948 * OCSP response.
949 */
950 ret = kmf_get_encoded_ocsp_response(handle, ocsp_reqname,
951 hostname, host_port, proxyname, proxy_port,
952 ocsp_respname, 30);
953 if (ret != KMF_OK) {
954 goto out;
955 }
956
957 ret = kmf_read_input_file(handle, ocsp_respname, response);
958
959 out:
960 (void) unlink(ocsp_reqname);
961 (void) unlink(ocsp_respname);
962
963 if (uriptr != NULL)
964 xmlFreeURI(uriptr);
965
966 return (ret);
967 }
968