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 */ 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 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 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 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 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 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 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 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 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 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 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