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 */ 64 static int init_socket(char *host, short port) 65 { 66 struct sockaddr_in sin; 67 struct hostent *hp, hrec; 68 int sockfd, opt, herrno; 69 char hostbuf[BUFSIZ]; 70 71 sin.sin_family = PF_INET; 72 sin.sin_port = htons(port); 73 if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { 74 if ((hp = gethostbyname_r(host, &hrec, hostbuf, 75 sizeof (hostbuf), &herrno)) == NULL) { 76 return (-1); 77 } 78 (void) memcpy((char *)&sin.sin_addr, hp->h_addr, 79 hp->h_length); 80 } 81 82 if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { 83 return (-1); 84 } 85 86 opt = 1; 87 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, 88 sizeof (opt)) < 0) { 89 (void) close(sockfd); 90 return (-1); 91 } 92 93 if (connect(sockfd, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 94 (void) close(sockfd); 95 return (-1); 96 } 97 98 return (sockfd); 99 } 100 101 /* 102 * This function will connect to host on the port. 103 * If succeed, return a socket descriptor; otherwise, return 0. 104 */ 105 static int 106 connect_to_server(char *host, short port) 107 { 108 int retry = 1; 109 int sd = 0; 110 111 while (retry) { 112 if ((sd = init_socket(host, port)) == -1) { 113 if (errno == ECONNREFUSED) { 114 retry = 1; 115 (void) sleep(1); 116 } else { 117 retry = 0; 118 } 119 } else { 120 retry = 0; 121 } 122 } 123 return (sd); 124 } 125 126 static KMF_RETURN 127 send_ocsp_request(int sock, char *reqfile, char *hostname) 128 { 129 KMF_RETURN ret = KMF_OK; 130 int filefd, bytes, n, total = 0; 131 char buf[OCSP_BUFSIZE]; 132 struct stat s; 133 char req_header[256]; 134 static char req_format[] = 135 "POST %s HTTP/1.0\r\n\ 136 Content-Type: application/ocsp-request\r\n\ 137 Content-Length: %d\r\n\r\n"; 138 139 if ((filefd = open(reqfile, O_RDONLY)) == -1) { 140 ret = KMF_ERR_OPEN_FILE; 141 return (ret); 142 } 143 144 /* open the request file */ 145 if (fstat(filefd, &s) < 0) { 146 ret = KMF_ERR_OPEN_FILE; 147 return (ret); 148 } 149 150 151 /* Send http header */ 152 if (hostname != NULL) { 153 (void) snprintf(req_header, 256, req_format, hostname, 154 s.st_size); 155 } else { 156 (void) snprintf(req_header, 256, req_format, "/", s.st_size); 157 } 158 bytes = strlen(req_header); 159 160 if ((n = write(sock, req_header, bytes)) < 0) { 161 ret = KMF_ERR_SEND_REQUEST; 162 goto exit; 163 } 164 165 /* Send the request content */ 166 while ((bytes = read(filefd, buf, OCSP_BUFSIZE)) > 0) { 167 if ((n = write(sock, buf, bytes)) < 0) { 168 ret = KMF_ERR_SEND_REQUEST; 169 goto exit; 170 } 171 total += n; 172 (void) memset(buf, 0, sizeof (buf)); 173 } 174 175 exit: 176 (void) close(filefd); 177 return (ret); 178 } 179 180 181 /* 182 * Perform a write that can handle EINTR. 183 */ 184 static int 185 looping_write(int fd, void *buf, int len) 186 { 187 char *p = buf; 188 int cc, len2 = 0; 189 190 if (len == 0) 191 return (0); 192 do { 193 cc = write(fd, p, len); 194 if (cc < 0) { 195 if (errno == EINTR) 196 continue; 197 return (cc); 198 } else if (cc == 0) { 199 return (len2); 200 } else { 201 p += cc; 202 len2 += cc; 203 len -= cc; 204 } 205 } while (len > 0); 206 207 return (len2); 208 } 209 210 /* 211 * This function will get the response from the server, check the http status 212 * line, and write the response content to a file. If this is a OCSP response, 213 * it will check the content type also. 214 */ 215 static KMF_RETURN 216 get_encoded_response(int sock, KMF_RESPONSE_TYPE resptype, int filefd, 217 unsigned int maxsecs) 218 { 219 int ret = KMF_OK; 220 char *buf = NULL; 221 int buflen = 0; 222 int offset = 0; 223 int search_offset; 224 const int buf_incre = OCSP_BUFSIZE; /* 1 KB at a time */ 225 const int maxBufSize = 8 * buf_incre; /* 8 KB max */ 226 const char *CRLF = "\r\n"; 227 const char *headerEndMark = "\r\n\r\n"; 228 const char *httpprotocol = "HTTP/"; 229 const int CRLFlen = strlen(CRLF); 230 const int marklen = strlen(headerEndMark); 231 const int httplen = strlen(httpprotocol); 232 char *headerEnd = NULL; 233 boolean_t EOS = B_FALSE; 234 const char *httpcode = NULL; 235 const char *contenttype = NULL; 236 int contentlength = 0; 237 int bytes = 0; 238 char *statusLineEnd = NULL; 239 char *space = NULL; 240 char *nextHeader = NULL; 241 struct pollfd pfd; 242 int sock_flag; 243 int poll_ret; 244 boolean_t timeout = B_FALSE; 245 246 /* set O_NONBLOCK flag on socket */ 247 if ((sock_flag = fcntl(sock, F_GETFL, 0)) == -1) { 248 return (KMF_ERR_RECV_RESPONSE); 249 } 250 sock_flag |= O_NONBLOCK; 251 if (fcntl(sock, F_SETFL, sock_flag) == -1) { 252 return (KMF_ERR_RECV_RESPONSE); 253 } 254 255 /* set up poll */ 256 pfd.fd = sock; 257 pfd.events = POLLIN; 258 259 /* 260 * First read HTTP status line and headers. We will read up to at 261 * least the end of the HTTP headers 262 */ 263 do { 264 if ((buflen - offset) < buf_incre) { 265 buflen += buf_incre; 266 buf = realloc(buf, buflen + 1); 267 if (buf == NULL) { 268 ret = KMF_ERR_MEMORY; 269 goto out; 270 } 271 } 272 273 pfd.revents = 0; 274 poll_ret = poll(&pfd, 1, maxsecs * MILLISEC); 275 if (poll_ret == 0) { 276 timeout = B_TRUE; 277 break; 278 } else if (poll_ret < 0) { 279 ret = KMF_ERR_RECV_RESPONSE; 280 goto out; 281 } else { 282 if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { 283 ret = KMF_ERR_RECV_RESPONSE; 284 goto out; 285 } 286 } 287 288 bytes = read(sock, buf + offset, buf_incre); 289 if (bytes < 0) { 290 if (errno == EWOULDBLOCK) { /* no data this time */ 291 continue; 292 } else { 293 ret = KMF_ERR_RECV_RESPONSE; 294 goto out; 295 } 296 } else if (bytes == 0) { /* no more data */ 297 EOS = B_TRUE; 298 } else { /* bytes > 0 */ 299 search_offset = (offset - marklen) > 0 ? 300 offset - marklen : 0; 301 offset += bytes; 302 *(buf + offset) = '\0'; /* NULL termination */ 303 304 headerEnd = strstr((const char *)buf + search_offset, 305 headerEndMark); 306 } 307 308 } while ((!headerEnd) && (EOS == B_FALSE) && (buflen < maxBufSize)); 309 310 if (timeout == B_TRUE) { 311 ret = KMF_ERR_RECV_TIMEOUT; 312 goto out; 313 } else if (headerEnd == NULL) { 314 /* could not find the end of headers */ 315 ret = KMF_ERR_BAD_HTTP_RESPONSE; 316 goto out; 317 } 318 319 /* 320 * Parse the HTTP status line, which will look like this: 321 * "HTTP/1.1 200 OK". 322 */ 323 statusLineEnd = strstr((const char *)buf, CRLF); 324 if (statusLineEnd == NULL) { 325 ret = KMF_ERR_BAD_HTTP_RESPONSE; 326 goto out; 327 } 328 *statusLineEnd = '\0'; 329 330 space = strchr((const char *)buf, ' '); 331 if (space == NULL || 332 (strncasecmp((const char *)buf, httpprotocol, httplen) != 0)) { 333 ret = KMF_ERR_BAD_HTTP_RESPONSE; 334 goto out; 335 } 336 337 /* 338 * Check the HTTP status code. If it is not 200, the HTTP response 339 * is not good. 340 */ 341 httpcode = space + 1; 342 space = strchr(httpcode, ' '); 343 if (space == NULL) { 344 ret = KMF_ERR_BAD_HTTP_RESPONSE; 345 goto out; 346 } 347 348 *space = 0; 349 if (strcmp(httpcode, "200") != 0) { 350 ret = KMF_ERR_BAD_HTTP_RESPONSE; 351 goto out; 352 } 353 354 /* 355 * Parse the HTTP headers in the buffer. Save content-type and 356 * content-length only. 357 */ 358 nextHeader = statusLineEnd + CRLFlen; 359 *headerEnd = '\0'; /* terminate */ 360 do { 361 char *thisHeaderEnd = NULL; 362 char *value = NULL; 363 char *colon = strchr(nextHeader, ':'); 364 365 if (colon == NULL) { 366 ret = KMF_ERR_BAD_HTTP_RESPONSE; 367 goto out; 368 } 369 *colon = '\0'; 370 371 value = colon + 1; 372 if (*value != ' ') { 373 ret = KMF_ERR_BAD_HTTP_RESPONSE; 374 goto out; 375 } 376 value++; 377 378 thisHeaderEnd = strstr(value, CRLF); 379 if (thisHeaderEnd != NULL) 380 *thisHeaderEnd = '\0'; 381 382 if (strcasecmp(nextHeader, "content-type") == 0) { 383 contenttype = value; 384 } else if (strcasecmp(nextHeader, "content-length") == 0) { 385 contentlength = atoi(value); 386 } 387 388 if (thisHeaderEnd != NULL) { 389 nextHeader = thisHeaderEnd + CRLFlen; 390 } else { 391 nextHeader = NULL; 392 } 393 394 } while (nextHeader && (nextHeader < (headerEnd + CRLFlen))); 395 396 /* Check the contenttype if this is an OCSP response */ 397 if (resptype == KMF_RESPONSE_OCSP) { 398 if (contenttype == NULL) { 399 ret = KMF_ERR_BAD_HTTP_RESPONSE; 400 goto out; 401 } else if (strcasecmp(contenttype, 402 "application/ocsp-response") != 0) { 403 ret = KMF_ERR_BAD_HTTP_RESPONSE; 404 goto out; 405 } 406 } 407 408 /* Now we are ready to read the body of the response */ 409 offset = offset - (int)(headerEnd - (const char *)buf) - marklen; 410 if (offset) { 411 /* move all data to the beginning of the buffer */ 412 (void) memmove(buf, headerEnd + marklen, offset); 413 } 414 415 /* resize buffer to only what's needed to hold the current response */ 416 buflen = (1 + (offset-1) / buf_incre) * buf_incre; 417 418 while ((EOS == B_FALSE) && 419 ((contentlength == 0) || (offset < contentlength)) && 420 (buflen < maxBufSize)) { 421 /* we still need to receive more content data */ 422 if ((buflen - offset) < buf_incre) { 423 buflen += buf_incre; 424 buf = realloc(buf, buflen + 1); 425 if (buf == NULL) { 426 ret = KMF_ERR_MEMORY; 427 goto out; 428 } 429 } 430 431 pfd.revents = 0; 432 poll_ret = poll(&pfd, 1, maxsecs * MILLISEC); 433 if (poll_ret == 0) { 434 timeout = B_TRUE; 435 break; 436 } else if (poll_ret < 0) { 437 ret = KMF_ERR_RECV_RESPONSE; 438 goto out; 439 } else { 440 if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { 441 ret = KMF_ERR_RECV_RESPONSE; 442 goto out; 443 } 444 } 445 446 bytes = read(sock, buf + offset, buf_incre); 447 if (bytes < 0) { 448 if (errno == EWOULDBLOCK) { 449 continue; 450 } else { 451 ret = KMF_ERR_RECV_RESPONSE; 452 goto out; 453 } 454 } else if (bytes == 0) { /* no more data */ 455 EOS = B_TRUE; 456 } else { 457 offset += bytes; 458 } 459 } 460 461 if (timeout == B_TRUE) { 462 ret = KMF_ERR_RECV_TIMEOUT; 463 goto out; 464 } else if (((contentlength != 0) && (offset < contentlength)) || 465 offset == 0) { 466 ret = KMF_ERR_BAD_HTTP_RESPONSE; 467 goto out; 468 } 469 470 /* write to the file */ 471 if (looping_write(filefd, buf, offset) != offset) { 472 ret = KMF_ERR_WRITE_FILE; 473 } 474 475 out: 476 free(buf); 477 return (ret); 478 } 479 480 KMF_RETURN 481 kmf_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 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 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 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 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 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