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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #include <stdio.h> 32 #include <limits.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <pkglocs.h> 37 #include <locale.h> 38 #include <libintl.h> 39 #include <libgen.h> 40 #include <signal.h> 41 #include <sys/stat.h> 42 #include <sys/statvfs.h> 43 #include <sys/types.h> 44 #include <fcntl.h> 45 #include <dirent.h> 46 #include <boot_http.h> 47 #include <errno.h> 48 #include <ctype.h> 49 #include <openssl/pkcs7.h> 50 #include <openssl/ocsp.h> 51 #include <openssl/pkcs12.h> 52 #include <openssl/err.h> 53 #include <openssl/x509.h> 54 #include <openssl/pem.h> 55 #include <openssl/evp.h> 56 #include <openssl/rand.h> 57 #include <openssl/x509v3.h> 58 #include "pkglib.h" 59 #include "pkglibmsgs.h" 60 #include "pkglocale.h" 61 #include "keystore.h" 62 #include "pkgweb.h" 63 #include "pkgerr.h" 64 #include "p12lib.h" 65 66 /* fixed format when making an OCSP request */ 67 #define OCSP_REQUEST_FORMAT \ 68 "POST %s HTTP/1.0\r\n" \ 69 "Content-Type: application/ocsp-request\r\n" \ 70 "Content-Length: %d\r\n\r\n" 71 72 /* 73 * no security is afforded by using this phrase to "encrypt" CA certificates, 74 * but it might aid in debugging and has to be non-null 75 */ 76 #define WEB_CA_PHRASE "schizophrenic" 77 78 /* This one needs the ': ' at the end */ 79 #define CONTENT_TYPE_HDR "Content-Type" 80 #define CONTENT_DISPOSITION_HDR "Content-Disposition" 81 #define CONTENT_OCSP_RESP "application/ocsp-response" 82 #define CONTENT_LENGTH_HDR "Content-Length" 83 #define LAST_MODIFIED_HDR "Last-Modified" 84 #define OCSP_BUFSIZ 1024 85 86 /* 87 * default amount of time that is allowed for error when checking 88 * OCSP response validity. 89 * For example, if this is set to 5 minutes, then if a response 90 * is issued that is valid from 12:00 to 1:00, then we will 91 * accept it if the local time is between 11:55 and 1:05. 92 * This takes care of not-quite-synchronized server and client clocks. 93 */ 94 #define OCSP_VALIDITY_PERIOD (5 * 60) 95 96 /* this value is defined by getpassphrase(3c) manpage */ 97 #define MAX_PHRASELEN 257 98 99 /* Max length of "enter password again" prompt message */ 100 #define MAX_VERIFY_MSGLEN 1024 101 102 /* local prototypes */ 103 static boolean_t remove_dwnld_file(char *); 104 static boolean_t get_ENV_proxyport(PKG_ERR *, ushort_t *); 105 static boolean_t make_link(char *, char *); 106 static WebStatus web_send_request(PKG_ERR *, int, int, int); 107 static boolean_t web_eval_headers(PKG_ERR *); 108 static WebStatus web_get_file(PKG_ERR *, char *, int, char **); 109 static boolean_t ck_dwnld_dir_space(PKG_ERR *, char *, ulong_t); 110 static WebStatus web_connect(PKG_ERR *); 111 static boolean_t web_setup(PKG_ERR *); 112 static boolean_t check_dwnld_dir(PKG_ERR *, char *); 113 static boolean_t parse_url_proxy(PKG_ERR *, char *, char *, ushort_t); 114 static boolean_t web_disconnect(void); 115 static char *get_unique_filename(char *, char *); 116 static boolean_t get_ENV_proxy(PKG_ERR *, char **); 117 static char *condense_lastmodified(char *); 118 static int web_verify(int, X509_STORE_CTX *); 119 static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); 120 static boolean_t get_ocsp_uri(X509 *, char **); 121 static OCSPStatus ocsp_verify(PKG_ERR *, X509 *, X509 *, char *, url_hport_t *, 122 STACK_OF(X509) *); 123 static char *get_time_string(ASN1_GENERALIZEDTIME *); 124 static char *write_ca_file(PKG_ERR *, char *, STACK_OF(X509) *, char *); 125 static boolean_t _get_random_info(void *, int); 126 static boolean_t init_session(void); 127 static void progress_setup(int, ulong_t); 128 static void progress_report(int, ulong_t); 129 static void progress_finish(int); 130 static char *replace_token(char *, char, char); 131 static void dequote(char *); 132 static void trim(char *); 133 134 135 /* 136 * structure used to hold data passed back to the 137 * X509 verify callback routine in validate_signature() 138 */ 139 typedef struct { 140 url_hport_t *proxy; 141 PKG_ERR *err; 142 STACK_OF(X509) *cas; 143 } verify_cb_data_t; 144 145 /* Progress bar variables */ 146 static ulong_t const_increment, const_divider, completed, const_completed; 147 148 /* current network backoff wait period */ 149 static int cur_backoff = 0; 150 151 /* download session context handle */ 152 static WEB_SESSION *ps; 153 154 static int webpkg_install = 0; 155 static char *prompt = NULL; 156 static char *passarg = NULL; 157 158 159 /* ~~~~~~~~~~~~~~ Public Functions ~~~~~~~~~~~~~~~~~~~ */ 160 161 /* 162 * Name: set_prompt 163 * Description: Specifies the prompt to use with the pkglib 164 * passphrase callback routine. 165 * 166 * Arguments: newprompt - The prompt to display 167 * 168 * Returns : NONE 169 */ 170 void 171 set_passphrase_prompt(char *newprompt) 172 { 173 prompt = newprompt; 174 } 175 176 /* 177 * Name: set_passarg 178 * Description: Specifies the passphrase retrieval method 179 * to use with the pkglib 180 * passphrase callback routine. 181 * 182 * Arguments: newpassarg - The new password retrieval arg 183 * 184 * Returns : NONE 185 */ 186 void 187 set_passphrase_passarg(char *newpassarg) 188 { 189 passarg = newpassarg; 190 } 191 192 /* 193 * Name: get_proxy_port 194 * Description: Resolves proxy specification 195 * 196 * Arguments: err - where to record any errors. 197 * proxy - Location to store result - if *proxy is not 198 * null, then it will be validated, but not changed 199 * 200 * Returns : B_TRUE - success, B_FALSE otherwise 201 * on success, *proxy and *port are set to either 202 * the user-supplied proxy and port, or the 203 * ones found in the environment variables 204 * HTTPPROXY and/or HTTPROXYPORT 205 */ 206 boolean_t 207 get_proxy_port(PKG_ERR *err, char **proxy, ushort_t *port) 208 { 209 if (*proxy != NULL) { 210 if (!path_valid(*proxy)) { 211 /* bad proxy supplied */ 212 pkgerr_add(err, PKGERR_WEB, 213 gettext(ERR_BAD_PROXY), *proxy); 214 return (B_FALSE); 215 } 216 if (!get_ENV_proxyport(err, port)) { 217 /* env set, but bad */ 218 return (B_FALSE); 219 } 220 } else { 221 if (!get_ENV_proxy(err, proxy)) { 222 /* environment variable set, but bad */ 223 return (B_FALSE); 224 } 225 if ((*proxy != NULL) && !path_valid(*proxy)) { 226 /* env variable set, but bad */ 227 pkgerr_add(err, PKGERR_WEB, 228 gettext(ERR_BAD_PROXY), *proxy); 229 return (B_FALSE); 230 } 231 if (!get_ENV_proxyport(err, port)) { 232 /* env variable set, but bad */ 233 return (B_FALSE); 234 } 235 } 236 return (B_TRUE); 237 } 238 239 /* 240 * Name: path_valid 241 * Description: Checks a string for being a valid path 242 * 243 * Arguments: path - path to validate 244 * 245 * Returns : B_TRUE - success, B_FALSE otherwise. 246 * B_FALSE means path was null, too long (>PATH_MAX), 247 * or too short (<1) 248 */ 249 boolean_t 250 path_valid(char *path) 251 { 252 if (path == NULL) { 253 return (B_FALSE); 254 } else if (strlen(path) > PATH_MAX) { 255 return (B_FALSE); 256 } else if (strlen(path) >= 1) { 257 return (B_TRUE); 258 } else { 259 /* path < 1 */ 260 return (B_FALSE); 261 } 262 } 263 264 /* 265 * Name: web_cleanup 266 * Description: Deletes temp files, closes, frees memory taken 267 * by 'ps' static structure 268 * 269 * Arguments: none 270 * 271 * Returns : none 272 */ 273 void 274 web_cleanup(void) 275 { 276 PKG_ERR *err; 277 278 if (ps == NULL) 279 return; 280 281 err = pkgerr_new(); 282 283 if (ps->keystore) { 284 (void) close_keystore(err, ps->keystore, NULL); 285 } 286 287 ps->keystore = NULL; 288 289 pkgerr_free(err); 290 291 if (ps->uniqfile) { 292 (void) remove_dwnld_file(ps->uniqfile); 293 free(ps->uniqfile); 294 ps->uniqfile = NULL; 295 } 296 if (ps->link) { 297 (void) remove_dwnld_file(ps->link); 298 free(ps->link); 299 ps->link = NULL; 300 } 301 if (ps->dwnld_dir) { 302 (void) rmdir(ps->dwnld_dir); 303 ps->dwnld_dir = NULL; 304 } 305 if (ps->errstr) { 306 free(ps->errstr); 307 ps->errstr = NULL; 308 } 309 310 if (ps->content) { 311 free(ps->content); 312 ps->content = NULL; 313 } 314 315 if (ps->resp) { 316 http_free_respinfo(ps->resp); 317 ps->resp = NULL; 318 } 319 320 if (ps) { 321 free(ps); 322 ps = NULL; 323 } 324 } 325 326 /* 327 * Name: web_session_control 328 * Description: Downloads an arbitrary URL and saves to disk. 329 * 330 * Arguments: err - where to record any errors. 331 * url - URL pointing to content to download - can be 332 * http:// or https:// 333 * dwnld_dir - Directory to download into 334 * keystore - keystore to use for accessing trusted 335 * certs when downloading using SSL 336 * proxy - HTTP proxy to use, or NULL for no proxy 337 * proxy_port - HTTP proxy port to use, ignored 338 * if proxy is NULL 339 * passarg - method to retrieve password 340 * retries - # of times to retry download before 341 * giving up 342 * timeout - how long to wait before retrying, 343 * when download is interrupted 344 * nointeract - if non-zero, do not output 345 * download progress to screen 346 * 347 * Returns : B_TRUE - success, B_FALSE otherwise 348 */ 349 boolean_t 350 web_session_control(PKG_ERR *err, char *url, char *dwnld_dir, 351 keystore_handle_t keystore, char *proxy, ushort_t proxy_port, 352 int retries, int timeout, int nointeract, char **fname) 353 { 354 int i; 355 boolean_t ret = B_TRUE; 356 boolean_t retrieved = B_FALSE; 357 358 if (!init_session()) { 359 ret = B_FALSE; 360 goto cleanup; 361 } 362 363 if (!parse_url_proxy(err, url, proxy, proxy_port)) { 364 ret = B_FALSE; 365 goto cleanup; 366 } 367 368 ps->timeout = timeout; 369 370 if (keystore != NULL) 371 ps->keystore = keystore; 372 373 if (dwnld_dir != NULL) 374 ps->dwnld_dir = xstrdup(dwnld_dir); 375 else { 376 pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_DWNLD_DIR)); 377 ret = B_FALSE; 378 goto cleanup; 379 } 380 381 if (!check_dwnld_dir(err, dwnld_dir)) { 382 ret = B_FALSE; 383 goto cleanup; 384 } 385 386 for (i = 0; i < retries && !retrieved; i++) { 387 if (!web_setup(err)) { 388 ret = B_FALSE; 389 goto cleanup; 390 } 391 392 switch (web_connect(err)) { 393 /* time out and wait a little bit for these failures */ 394 case WEB_OK: 395 /* were able to connect */ 396 reset_backoff(); 397 break; 398 case WEB_TIMEOUT: 399 echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT)); 400 (void) web_disconnect(); 401 backoff(); 402 continue; 403 404 case WEB_CONNREFUSED: 405 echo_out(nointeract, gettext(MSG_DWNLD_CONNREF), 406 ps->url.hport.hostname); 407 (void) web_disconnect(); 408 backoff(); 409 continue; 410 case WEB_HOSTDOWN: 411 echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN), 412 ps->url.hport.hostname); 413 (void) web_disconnect(); 414 backoff(); 415 continue; 416 417 default: 418 /* every other failure is a hard failure, so bail */ 419 ret = B_FALSE; 420 goto cleanup; 421 } 422 423 switch (web_send_request(err, HTTP_REQ_TYPE_HEAD, 424 ps->data.cur_pos, ps->data.content_length)) { 425 case WEB_OK: 426 /* were able to connect */ 427 reset_backoff(); 428 break; 429 case WEB_TIMEOUT: 430 echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT)); 431 (void) web_disconnect(); 432 backoff(); 433 continue; 434 435 case WEB_CONNREFUSED: 436 echo_out(nointeract, gettext(MSG_DWNLD_CONNREF), 437 ps->url.hport.hostname); 438 (void) web_disconnect(); 439 backoff(); 440 continue; 441 case WEB_HOSTDOWN: 442 echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN), 443 ps->url.hport.hostname); 444 (void) web_disconnect(); 445 backoff(); 446 continue; 447 default: 448 /* every other case is failure, so bail */ 449 ret = B_FALSE; 450 goto cleanup; 451 } 452 453 if (!web_eval_headers(err)) { 454 ret = B_FALSE; 455 goto cleanup; 456 } 457 458 switch (web_get_file(err, dwnld_dir, nointeract, fname)) { 459 case WEB_OK: 460 /* were able to retrieve file */ 461 retrieved = B_TRUE; 462 reset_backoff(); 463 break; 464 465 case WEB_TIMEOUT: 466 echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT)); 467 (void) web_disconnect(); 468 backoff(); 469 continue; 470 471 case WEB_CONNREFUSED: 472 echo_out(nointeract, gettext(MSG_DWNLD_CONNREF), 473 ps->url.hport.hostname); 474 (void) web_disconnect(); 475 backoff(); 476 continue; 477 case WEB_HOSTDOWN: 478 echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN), 479 ps->url.hport.hostname); 480 (void) web_disconnect(); 481 backoff(); 482 continue; 483 default: 484 /* every other failure is a hard failure, so bail */ 485 ret = B_FALSE; 486 goto cleanup; 487 } 488 } 489 490 if (!retrieved) { 491 /* max retries attempted */ 492 pkgerr_add(err, PKGERR_WEB, 493 gettext(ERR_DWNLD_FAILED), retries); 494 ret = B_FALSE; 495 } 496 cleanup: 497 (void) web_disconnect(); 498 if (!ret) { 499 pkgerr_add(err, PKGERR_WEB, gettext(ERR_DWNLD), url); 500 } 501 return (ret); 502 } 503 504 /* 505 * Name: get_signature 506 * Description: retrieves signature from signed package. 507 * 508 * Arguments: err - where to record any errors. 509 * ids_name - name of package stream, for error reporting 510 * devp - Device on which package resides that we 511 * result - where to store resulting PKCS7 signature 512 * 513 * Returns : B_TRUE - package is signed and signature returned OR 514 * package is not signed, in which case result is NULL 515 * 516 * B_FALSE - there were problems accessing signature, 517 * and it is unknown whether it is signed or not. Errors 518 * recorded in 'err'. 519 */ 520 boolean_t 521 get_signature(PKG_ERR *err, char *ids_name, struct pkgdev *devp, PKCS7 **result) 522 { 523 char path[PATH_MAX]; 524 int len, fd = -1; 525 struct stat buf; 526 FILE *fp = NULL; 527 boolean_t ret = B_TRUE; 528 BIO *sig_in = NULL; 529 PKCS7 *p7 = NULL; 530 531 /* 532 * look for signature. If one was in the stream, 533 * it is now extracted 534 */ 535 if (((len = snprintf(path, PATH_MAX, "%s/%s", devp->dirname, 536 SIGNATURE_FILENAME)) >= PATH_MAX) || (len < 0)) { 537 pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), ids_name); 538 ret = B_FALSE; 539 goto cleanup; 540 } 541 542 if ((fd = open(path, O_RDONLY|O_NONBLOCK)) == -1) { 543 /* 544 * only if the signature is non-existant 545 * do we "pass" 546 */ 547 if (errno != ENOENT) { 548 pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG), 549 strerror(errno)); 550 ret = B_FALSE; 551 goto cleanup; 552 } 553 } else { 554 /* found sig file. parse it. */ 555 if (fstat(fd, &buf) == -1) { 556 pkgerr_add(err, PKGERR_WEB, 557 gettext(ERR_OPENSIG), strerror(errno)); 558 ret = B_FALSE; 559 goto cleanup; 560 } 561 562 if (!S_ISREG(buf.st_mode)) { 563 pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG), 564 (gettext(ERR_NOT_REG))); 565 ret = B_FALSE; 566 goto cleanup; 567 } 568 569 if ((fp = fdopen(fd, "r")) == NULL) { 570 pkgerr_add(err, PKGERR_WEB, 571 gettext(ERR_OPENSIG), strerror(errno)); 572 ret = B_FALSE; 573 goto cleanup; 574 } 575 576 /* 577 * read in signature. If it's invalid, we 578 * punt, unless we're ignoring it 579 */ 580 if ((sig_in = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) { 581 pkgerr_add(err, PKGERR_WEB, 582 gettext(ERR_OPENSIG), strerror(errno)); 583 goto cleanup; 584 } 585 586 if ((p7 = PEM_read_bio_PKCS7(sig_in, 587 NULL, NULL, NULL)) == NULL) { 588 pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), 589 ids_name); 590 ret = B_FALSE; 591 goto cleanup; 592 } 593 *result = p7; 594 p7 = NULL; 595 } 596 597 cleanup: 598 if (sig_in) 599 (void) BIO_free(sig_in); 600 if (fp) 601 (void) fclose(fp); 602 if (fd != -1) 603 (void) close(fd); 604 if (p7) 605 (void) PKCS7_free(p7); 606 607 return (ret); 608 } 609 610 /* 611 * Name: echo_out 612 * Description: Conditionally output a message to stdout 613 * 614 * Arguments: nointeract - if non-zero, do not output anything 615 * fmt - print format 616 * ... - print arguments 617 * 618 * Returns : none 619 */ 620 /*PRINTFLIKE2*/ 621 void 622 echo_out(int nointeract, char *fmt, ...) 623 { 624 va_list ap; 625 626 va_start(ap, fmt); 627 628 if (nointeract) 629 return; 630 631 (void) vfprintf(stdout, fmt, ap); 632 633 va_end(ap); 634 635 (void) putc('\n', stdout); 636 } 637 638 /* 639 * Name: strip_port 640 * Description: Returns "port" portion of a "hostname:port" string 641 * 642 * Arguments: proxy - full "hostname:port" string pointer 643 * 644 * Returns : the "port" portion of a "hostname:port" string, 645 * converted to a decimal integer, or (int)0 646 * if string contains no :port suffix. 647 */ 648 ushort_t 649 strip_port(char *proxy) 650 { 651 char *tmp_port; 652 653 if ((tmp_port = strpbrk(proxy, ":")) != NULL) 654 return (atoi(tmp_port)); 655 else 656 return (0); 657 } 658 659 /* 660 * Name: set_web_install 661 * Description: Sets flag indicating we are doing a web-based install 662 * 663 * Arguments: none 664 * 665 * Returns : none 666 */ 667 void 668 set_web_install(void) 669 { 670 webpkg_install++; 671 } 672 673 /* 674 * Name: is_web_install 675 * Description: Determines whether we are doing a web-based install 676 * 677 * Arguments: none 678 * 679 * Returns : non-zero if we are doing a web-based install, 0 otherwise 680 */ 681 int 682 is_web_install(void) 683 { 684 return (webpkg_install); 685 } 686 687 /* ~~~~~~~~~~~~~~ Private Functions ~~~~~~~~~~~~~~~~~~~ */ 688 689 /* 690 * Name: web_disconnect 691 * Description: Disconnects connection to web server 692 * 693 * Arguments: none 694 * 695 * Returns : B_TRUE - successful disconnect, B_FALSE otherwise 696 * Temp certificiate files are deleted, 697 * if one was used to initiate the connection 698 * (such as when using SSL) 699 */ 700 static boolean_t 701 web_disconnect(void) 702 { 703 if (ps->certfile) { 704 (void) unlink(ps->certfile); 705 } 706 if (http_srv_disconnect(ps->hps) == 0) 707 if (http_srv_close(ps->hps) == 0) 708 return (B_TRUE); 709 710 return (B_FALSE); 711 } 712 713 /* 714 * Name: check_dwnld_dir 715 * Description: Creates temp download directory 716 * 717 * Arguments: err - where to record any errors. 718 * dwnld_dir - name of directory to create 719 * 720 * Returns : B_TRUE - success, B_FALSE otherwise 721 * on success, directory is created with 722 * safe permissions 723 */ 724 static boolean_t 725 check_dwnld_dir(PKG_ERR *err, char *dwnld_dir) 726 { 727 DIR *dirp; 728 729 /* 730 * Check the directory passed in. If it doesn't exist, create it 731 * with strict permissions 732 */ 733 if ((dirp = opendir(dwnld_dir)) == NULL) { 734 if (mkdir(dwnld_dir, 0744) == -1) { 735 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), 736 dwnld_dir); 737 return (B_FALSE); 738 } 739 } 740 if (dirp) { 741 (void) closedir(dirp); 742 } 743 return (B_TRUE); 744 } 745 746 /* 747 * Name: ds_validate_signature 748 * Description: Validates signature found in a package datastream 749 * 750 * Arguments: err - where to record any errors. 751 * pkgdev - Package context handle of package to verify 752 * pkgs - Null-terminated List of package name to verify 753 * ids_name - Pathname to stream to validate 754 * p7 - PKCS7 signature decoded from stream header 755 * cas - List of trusted CA certificates 756 * proxy - Proxy to use when doing online validation (OCSP) 757 * nointeract - if non-zero, do not output to screen 758 * 759 * Returns : B_TRUE - success, B_FALSE otherwise 760 * success means signature was completely validated, 761 * and contents of stream checked against signature. 762 */ 763 boolean_t 764 ds_validate_signature(PKG_ERR *err, struct pkgdev *pkgdev, char **pkgs, 765 char *ids_name, PKCS7 *p7, STACK_OF(X509) *cas, 766 url_hport_t *proxy, int nointeract) 767 { 768 BIO *p7_bio; 769 boolean_t ret = B_TRUE; 770 771 /* make sure it's a Signed PKCS7 message */ 772 if (!PKCS7_type_is_signed(p7)) { 773 pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_TYPE), 774 ids_name); 775 ret = B_FALSE; 776 goto cleanup; 777 } 778 779 /* initialize PKCS7 object to be filled in */ 780 if (!PKCS7_get_detached(p7)) { 781 pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_DT), 782 ids_name); 783 ret = B_FALSE; 784 goto cleanup; 785 } 786 787 /* dump header and packages into BIO to calculate the message digest */ 788 if ((p7_bio = PKCS7_dataInit(p7, NULL)) == NULL) { 789 pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), 790 ids_name); 791 ret = B_FALSE; 792 goto cleanup; 793 } 794 795 if ((BIO_ds_dump_header(err, p7_bio) != 0) || 796 (BIO_ds_dump(err, ids_name, p7_bio) != 0)) { 797 ret = B_FALSE; 798 goto cleanup; 799 } 800 (void) BIO_flush(p7_bio); 801 802 /* validate the stream and its signature */ 803 if (!validate_signature(err, ids_name, p7_bio, p7, cas, 804 proxy, nointeract)) { 805 ret = B_FALSE; 806 goto cleanup; 807 } 808 809 /* reset device stream (really bad performance for tapes) */ 810 (void) ds_close(1); 811 (void) ds_init(ids_name, pkgs, pkgdev->norewind); 812 813 cleanup: 814 return (ret); 815 } 816 817 818 /* 819 * Name: validate_signature 820 * Description: Validates signature of an arbitrary stream of bits 821 * 822 * Arguments: err - where to record any errors. 823 * name - Descriptive name of object being validated, 824 * for good error reporting messages 825 * indata - BIO object to read stream bits from 826 * p7 - PKCS7 signature of stream 827 * cas - List of trusted CA certificates 828 * proxy - Proxy to use when doing online validation (OCSP) 829 * nointeract - if non-zero, do not output to screen 830 * 831 * Returns : B_TRUE - success, B_FALSE otherwise 832 * success means signature was completely validated, 833 * and contents of stream checked against signature. 834 */ 835 boolean_t 836 validate_signature(PKG_ERR *err, char *name, BIO *indata, PKCS7 *p7, 837 STACK_OF(X509) *cas, url_hport_t *proxy, int nointeract) 838 { 839 STACK_OF(PKCS7_SIGNER_INFO) *sec_sinfos = NULL; 840 841 PKCS7_SIGNER_INFO *signer = NULL; 842 X509_STORE *sec_truststore = NULL; 843 X509_STORE_CTX *ctx = NULL; 844 X509 *signer_cert = NULL, *issuer = NULL; 845 STACK_OF(X509) *chaincerts = NULL; 846 int i, k; 847 unsigned long errcode; 848 const char *err_data = NULL; 849 const char *err_reason = NULL; 850 char *err_string; 851 int err_flags; 852 verify_cb_data_t verify_data; 853 char *signer_sname; 854 char *signer_iname; 855 PKCS7_ISSUER_AND_SERIAL *ias; 856 boolean_t ret = B_TRUE; 857 858 /* only support signed PKCS7 signatures */ 859 if (!PKCS7_type_is_signed(p7)) { 860 PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_WRONG_PKCS7_TYPE); 861 ret = B_FALSE; 862 goto cleanup; 863 } 864 865 /* initialize temporary internal trust store used for verification */ 866 sec_truststore = X509_STORE_new(); 867 868 for (i = 0; i < sk_X509_num(cas); i++) { 869 if (X509_STORE_add_cert(sec_truststore, 870 sk_X509_value(cas, i)) == 0) { 871 pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM)); 872 ret = B_FALSE; 873 goto cleanup; 874 } 875 } 876 877 /* get signers from the signature */ 878 if ((sec_sinfos = PKCS7_get_signer_info(p7)) == NULL) { 879 pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), name); 880 ret = B_FALSE; 881 goto cleanup; 882 } 883 884 /* verify each signer found in the PKCS7 signature */ 885 for (k = 0; k < sk_PKCS7_SIGNER_INFO_num(sec_sinfos); k++) { 886 signer = sk_PKCS7_SIGNER_INFO_value(sec_sinfos, k); 887 signer_cert = PKCS7_cert_from_signer_info(p7, signer); 888 signer_sname = get_subject_display_name(signer_cert); 889 signer_iname = get_issuer_display_name(signer_cert); 890 891 echo_out(nointeract, gettext(MSG_VERIFY), signer_sname); 892 893 /* find the issuer of the current cert */ 894 chaincerts = p7->d.sign->cert; 895 ias = signer->issuer_and_serial; 896 issuer = X509_find_by_issuer_and_serial(chaincerts, 897 ias->issuer, ias->serial); 898 899 /* were we not able to find the issuer cert */ 900 if (issuer == NULL) { 901 pkgerr_add(err, PKGERR_WEB, 902 gettext(ERR_VERIFY_ISSUER), 903 signer_iname, signer_sname); 904 ret = B_FALSE; 905 goto cleanup; 906 } 907 908 /* Lets verify */ 909 if ((ctx = X509_STORE_CTX_new()) == NULL) { 910 pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM)); 911 ret = B_FALSE; 912 goto cleanup; 913 } 914 (void) X509_STORE_CTX_init(ctx, sec_truststore, 915 issuer, chaincerts); 916 (void) X509_STORE_CTX_set_purpose(ctx, 917 X509_PURPOSE_ANY); 918 919 /* callback will perform OCSP on certificates with OCSP data */ 920 X509_STORE_CTX_set_verify_cb(ctx, web_verify); 921 922 /* pass needed data into callback through the app_data handle */ 923 verify_data.proxy = proxy; 924 verify_data.cas = cas; 925 verify_data.err = err; 926 (void) X509_STORE_CTX_set_app_data(ctx, &verify_data); 927 928 /* first verify the certificate chain */ 929 i = X509_verify_cert(ctx); 930 if (i <= 0 && ctx->error != X509_V_ERR_CERT_HAS_EXPIRED) { 931 signer_sname = 932 get_subject_display_name(ctx->current_cert); 933 signer_iname = 934 get_issuer_display_name(ctx->current_cert); 935 /* if the verify context holds an error, print it */ 936 if (ctx->error != X509_V_OK) { 937 pkgerr_add(err, PKGERR_VERIFY, 938 gettext(ERR_VERIFY_SIG), signer_sname, 939 signer_iname, 940 (char *)X509_verify_cert_error_string(ctx->error)); 941 } else { 942 /* some other error. print them all. */ 943 while ((errcode = ERR_get_error_line_data(NULL, 944 NULL, &err_data, &err_flags)) != 0) { 945 size_t errsz; 946 err_reason = 947 ERR_reason_error_string(errcode); 948 if (err_reason == NULL) { 949 err_reason = 950 gettext(ERR_SIG_INT); 951 } 952 953 if (!(err_flags & ERR_TXT_STRING)) { 954 err_data = 955 gettext(ERR_SIG_INT); 956 } 957 errsz = strlen(err_reason) + 958 strlen(err_data) + 3; 959 err_string = xmalloc(errsz); 960 (void) snprintf(err_string, errsz, 961 "%s: %s", err_reason, err_data); 962 pkgerr_add(err, PKGERR_VERIFY, 963 gettext(ERR_VERIFY_SIG), 964 signer_sname, signer_iname, 965 err_string); 966 free(err_string); 967 } 968 } 969 ret = B_FALSE; 970 goto cleanup; 971 } 972 973 /* now verify the signature */ 974 i = PKCS7_signatureVerify(indata, p7, signer, issuer); 975 976 if (i <= 0) { 977 /* print out any OpenSSL-specific errors */ 978 signer_sname = 979 get_subject_display_name(ctx->current_cert); 980 signer_iname = 981 get_subject_display_name(ctx->current_cert); 982 while ((errcode = ERR_get_error_line_data(NULL, 983 NULL, &err_data, &err_flags)) != 0) { 984 err_reason = 985 ERR_reason_error_string(errcode); 986 if (err_reason == NULL) { 987 err_reason = 988 gettext(ERR_SIG_INT); 989 } 990 991 if (!(err_flags & ERR_TXT_STRING)) { 992 err_data = 993 gettext(ERR_SIG_INT); 994 } 995 pkgerr_add(err, PKGERR_VERIFY, 996 gettext(ERR_VERIFY_SIG), signer_sname, 997 signer_iname, err_reason); 998 pkgerr_add(err, PKGERR_VERIFY, 999 gettext(ERR_VERIFY_SIG), signer_sname, 1000 signer_iname, err_data); 1001 } 1002 ret = B_FALSE; 1003 goto cleanup; 1004 } 1005 1006 echo_out(nointeract, gettext(MSG_VERIFY_OK), signer_sname); 1007 } 1008 1009 /* signature(s) verified successfully */ 1010 cleanup: 1011 if (ctx) 1012 X509_STORE_CTX_cleanup(ctx); 1013 return (ret); 1014 } 1015 1016 /* 1017 * Name: web_verify 1018 * Description: Callback used by PKCS7_dataVerify when 1019 * verifying a certificate chain. 1020 * 1021 * Arguments: err - where to record any errors. 1022 * ctx - The context handle of the current verification operation 1023 * 1024 * Returns : B_TRUE - success, B_FALSE otherwise 1025 * if it's '0' (not OK) we simply return it, since the 1026 * verification operation has already determined that the 1027 * cert is invalid. if 'ok' is non-zero, then we do our 1028 * checks, and return 0 or 1 based on if the cert is 1029 * invalid or valid. 1030 */ 1031 static int 1032 web_verify(int ok, X509_STORE_CTX *ctx) 1033 { 1034 X509 *curr_cert; 1035 X509 *curr_issuer; 1036 char *uri; 1037 url_hport_t *proxy; 1038 PKG_ERR *err = NULL; 1039 STACK_OF(X509) *cas; 1040 if (!ok) { 1041 /* don't override a verify failure */ 1042 return (ok); 1043 } 1044 1045 1046 /* get app data supplied through callback context */ 1047 err = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->err; 1048 proxy = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->proxy; 1049 cas = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->cas; 1050 1051 /* Check revocation status */ 1052 curr_cert = X509_STORE_CTX_get_current_cert(ctx); 1053 1054 /* this shouldn't happen */ 1055 if (curr_cert == NULL) { 1056 pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL), 1057 __FILE__, __LINE__); 1058 return (0); 1059 } 1060 1061 /* don't perform OCSP unless cert has required OCSP extensions */ 1062 if (get_ocsp_uri(curr_cert, &uri)) { 1063 if (get_issuer(&curr_issuer, ctx, curr_cert) <= 0) { 1064 /* no issuer! */ 1065 pkgerr_add(err, PKGERR_INTERNAL, 1066 gettext(ERR_PKG_INTERNAL), 1067 __FILE__, __LINE__); 1068 return (0); 1069 } 1070 1071 /* 1072 * ok we have the current cert 1073 * and its issuer. Do the OCSP check 1074 */ 1075 1076 /* 1077 * OCSP extensions are, by, RFC 2459, never critical 1078 * extensions, therefore, we only fail if we were able 1079 * to explicitly contact an OCSP responder, and that 1080 * responder did not indicate the cert was valid. We 1081 * also fail if user-supplied data could not be parsed 1082 * or we run out of memory. We succeeed for "soft" 1083 * failures, such as not being able to connect to the 1084 * OCSP responder, or trying to use if the OCSP URI 1085 * indicates SSL must be used (which we do not 1086 * support) 1087 */ 1088 switch (ocsp_verify(err, curr_cert, curr_issuer, 1089 uri, proxy, cas)) { 1090 case OCSPMem: /* Ran out of memory */ 1091 case OCSPInternal: /* Some internal error */ 1092 case OCSPVerify: /* OCSP responder indicated fail */ 1093 return (0); 1094 } 1095 /* all other cases are success, or soft failures */ 1096 pkgerr_clear(err); 1097 } 1098 1099 return (ok); 1100 } 1101 1102 /* 1103 * Name: get_time_string 1104 * Description: Generates a human-readable string from an ASN1_GENERALIZED_TIME 1105 * 1106 * Arguments: intime - The time to convert 1107 * 1108 * Returns : A pointer to a static string representing the passed-in time. 1109 */ 1110 static char 1111 *get_time_string(ASN1_GENERALIZEDTIME *intime) 1112 { 1113 1114 static char time[ATTR_MAX]; 1115 BIO *mem; 1116 char *p; 1117 1118 if (intime == NULL) { 1119 return (NULL); 1120 } 1121 if ((mem = BIO_new(BIO_s_mem())) == NULL) { 1122 return (NULL); 1123 } 1124 1125 if (ASN1_GENERALIZEDTIME_print(mem, intime) == 0) { 1126 (void) BIO_free(mem); 1127 return (NULL); 1128 } 1129 1130 if (BIO_gets(mem, time, ATTR_MAX) <= 0) { 1131 (void) BIO_free(mem); 1132 return (NULL); 1133 } 1134 1135 (void) BIO_free(mem); 1136 1137 /* trim the end of the string */ 1138 for (p = time + strlen(time) - 1; isspace(*p); p--) { 1139 *p = '\0'; 1140 } 1141 1142 return (time); 1143 } 1144 1145 /* 1146 * Name: get_ocsp_uri 1147 * Description: Examines an X509 certificate and retrieves the embedded 1148 * OCSP Responder URI if one exists. 1149 * 1150 * Arguments: cert - The cert to inspect 1151 * uri - pointer where the newly-allocated URI is placed, if found 1152 * 1153 * Returns : Success if the URI was found. Appropriate status otherwise. 1154 */ 1155 static boolean_t 1156 get_ocsp_uri(X509 *cert, char **uri) 1157 { 1158 AUTHORITY_INFO_ACCESS *aia; 1159 ACCESS_DESCRIPTION *ad; 1160 int i; 1161 1162 if (getenv("PKGWEB_TEST_OCSP")) { 1163 *uri = xstrdup(getenv("PKGWEB_TEST_OCSP")); 1164 return (B_TRUE); 1165 } 1166 1167 /* get the X509v3 extension holding the OCSP URI */ 1168 if ((aia = X509_get_ext_d2i(cert, NID_info_access, 1169 NULL, NULL)) != NULL) { 1170 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) { 1171 ad = sk_ACCESS_DESCRIPTION_value(aia, i); 1172 if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) { 1173 if (ad->location->type == GEN_URI) { 1174 *uri = 1175 xstrdup((char *)ASN1_STRING_data(ad->location->d.ia5)); 1176 return (B_TRUE); 1177 } 1178 } 1179 } 1180 } 1181 1182 /* no URI was found */ 1183 return (B_FALSE); 1184 } 1185 1186 /* 1187 * Name: ocsp_verify 1188 * Description: Attempts to contact an OCSP Responder and ascertain the validity 1189 * of an X509 certificate. 1190 * 1191 * Arguments: err - Error object to add error messages to 1192 * cert - The cert to validate 1193 * issuer - The certificate of the issuer of 'cert' 1194 * uri - The OCSP Responder URI 1195 * cas - The trusted CA certificates used to verify the 1196 * signed OCSP response 1197 * Returns : Success - The OCSP Responder reported a 'good' 1198 * status for the cert otherwise, appropriate 1199 * error is returned. 1200 */ 1201 static OCSPStatus 1202 ocsp_verify(PKG_ERR *err, X509 *cert, X509 *issuer, 1203 char *uri, url_hport_t *proxy, STACK_OF(X509) *cas) 1204 { 1205 OCSP_CERTID *id; 1206 OCSP_REQUEST *req; 1207 OCSP_RESPONSE *resp; 1208 OCSP_BASICRESP *bs; 1209 BIO *cbio, *mem; 1210 char ocspbuf[OCSP_BUFSIZ]; 1211 char *host = NULL, *portstr = NULL, *path = "/", *p, *q, *r; 1212 int port, status, reason; 1213 int len, retval, respcode, use_ssl = 0; 1214 ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; 1215 char *subjname; 1216 time_t currtime; 1217 char currtimestr[ATTR_MAX]; 1218 unsigned long errcode; 1219 const char *err_reason; 1220 1221 subjname = get_subject_display_name(cert); 1222 1223 /* parse the URI into its constituent parts */ 1224 if (OCSP_parse_url(uri, &host, &portstr, &path, &use_ssl) == NULL) { 1225 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_PARSE), uri); 1226 return (OCSPParse); 1227 } 1228 1229 /* we don't currently support SSL-based OCSP Responders */ 1230 if (use_ssl) { 1231 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_UNSUP), uri); 1232 return (OCSPUnsupported); 1233 } 1234 1235 /* default port if none specified */ 1236 if (portstr == NULL) { 1237 port = (int)URL_DFLT_SRVR_PORT; 1238 } else { 1239 port = (int)strtoul(portstr, &r, 10); 1240 if (*r != '\0') { 1241 pkgerr_add(err, PKGERR_PARSE, 1242 gettext(ERR_OCSP_PARSE), uri); 1243 return (OCSPParse); 1244 } 1245 } 1246 1247 /* allocate new request structure */ 1248 if ((req = OCSP_REQUEST_new()) == NULL) { 1249 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM)); 1250 return (OCSPMem); 1251 } 1252 1253 /* convert cert and issuer fields into OCSP request data */ 1254 if ((id = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) { 1255 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL), 1256 __FILE__, __LINE__); 1257 return (OCSPInternal); 1258 } 1259 1260 /* fill out request structure with request data */ 1261 if ((OCSP_request_add0_id(req, id)) == NULL) { 1262 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL), 1263 __FILE__, __LINE__); 1264 return (OCSPInternal); 1265 } 1266 1267 /* add nonce */ 1268 (void) OCSP_request_add1_nonce(req, NULL, -1); 1269 1270 /* connect to host, or proxy */ 1271 if (proxy != NULL) { 1272 if ((cbio = BIO_new_connect(proxy->hostname)) == NULL) { 1273 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM)); 1274 return (OCSPMem); 1275 } 1276 1277 /* 1278 * BIO_set_conn_int_port takes an int *, so let's give it one 1279 * rather than an ushort_t * 1280 */ 1281 port = proxy->port; 1282 (void) BIO_set_conn_int_port(cbio, &port); 1283 if (BIO_do_connect(cbio) <= 0) { 1284 pkgerr_add(err, PKGERR_PARSE, 1285 gettext(ERR_OCSP_CONNECT), 1286 proxy->hostname, port); 1287 return (OCSPConnect); 1288 } 1289 } else { 1290 if ((cbio = BIO_new_connect(host)) == NULL) { 1291 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM)); 1292 return (OCSPMem); 1293 } 1294 1295 (void) BIO_set_conn_int_port(cbio, &port); 1296 if (BIO_do_connect(cbio) <= 0) { 1297 pkgerr_add(err, PKGERR_PARSE, 1298 gettext(ERR_OCSP_CONNECT), 1299 host, port); 1300 return (OCSPConnect); 1301 } 1302 } 1303 1304 /* calculate length of binary request data */ 1305 len = i2d_OCSP_REQUEST(req, NULL); 1306 1307 /* send the request headers */ 1308 if (proxy != NULL) { 1309 retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, uri, len); 1310 } else { 1311 retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, path, len); 1312 } 1313 1314 if (retval <= 0) { 1315 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host); 1316 return (OCSPRequest); 1317 } 1318 1319 /* send the request binary data */ 1320 if (i2d_OCSP_REQUEST_bio(cbio, req) <= 0) { 1321 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host); 1322 return (OCSPRequest); 1323 } 1324 1325 /* 1326 * read the response into a memory BIO, so we can 'gets' 1327 * (socket bio's don't support BIO_gets) 1328 */ 1329 if ((mem = BIO_new(BIO_s_mem())) == NULL) { 1330 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM)); 1331 return (OCSPMem); 1332 } 1333 1334 while ((len = BIO_read(cbio, ocspbuf, OCSP_BUFSIZ))) { 1335 if (len < 0) { 1336 pkgerr_add(err, PKGERR_PARSE, 1337 gettext(ERR_OCSP_READ), host); 1338 return (OCSPRequest); 1339 } 1340 if (BIO_write(mem, ocspbuf, len) != len) { 1341 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM)); 1342 return (OCSPMem); 1343 } 1344 } 1345 1346 /* now get the first line of the response */ 1347 if (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) <= 0) { 1348 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_PARSE)); 1349 return (OCSPRequest); 1350 } 1351 1352 /* parse the header response */ 1353 /* it should look like "HTTP/x.x 200 OK" */ 1354 1355 /* skip past the protocol info */ 1356 for (p = ocspbuf; (*p != '\0') && !isspace(*p); p++) 1357 continue; 1358 1359 /* skip past whitespace betwen protocol and start of response code */ 1360 while ((*p != '\0') && isspace(*p)) { 1361 p++; 1362 } 1363 1364 if (*p == '\0') { 1365 /* premature end */ 1366 pkgerr_add(err, PKGERR_PARSE, 1367 gettext(ERR_OCSP_RESP_PARSE), ocspbuf); 1368 return (OCSPRequest); 1369 } 1370 1371 /* find end of response code */ 1372 for (q = p; (*q != NULL) && !isspace(*q); q++) 1373 continue; 1374 1375 /* mark end of response code */ 1376 *q++ = '\0'; 1377 1378 /* parse response code */ 1379 respcode = strtoul(p, &r, 10); 1380 if (*r != '\0') { 1381 pkgerr_add(err, PKGERR_PARSE, 1382 gettext(ERR_OCSP_RESP_PARSE), ocspbuf); 1383 return (OCSPRequest); 1384 } 1385 1386 /* now find beginning of the response string */ 1387 while ((*q != NULL) && isspace(*q)) { 1388 q++; 1389 } 1390 1391 /* trim whitespace from end of message */ 1392 for (r = (q + strlen(q) - 1); isspace(*r); r--) { 1393 *r = '\0'; 1394 } 1395 1396 /* response must be OK */ 1397 if (respcode != 200) { 1398 pkgerr_add(err, PKGERR_PARSE, 1399 gettext(ERR_OCSP_RESP_NOTOK), 200, 1400 respcode, q); 1401 return (OCSPRequest); 1402 } 1403 1404 /* read headers, looking for content-type or a blank line */ 1405 while (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) > 0) { 1406 1407 /* if we get a content type, make sure it's the right type */ 1408 if (ci_strneq(ocspbuf, CONTENT_TYPE_HDR, 1409 strlen(CONTENT_TYPE_HDR))) { 1410 1411 /* look for the delimiting : */ 1412 p = strchr(ocspbuf + strlen(CONTENT_TYPE_HDR), ':'); 1413 1414 if (p == NULL) { 1415 pkgerr_add(err, PKGERR_PARSE, 1416 gettext(ERR_OCSP_RESP_PARSE), ocspbuf); 1417 return (OCSPResponder); 1418 } 1419 1420 /* skip over ':' */ 1421 p++; 1422 1423 /* find beginning of the content type */ 1424 while ((*p != NULL) && isspace(*p)) { 1425 p++; 1426 } 1427 1428 if (!ci_strneq(p, CONTENT_OCSP_RESP, 1429 strlen(CONTENT_OCSP_RESP))) { 1430 /* response is not right type */ 1431 pkgerr_add(err, PKGERR_PARSE, 1432 gettext(ERR_OCSP_RESP_TYPE), 1433 p, CONTENT_OCSP_RESP); 1434 return (OCSPResponder); 1435 } 1436 1437 /* continue with next header line */ 1438 continue; 1439 } 1440 1441 /* scan looking for a character */ 1442 for (p = ocspbuf; (*p != '\0') && isspace(*p); p++) { 1443 continue; 1444 } 1445 /* 1446 * if we got to the end of the line with 1447 * no chars, then this is a blank line 1448 */ 1449 if (*p == '\0') { 1450 break; 1451 } 1452 } 1453 1454 1455 if (*p != '\0') { 1456 /* last line was not blank */ 1457 pkgerr_add(err, PKGERR_PARSE, 1458 gettext(ERR_OCSP_RESP_PARSE), ocspbuf); 1459 return (OCSPResponder); 1460 } 1461 1462 /* now read in the binary response */ 1463 if ((resp = d2i_OCSP_RESPONSE_bio(mem, NULL)) == NULL) { 1464 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host); 1465 return (OCSPResponder); 1466 } 1467 1468 /* free temp BIOs */ 1469 (void) BIO_free(mem); 1470 (void) BIO_free_all(cbio); 1471 cbio = NULL; 1472 1473 /* make sure request was successful */ 1474 if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) { 1475 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_NOTOK), 1476 OCSP_RESPONSE_STATUS_SUCCESSFUL, 1477 OCSP_response_status(resp), 1478 OCSP_response_status_str(OCSP_response_status(resp))); 1479 return (OCSPResponder); 1480 } 1481 1482 /* parse binary response into internal structure */ 1483 if ((bs = OCSP_response_get1_basic(resp)) == NULL) { 1484 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host); 1485 return (OCSPParse); 1486 } 1487 1488 /* 1489 * From here to the end of the code, the return values 1490 * should be hard failures 1491 */ 1492 1493 /* verify the response, warn if no nonce */ 1494 if (OCSP_check_nonce(req, bs) <= 0) { 1495 logerr(pkg_gt(WRN_OCSP_RESP_NONCE)); 1496 } 1497 1498 if (OCSP_basic_verify(bs, cas, NULL, OCSP_TRUSTOTHER) <= 0) { 1499 while ((errcode = ERR_get_error()) != NULL) { 1500 err_reason = ERR_reason_error_string(errcode); 1501 if (err_reason == NULL) { 1502 err_reason = 1503 gettext(ERR_SIG_INT); 1504 } 1505 pkgerr_add(err, PKGERR_PARSE, (char *)err_reason); 1506 } 1507 pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_VERIFY_FAIL), 1508 uri); 1509 return (OCSPVerify); 1510 } 1511 1512 /* check the validity of our certificate */ 1513 if (OCSP_resp_find_status(bs, id, &status, &reason, 1514 &rev, &thisupd, &nextupd) == NULL) { 1515 pkgerr_add(err, PKGERR_PARSE, 1516 gettext(ERR_OCSP_VERIFY_NO_STATUS), subjname); 1517 return (OCSPVerify); 1518 } 1519 1520 if ((currtime = time(NULL)) == (time_t)-1) { 1521 pkgerr_add(err, PKGERR_PARSE, 1522 gettext(ERR_OCSP_VERIFY_NOTIME)); 1523 return (OCSPVerify); 1524 } 1525 1526 (void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX); 1527 1528 /* trim end */ 1529 for (r = currtimestr + strlen(currtimestr) - 1; 1530 isspace(*r); r--) { 1531 *r = '\0'; 1532 } 1533 1534 if (!OCSP_check_validity(thisupd, nextupd, 1535 OCSP_VALIDITY_PERIOD, -1)) { 1536 if (nextupd != NULL) { 1537 pkgerr_add(err, PKGERR_PARSE, 1538 gettext(ERR_OCSP_VERIFY_VALIDITY), 1539 get_time_string(thisupd), get_time_string(nextupd), 1540 currtimestr); 1541 } else { 1542 pkgerr_add(err, PKGERR_PARSE, 1543 gettext(ERR_OCSP_VERIFY_VALIDITY), 1544 get_time_string(thisupd), 1545 currtimestr); 1546 } 1547 return (OCSPVerify); 1548 } 1549 1550 if (status != V_OCSP_CERTSTATUS_GOOD) { 1551 pkgerr_add(err, PKGERR_PARSE, 1552 gettext(ERR_OCSP_VERIFY_STATUS), subjname, 1553 OCSP_cert_status_str(status)); 1554 return (OCSPVerify); 1555 } 1556 1557 /* everythign checks out */ 1558 return (OCSPSuccess); 1559 } 1560 1561 /* 1562 * Name: get_issuer 1563 * Description: Attempts to find the issuing certificate for a given certificate 1564 * This will look in both the list of trusted certificates found in 1565 * the X509_STORE_CTX structure, as well as the list of untrusted 1566 * chain certificates found in the X509_STORE_CTX structure. 1567 * Arguments: 1568 * issuer - The resulting issuer cert is placed here, if found 1569 * ctx - The current verification context 1570 * x - The certificate whose issuer we are looking for 1571 * Returns : Success - The issuer cert was found and placed in *issuer. 1572 * otherwise, appropriate error is returned. 1573 */ 1574 static int 1575 get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) 1576 { 1577 int i, ok; 1578 1579 /* 1580 * first look in the list of trusted 1581 * certs, using the context's method to do so 1582 */ 1583 if ((ok = ctx->get_issuer(issuer, ctx, x)) > 0) { 1584 return (ok); 1585 } 1586 1587 if (ctx->untrusted != NULL) { 1588 /* didn't find it in trusted certs, look through untrusted */ 1589 for (i = 0; i < sk_X509_num(ctx->untrusted); i++) { 1590 if (X509_check_issued(sk_X509_value(ctx->untrusted, i), 1591 x) == X509_V_OK) { 1592 *issuer = sk_X509_value(ctx->untrusted, i); 1593 return (1); 1594 } 1595 } 1596 } 1597 *issuer = NULL; 1598 return (0); 1599 } 1600 1601 /* 1602 * Name: parse_url_proxy 1603 * Description: Parses URL and optional proxy specification, populates static 1604 * 'ps' structure 1605 * 1606 * Arguments: err - where to record any errors. 1607 * url - URL to parse 1608 * proxy - proxy to parse, or NULL for no proxy 1609 * proxy_port - Default proxy port to use if no proxy 1610 * port specified in 'proxy' 1611 * 1612 * Returns : B_TRUE - success, B_FALSE otherwise 1613 * on success, 'ps->url' and 'ps->proxy' are populated 1614 * with parsed data. 1615 */ 1616 static boolean_t 1617 parse_url_proxy(PKG_ERR *err, char *url, char *proxy, ushort_t proxy_port) 1618 { 1619 boolean_t ret = B_TRUE; 1620 if (!path_valid(url)) { 1621 ret = B_FALSE; 1622 goto cleanup; 1623 } 1624 1625 if (url_parse(url, &ps->url) != URL_PARSE_SUCCESS) { 1626 pkgerr_add(err, PKGERR_WEB, gettext(ERR_PARSE_URL), url); 1627 ret = B_FALSE; 1628 goto cleanup; 1629 } 1630 1631 if (proxy != NULL) { 1632 if (url_parse_hostport(proxy, &ps->proxy, proxy_port) 1633 != URL_PARSE_SUCCESS) { 1634 pkgerr_add(err, PKGERR_WEB, 1635 gettext(ERR_BAD_PROXY), proxy); 1636 ret = B_FALSE; 1637 goto cleanup; 1638 } 1639 } 1640 1641 cleanup: 1642 return (ret); 1643 } 1644 1645 /* 1646 * Name: web_setup 1647 * Description: Initializes http library settings 1648 * 1649 * Arguments: err - where to record any errors. 1650 * 1651 * Returns : B_TRUE - success, B_FALSE otherwise 1652 */ 1653 static boolean_t 1654 web_setup(PKG_ERR *err) 1655 { 1656 boolean_t ret = B_TRUE; 1657 static boolean_t keepalive = B_TRUE; 1658 1659 if ((ps->hps = http_srv_init(&ps->url)) == NULL) { 1660 pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url); 1661 ret = B_FALSE; 1662 goto cleanup; 1663 } 1664 1665 if (getenv("WEBPKG_DEBUG") != NULL) { 1666 http_set_verbose(B_TRUE); 1667 } 1668 1669 if (ps->proxy.hostname[0] != '\0' && 1670 http_set_proxy(ps->hps, &ps->proxy) != 0) { 1671 pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url); 1672 ret = B_FALSE; 1673 goto cleanup; 1674 } 1675 if (http_set_keepalive(ps->hps, keepalive) != 0) { 1676 pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url); 1677 ret = B_FALSE; 1678 goto cleanup; 1679 } 1680 if (http_set_socket_read_timeout(ps->hps, ps->timeout) != 0) { 1681 pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url); 1682 ret = B_FALSE; 1683 goto cleanup; 1684 } 1685 if (http_set_random_file(ps->hps, RANDOM) != 0) { 1686 pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url); 1687 ret = B_FALSE; 1688 goto cleanup; 1689 } 1690 1691 (void) http_set_p12_format(B_TRUE); 1692 1693 cleanup: 1694 return (ret); 1695 } 1696 1697 /* 1698 * Name: web_connect 1699 * Description: Makes connection with URL stored in static 'ps' structure. 1700 * 1701 * Arguments: err - where to record any errors. 1702 * 1703 * Returns : WEB_OK - connection successful 1704 * WEB_VERIFY_SETUP - Unable to complete necessary 1705 * SSL setup 1706 * WEB_CONNREFUSED - Connection was refused to web site 1707 * WEB_HOSTDOWN - Host was not responding to request 1708 * WEB_NOCONNECT - Some other connection failure 1709 */ 1710 static WebStatus 1711 web_connect(PKG_ERR *err) 1712 { 1713 STACK_OF(X509) *sec_cas = NULL; 1714 char *path; 1715 WebStatus ret = WEB_OK; 1716 ulong_t errcode; 1717 uint_t errsrc; 1718 int my_errno = 0; 1719 const char *libhttperr = NULL; 1720 1721 if (ps->url.https == B_TRUE) { 1722 /* get CA certificates */ 1723 if (find_ca_certs(err, ps->keystore, &sec_cas) != 0) { 1724 ret = WEB_VERIFY_SETUP; 1725 goto cleanup; 1726 } 1727 1728 if (sk_X509_num(sec_cas) < 1) { 1729 /* no trusted websites */ 1730 pkgerr_add(err, PKGERR_WEB, 1731 gettext(ERR_KEYSTORE_NOTRUST)); 1732 ret = WEB_VERIFY_SETUP; 1733 goto cleanup; 1734 } 1735 1736 /* 1737 * write out all CA certs to temp file. libwanboot should 1738 * have an interface for giving it a list of trusted certs 1739 * through an in-memory structure, but currently that does 1740 * not exist 1741 */ 1742 if ((path = write_ca_file(err, ps->dwnld_dir, sec_cas, 1743 WEB_CA_PHRASE)) == NULL) { 1744 ret = WEB_VERIFY_SETUP; 1745 goto cleanup; 1746 } 1747 1748 ps->certfile = path; 1749 if (http_set_password(ps->hps, WEB_CA_PHRASE) != 0) { 1750 pkgerr_add(err, PKGERR_WEB, 1751 gettext(ERR_HTTPS_PASSWD)); 1752 ret = WEB_VERIFY_SETUP; 1753 goto cleanup; 1754 } 1755 1756 if (http_set_certificate_authority_file(path) != 0) { 1757 pkgerr_add(err, PKGERR_WEB, 1758 gettext(ERR_HTTPS_CA)); 1759 ret = WEB_VERIFY_SETUP; 1760 goto cleanup; 1761 } 1762 } 1763 1764 if (http_srv_connect(ps->hps) != 0) { 1765 while ((errcode = http_get_lasterr(ps->hps, &errsrc)) != 0) { 1766 /* Have an error - is it EINTR? */ 1767 if (errsrc == ERRSRC_SYSTEM) { 1768 my_errno = errcode; 1769 break; 1770 } else if (libhttperr == NULL) { 1771 /* save the first non-system error message */ 1772 libhttperr = http_errorstr(errsrc, errcode); 1773 } 1774 } 1775 switch (my_errno) { 1776 case EINTR: 1777 case ETIMEDOUT: 1778 /* Timed out. Try, try again */ 1779 ret = WEB_TIMEOUT; 1780 break; 1781 case ECONNREFUSED: 1782 ret = WEB_CONNREFUSED; 1783 break; 1784 case EHOSTDOWN: 1785 ret = WEB_HOSTDOWN; 1786 break; 1787 default: 1788 /* some other fatal error */ 1789 ret = WEB_NOCONNECT; 1790 if (libhttperr == NULL) { 1791 pkgerr_add(err, PKGERR_WEB, 1792 gettext(ERR_INIT_CONN), 1793 ps->url.hport.hostname); 1794 } else { 1795 pkgerr_add(err, PKGERR_WEB, 1796 gettext(ERR_HTTP), libhttperr); 1797 } 1798 break; 1799 } 1800 } 1801 cleanup: 1802 return (ret); 1803 } 1804 1805 /* 1806 * Name: write_ca_file 1807 * Description: Writes out a PKCS12 file containing all trusted certs 1808 * found in keystore recorded in static 'ps' structure 1809 * 1810 * This routine is used because the libwanboot library's 1811 * HTTPS routines cannot accept trusted certificates 1812 * through an in-memory structure, when initiating an 1813 * SSL connection. They must be in a PKCS12, which is 1814 * admittedly a poor interface. 1815 * 1816 * Arguments: err - where to record any errors. 1817 * tmpdir - Directory to write certificate file in 1818 * cacerts - Certs to write out 1819 * passwd - password used to encrypt certs 1820 * 1821 * Returns : path to resulting file, if successfullly written, 1822 * otherwise NULL. 1823 */ 1824 static char 1825 *write_ca_file(PKG_ERR *err, char *tmpdir, STACK_OF(X509) *cacerts, 1826 char *passwd) 1827 { 1828 int fd, len; 1829 FILE *fp; 1830 PKCS12 *p12 = NULL; 1831 char *ret = NULL; 1832 static char tmp_file[PATH_MAX] = ""; 1833 struct stat buf; 1834 1835 if (!path_valid(tmpdir)) { 1836 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir); 1837 goto cleanup; 1838 } 1839 1840 /* mkstemp replaces XXXXXX with a unique string */ 1841 if (((len = snprintf(tmp_file, PATH_MAX, "%s/%sXXXXXX", tmpdir, 1842 "cert")) < 0) || 1843 (len >= PATH_MAX)) { 1844 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir); 1845 goto cleanup; 1846 } 1847 1848 if ((fd = mkstemp(tmp_file)) == -1) { 1849 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file); 1850 goto cleanup; 1851 } 1852 1853 if (fstat(fd, &buf) == -1) { 1854 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file); 1855 goto cleanup; 1856 } 1857 1858 if (!S_ISREG(buf.st_mode)) { 1859 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file); 1860 goto cleanup; 1861 } 1862 1863 if ((fp = fdopen(fd, "w")) == NULL) { 1864 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file); 1865 goto cleanup; 1866 } 1867 1868 if ((p12 = sunw_PKCS12_create(passwd, NULL, NULL, cacerts)) == NULL) { 1869 pkgerr_add(err, PKGERR_WEB, 1870 gettext(ERR_KEYSTORE_FORM), tmp_file); 1871 goto cleanup; 1872 } 1873 1874 if (i2d_PKCS12_fp(fp, p12) == 0) { 1875 pkgerr_add(err, PKGERR_WEB, 1876 gettext(ERR_KEYSTORE_FORM), tmp_file); 1877 goto cleanup; 1878 } 1879 1880 (void) fflush(fp); 1881 (void) fclose(fp); 1882 (void) close(fd); 1883 fp = NULL; 1884 fd = -1; 1885 ret = tmp_file; 1886 1887 cleanup: 1888 if (p12 != NULL) 1889 PKCS12_free(p12); 1890 if (fp != NULL) 1891 (void) fclose(fp); 1892 if (fd != -1) { 1893 (void) close(fd); 1894 (void) unlink(tmp_file); 1895 } 1896 1897 return (ret); 1898 } 1899 1900 /* 1901 * Name: web_send_request 1902 * Description: Sends an HTTP request for a file to the 1903 * web server being communicated with in the static 1904 * 'ps' structure 1905 * 1906 * Arguments: err - where to record any errors. 1907 * request_type - HTTP_REQ_TYPE_HEAD to send an HTTP HEAD request, 1908 * or HTTP_REQ_TYPE_GET to send an HTTP GET request 1909 * cp - 1910 * Returns : WEB_OK - request sent successfully 1911 * WEB_CONNREFUSED - Connection was refused to web site 1912 * WEB_HOSTDOWN - Host was not responding to request 1913 * WEB_NOCONNECT - Some other connection failure 1914 */ 1915 static WebStatus 1916 web_send_request(PKG_ERR *err, int request_type, int cp, int ep) 1917 { 1918 WebStatus ret = WEB_OK; 1919 ulong_t errcode; 1920 uint_t errsrc; 1921 int my_errno = 0; 1922 const char *libhttperr = NULL; 1923 switch (request_type) { 1924 case HTTP_REQ_TYPE_HEAD: 1925 if ((http_head_request(ps->hps, ps->url.abspath)) != 0) { 1926 while ((errcode = http_get_lasterr(ps->hps, 1927 &errsrc)) != 0) { 1928 /* Have an error - is it EINTR? */ 1929 if (errsrc == ERRSRC_SYSTEM) { 1930 my_errno = errcode; 1931 break; 1932 } else if (libhttperr == NULL) { 1933 /* save first non-system error message */ 1934 libhttperr = 1935 http_errorstr(errsrc, errcode); 1936 } 1937 } 1938 switch (my_errno) { 1939 case EINTR: 1940 case ETIMEDOUT: 1941 /* Timed out. Try, try again */ 1942 ret = WEB_TIMEOUT; 1943 break; 1944 case ECONNREFUSED: 1945 ret = WEB_CONNREFUSED; 1946 break; 1947 case EHOSTDOWN: 1948 ret = WEB_HOSTDOWN; 1949 break; 1950 default: 1951 /* some other fatal error */ 1952 ret = WEB_NOCONNECT; 1953 if (libhttperr == NULL) { 1954 pkgerr_add(err, PKGERR_WEB, 1955 gettext(ERR_INIT_CONN), 1956 ps->url.hport.hostname); 1957 } else { 1958 pkgerr_add(err, PKGERR_WEB, 1959 gettext(ERR_HTTP), libhttperr); 1960 } 1961 break; 1962 } 1963 goto cleanup; 1964 } 1965 break; 1966 1967 case HTTP_REQ_TYPE_GET: 1968 if (cp && ep) { 1969 if (http_get_range_request(ps->hps, ps->url.abspath, 1970 cp, ep - cp) != 0) { 1971 while ((errcode = http_get_lasterr(ps->hps, 1972 &errsrc)) != 0) { 1973 /* Have an error - is it EINTR? */ 1974 if (errsrc == ERRSRC_SYSTEM) { 1975 my_errno = errcode; 1976 break; 1977 } else { 1978 /* 1979 * save first non-system 1980 * error message 1981 */ 1982 libhttperr = 1983 http_errorstr(errsrc, 1984 errcode); 1985 } 1986 } 1987 switch (my_errno) { 1988 case EINTR: 1989 case ETIMEDOUT: 1990 /* Timed out. Try, try again */ 1991 ret = WEB_TIMEOUT; 1992 break; 1993 case ECONNREFUSED: 1994 ret = WEB_CONNREFUSED; 1995 break; 1996 case EHOSTDOWN: 1997 ret = WEB_HOSTDOWN; 1998 break; 1999 default: 2000 /* some other fatal error */ 2001 ret = WEB_NOCONNECT; 2002 if (libhttperr == NULL) { 2003 pkgerr_add(err, PKGERR_WEB, 2004 gettext(ERR_INIT_CONN), 2005 ps->url.hport.hostname); 2006 } else { 2007 pkgerr_add(err, PKGERR_WEB, 2008 gettext(ERR_HTTP), 2009 libhttperr); 2010 } 2011 break; 2012 } 2013 goto cleanup; 2014 } 2015 2016 if (!web_eval_headers(err)) { 2017 ret = WEB_NOCONNECT; 2018 goto cleanup; 2019 } 2020 } else { 2021 if ((http_get_request(ps->hps, ps->url.abspath)) 2022 != 0) { 2023 while ((errcode = http_get_lasterr(ps->hps, 2024 &errsrc)) != 0) { 2025 /* Have an error - is it EINTR? */ 2026 if (errsrc == ERRSRC_SYSTEM) { 2027 my_errno = errcode; 2028 break; 2029 } else { 2030 /* 2031 * save the first non-system 2032 * error message 2033 */ 2034 libhttperr = 2035 http_errorstr(errsrc, 2036 errcode); 2037 } 2038 } 2039 switch (my_errno) { 2040 case EINTR: 2041 case ETIMEDOUT: 2042 /* Timed out. Try, try again */ 2043 ret = WEB_TIMEOUT; 2044 break; 2045 case ECONNREFUSED: 2046 ret = WEB_CONNREFUSED; 2047 break; 2048 case EHOSTDOWN: 2049 ret = WEB_HOSTDOWN; 2050 break; 2051 default: 2052 /* some other fatal error */ 2053 ret = WEB_NOCONNECT; 2054 if (libhttperr == NULL) { 2055 pkgerr_add(err, PKGERR_WEB, 2056 gettext(ERR_INIT_CONN), 2057 ps->url.hport.hostname); 2058 } else { 2059 pkgerr_add(err, PKGERR_WEB, 2060 gettext(ERR_HTTP), 2061 libhttperr); 2062 } 2063 break; 2064 } 2065 goto cleanup; 2066 } 2067 2068 if (!web_eval_headers(err)) { 2069 ret = WEB_NOCONNECT; 2070 goto cleanup; 2071 } 2072 } 2073 break; 2074 default: 2075 pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL), 2076 __FILE__, __LINE__); 2077 } 2078 2079 cleanup: 2080 return (ret); 2081 } 2082 2083 /* 2084 * Name: web_eval_headers 2085 * Description: Evaluates HTTP headers returned during an HTTP request. 2086 * This must be called before calling 2087 * http_get_header_value(). 2088 * 2089 * Arguments: err - where to record any errors. 2090 * 2091 * Returns : B_TRUE - success, B_FALSE otherwise 2092 */ 2093 static boolean_t 2094 web_eval_headers(PKG_ERR *err) 2095 { 2096 const char *http_err; 2097 ulong_t herr; 2098 uint_t errsrc; 2099 2100 if (http_process_headers(ps->hps, &ps->resp) != 0) { 2101 if ((ps->resp != NULL) && (ps->resp->statusmsg != NULL)) { 2102 pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP), 2103 ps->resp->statusmsg); 2104 } 2105 2106 herr = http_get_lasterr(ps->hps, &errsrc); 2107 http_err = http_errorstr(errsrc, herr); 2108 pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP), 2109 http_err); 2110 return (B_FALSE); 2111 } 2112 return (B_TRUE); 2113 } 2114 2115 /* 2116 * Name: web_get_file 2117 * Description: Downloads the file URL from the website, all of 2118 * which are recorded in the static 'ps' struct 2119 * 2120 * Arguments: err - where to record any errors. 2121 * dwnld_dir - Directory to download file into 2122 * device - Where to store path to resulting 2123 * file 2124 * nointeract - if non-zero, do not output 2125 * progress 2126 * fname - name of downloaded file link in the dwnld_dir 2127 * 2128 * Returns : WEB_OK - download successful 2129 * WEB_CONNREFUSED - Connection was refused to web site 2130 * WEB_HOSTDOWN - Host was not responding to request 2131 * WEB_GET_FAIL - Unable to initialize download 2132 * state (temp file creation, header parsing, etc) 2133 * WEB_NOCONNECT - Some other connection failure 2134 */ 2135 static WebStatus 2136 web_get_file(PKG_ERR *err, char *dwnld_dir, int nointeract, char **fname) 2137 { 2138 int i, fd; 2139 int n = 0; 2140 ulong_t abs_pos = 0; 2141 char *head_val = NULL; 2142 char *lastmod_val = NULL; 2143 char *bname = NULL; 2144 struct stat status; 2145 WebStatus ret = WEB_OK; 2146 WebStatus req_ret; 2147 ulong_t errcode; 2148 uint_t errsrc; 2149 int my_errno = 0; 2150 const char *libhttperr = NULL; 2151 char *disp; 2152 char tmp_file[PATH_MAX]; 2153 int len; 2154 2155 ps->data.prev_cont_length = 2156 ps->data.content_length = 2157 ps->data.cur_pos = 0; 2158 2159 if ((head_val = http_get_header_value(ps->hps, 2160 CONTENT_LENGTH_HDR)) != NULL) { 2161 ps->data.content_length = atol(head_val); 2162 } else { 2163 pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_HEAD_VAL), 2164 CONTENT_LENGTH_HDR); 2165 ret = WEB_GET_FAIL; 2166 goto cleanup; 2167 } 2168 2169 free(head_val); 2170 head_val = NULL; 2171 2172 if ((head_val = http_get_header_value(ps->hps, 2173 CONTENT_DISPOSITION_HDR)) != NULL) { 2174 /* "inline; parm=val; parm=val */ 2175 if ((disp = strtok(head_val, "; \t\n\f\r")) != NULL) { 2176 /* disp = "inline" */ 2177 while ((disp = strtok(NULL, "; \t\n\f\r")) != NULL) { 2178 /* disp = "parm=val" */ 2179 if (ci_strneq(disp, "filename=", 9)) { 2180 bname = xstrdup(basename(disp + 9)); 2181 trim(bname); 2182 dequote(bname); 2183 } 2184 } 2185 } 2186 free(head_val); 2187 head_val = NULL; 2188 } 2189 2190 if (bname == NULL) { 2191 /* 2192 * couldn't determine filename from header value, 2193 * so take basename of URL 2194 */ 2195 if ((bname = get_endof_string(ps->url.abspath, '/')) == NULL) { 2196 /* URL is bad */ 2197 pkgerr_add(err, PKGERR_PARSE, 2198 gettext(ERR_PARSE_URL), ps->url.abspath); 2199 ret = WEB_GET_FAIL; 2200 goto cleanup; 2201 } 2202 } 2203 2204 *fname = bname; 2205 2206 if ((head_val = http_get_header_value(ps->hps, LAST_MODIFIED_HDR)) 2207 != NULL) { 2208 2209 if ((lastmod_val = condense_lastmodified(head_val)) == NULL) { 2210 pkgerr_add(err, PKGERR_WEB, gettext(ERR_BAD_HEAD_VAL), 2211 LAST_MODIFIED_HDR, head_val); 2212 ret = WEB_GET_FAIL; 2213 goto cleanup; 2214 } 2215 free(head_val); 2216 head_val = NULL; 2217 2218 if ((ps->uniqfile = get_unique_filename(dwnld_dir, 2219 lastmod_val)) == NULL) { 2220 pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPEN_TMP)); 2221 ret = WEB_GET_FAIL; 2222 goto cleanup; 2223 } 2224 2225 free(lastmod_val); 2226 lastmod_val = NULL; 2227 2228 if ((fd = open(ps->uniqfile, 2229 O_NONBLOCK|O_RDWR|O_APPEND|O_CREAT|O_EXCL, 2230 640)) == -1) { 2231 2232 /* 2233 * A partial downloaded file 2234 * already exists, so open it. 2235 */ 2236 if ((fd = open(ps->uniqfile, 2237 O_NONBLOCK|O_RDWR|O_APPEND)) != -1) { 2238 if (fstat(fd, &status) == -1 || 2239 !S_ISREG(status.st_mode)) { 2240 pkgerr_add(err, PKGERR_WEB, 2241 gettext(ERR_DWNLD_NO_CONT), 2242 ps->uniqfile); 2243 ret = WEB_GET_FAIL; 2244 goto cleanup; 2245 } else { 2246 echo_out(nointeract, 2247 gettext(MSG_DWNLD_PART), 2248 ps->uniqfile, 2249 status.st_size); 2250 ps->data.prev_cont_length = 2251 status.st_size; 2252 } 2253 } else { 2254 /* unable to open partial file */ 2255 pkgerr_add(err, PKGERR_WEB, 2256 gettext(ERR_DWNLD_NO_CONT), 2257 ps->uniqfile); 2258 ret = WEB_GET_FAIL; 2259 goto cleanup; 2260 } 2261 } 2262 } else { 2263 /* 2264 * no "Last-Modified" header, so this file is not eligible for 2265 * spooling and "resuming last download" operations 2266 */ 2267 ps->spool = B_FALSE; 2268 2269 /* mkstemp replaces XXXXXX with a unique string */ 2270 if (((len = snprintf(tmp_file, PATH_MAX, 2271 "%s/%sXXXXXX", dwnld_dir, "stream")) < 0) || 2272 (len >= PATH_MAX)) { 2273 pkgerr_add(err, PKGERR_WEB, 2274 gettext(MSG_NOTEMP), dwnld_dir); 2275 ret = WEB_GET_FAIL; 2276 goto cleanup; 2277 } 2278 2279 if ((fd = mkstemp(tmp_file)) == -1) { 2280 pkgerr_add(err, PKGERR_WEB, 2281 gettext(MSG_NOTMPFIL), tmp_file); 2282 ret = WEB_GET_FAIL; 2283 goto cleanup; 2284 } 2285 2286 if (fstat(fd, &status) == -1 || 2287 !S_ISREG(status.st_mode)) { 2288 pkgerr_add(err, PKGERR_WEB, 2289 gettext(ERR_DWNLD_NO_CONT), 2290 ps->uniqfile); 2291 ret = WEB_GET_FAIL; 2292 goto cleanup; 2293 } 2294 2295 ps->data.prev_cont_length = 0; 2296 ps->uniqfile = xstrdup(tmp_file); 2297 } 2298 2299 /* File has already been completely downloaded */ 2300 if (ps->data.prev_cont_length == ps->data.content_length) { 2301 echo_out(nointeract, gettext(MSG_DWNLD_PREV), ps->uniqfile); 2302 ps->data.cur_pos = ps->data.prev_cont_length; 2303 if (!make_link(dwnld_dir, bname)) { 2304 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), 2305 dwnld_dir); 2306 ret = WEB_GET_FAIL; 2307 goto cleanup; 2308 } 2309 /* we're done, so cleanup and return success */ 2310 goto cleanup; 2311 } else if (ps->data.prev_cont_length != 0) { 2312 ps->data.cur_pos = ps->data.prev_cont_length; 2313 } 2314 2315 if (!ck_dwnld_dir_space(err, dwnld_dir, 2316 (ps->data.prev_cont_length != 0) ? 2317 (ps->data.content_length - ps->data.cur_pos) : 2318 ps->data.content_length)) { 2319 ret = WEB_GET_FAIL; 2320 goto cleanup; 2321 } 2322 2323 if ((req_ret = web_send_request(err, HTTP_REQ_TYPE_GET, 2324 ps->data.cur_pos, ps->data.content_length)) != WEB_OK) { 2325 ret = req_ret; 2326 goto cleanup; 2327 } 2328 2329 if (ps->data.prev_cont_length != 0) 2330 echo_out(nointeract, gettext(MSG_DWNLD_CONT)); 2331 else 2332 echo_out(nointeract, gettext(MSG_DWNLD)); 2333 2334 progress_setup(nointeract, ps->data.content_length); 2335 2336 /* Download the file a BLOCK at a time */ 2337 while (ps->data.cur_pos < ps->data.content_length) { 2338 progress_report(nointeract, abs_pos); 2339 i = ((ps->data.content_length - ps->data.cur_pos) < BLOCK) ? 2340 (ps->data.content_length - ps->data.cur_pos) 2341 : BLOCK; 2342 if ((n = http_read_body(ps->hps, ps->content, i)) <= 0) { 2343 while ((errcode = http_get_lasterr(ps->hps, 2344 &errsrc)) != 0) { 2345 /* Have an error - is it EINTR? */ 2346 if (errsrc == ERRSRC_SYSTEM) { 2347 my_errno = errcode; 2348 break; 2349 } else { 2350 /* 2351 * save first non-system 2352 * error message 2353 */ 2354 libhttperr = 2355 http_errorstr(errsrc, errcode); 2356 } 2357 } 2358 switch (my_errno) { 2359 case EINTR: 2360 case ETIMEDOUT: 2361 /* Timed out. Try, try again */ 2362 ret = WEB_TIMEOUT; 2363 break; 2364 case ECONNREFUSED: 2365 ret = WEB_CONNREFUSED; 2366 break; 2367 case EHOSTDOWN: 2368 ret = WEB_HOSTDOWN; 2369 break; 2370 default: 2371 /* some other fatal error */ 2372 ret = WEB_NOCONNECT; 2373 if (libhttperr == NULL) { 2374 pkgerr_add(err, PKGERR_WEB, 2375 gettext(ERR_INIT_CONN), 2376 ps->url.hport.hostname); 2377 } else { 2378 pkgerr_add(err, PKGERR_WEB, 2379 gettext(ERR_HTTP), libhttperr); 2380 } 2381 break; 2382 } 2383 goto cleanup; 2384 } 2385 if ((n = write(fd, ps->content, n)) == 0) { 2386 pkgerr_add(err, PKGERR_WEB, gettext(ERR_WRITE), 2387 ps->uniqfile, strerror(errno)); 2388 ret = WEB_GET_FAIL; 2389 goto cleanup; 2390 } 2391 ps->data.cur_pos += n; 2392 abs_pos += n; 2393 } 2394 2395 progress_finish(nointeract); 2396 echo_out(nointeract, gettext(MSG_DWNLD_COMPLETE)); 2397 2398 if (!make_link(dwnld_dir, bname)) { 2399 pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), 2400 dwnld_dir); 2401 ret = WEB_GET_FAIL; 2402 goto cleanup; 2403 } 2404 2405 cleanup: 2406 sync(); 2407 if (fd != -1) { 2408 (void) close(fd); 2409 } 2410 2411 if (head_val != NULL) 2412 free(head_val); 2413 2414 if (lastmod_val != NULL) 2415 free(lastmod_val); 2416 2417 return (ret); 2418 } 2419 2420 /* 2421 * Name: make_link 2422 * Description: Create new link to file being downloaded 2423 * 2424 * Arguments: dwnld_dir - directory in which downloaded file exists 2425 * bname - name of link 2426 * 2427 * Returns : B_TRUE - success, B_FALSE otherwise 2428 */ 2429 static boolean_t 2430 make_link(char *dwnld_dir, char *bname) 2431 { 2432 int len; 2433 2434 if ((ps->link = (char *)xmalloc(PATH_MAX)) == NULL) 2435 return (B_FALSE); 2436 if (((len = snprintf(ps->link, PATH_MAX, "%s/%s", 2437 dwnld_dir, bname)) < 0) || 2438 len >= PATH_MAX) 2439 return (B_FALSE); 2440 2441 (void) link(ps->uniqfile, ps->link); 2442 2443 return (B_TRUE); 2444 } 2445 2446 /* 2447 * Name: get_startof_string 2448 * Description: searches string for token, returns a newly-allocated 2449 * substring of the given string up to, but not 2450 * including, token. for example 2451 * get_startof_string("abcd", 'c') will return "ab" 2452 * 2453 * Arguments: path - path to split 2454 * token - character to split on 2455 * 2456 * Returns : substring of 'path', up to, but not including, 2457 * token, if token appears in path. Otherwise, 2458 * returns NULL. 2459 */ 2460 char * 2461 get_startof_string(char *path, char token) 2462 { 2463 char *p, *p2; 2464 2465 if (path == NULL) 2466 return (NULL); 2467 2468 p = xstrdup(path); 2469 2470 p2 = strchr(p, token); 2471 if (p2 == NULL) { 2472 free(p); 2473 return (NULL); 2474 } else { 2475 *p2 = '\0'; 2476 return (p); 2477 } 2478 } 2479 2480 /* 2481 * Name: get_endof_string 2482 * Description: searches string for token, returns a 2483 * newly-allocated substring of the given string, 2484 * starting at character following token, to end of 2485 * string. 2486 * 2487 * for example get_end_string("abcd", 'c') 2488 * will return "d" 2489 * 2490 * Arguments: path - path to split 2491 * token - character to split on 2492 * 2493 * Returns : substring of 'path', beginning at character 2494 * following token, to end of string, if 2495 * token appears in path. Otherwise, 2496 * returns NULL. 2497 */ 2498 char * 2499 get_endof_string(char *path, char token) 2500 { 2501 char *p, *p2; 2502 2503 if (path == NULL) 2504 return (NULL); 2505 2506 p = xstrdup(path); 2507 2508 if ((p2 = strrchr(p, token)) == NULL) { 2509 return (NULL); 2510 } 2511 2512 return (p2 + 1); 2513 } 2514 2515 /* 2516 * Name: progress_setup 2517 * Description: Initialize session for reporting progress 2518 * 2519 * Arguments: nointeract - if non-zero, do not do anything 2520 * ulong_t - size of job to report progress for 2521 * 2522 * Returns : none 2523 */ 2524 static void 2525 progress_setup(int nointeract, ulong_t size_of_load) 2526 { 2527 ulong_t divisor; 2528 ulong_t term_width = TERM_WIDTH; 2529 2530 if (nointeract) 2531 return; 2532 2533 if (size_of_load > MED_DWNLD && size_of_load < LARGE_DWNLD) 2534 divisor = MED_DIVISOR; 2535 else if (size_of_load > LARGE_DWNLD) { 2536 term_width = TERM_WIDTH - 8; 2537 divisor = LARGE_DIVISOR; 2538 } else 2539 divisor = SMALL_DIVISOR; 2540 2541 const_increment = size_of_load / term_width; 2542 const_divider = size_of_load / divisor; 2543 const_completed = 100 / divisor; 2544 } 2545 2546 /* 2547 * Name: progress_report 2548 * Description: Report progress for current progress context, 2549 * to stderr 2550 * 2551 * Arguments: nointeract - if non-zero, do not do anything 2552 * position - how far along in the job to report. 2553 * This should be <= size used during progress_setup 2554 * 2555 * Returns : none 2556 */ 2557 static void 2558 progress_report(int nointeract, ulong_t position) 2559 { 2560 static ulong_t increment; 2561 static ulong_t divider; 2562 2563 if (nointeract) 2564 return; 2565 2566 if (position == 0) { 2567 increment = const_increment; 2568 divider = const_divider; 2569 } 2570 if (position > increment && position < divider) { 2571 (void) putc('.', stderr); 2572 increment += const_increment; 2573 } else if (position > divider) { 2574 completed += const_completed; 2575 (void) fprintf(stderr, "%ld%c", completed, '%'); 2576 increment += const_increment; 2577 divider += const_divider; 2578 } 2579 } 2580 2581 /* 2582 * Name: progress_finish 2583 * Description: Finalize session for reporting progress. 2584 * "100%" is reported to screen 2585 * 2586 * Arguments: nointeract - if non-zero, do not do anything 2587 * 2588 * Returns : none 2589 */ 2590 static void 2591 progress_finish(int nointeract) 2592 { 2593 if (nointeract) 2594 return; 2595 2596 (void) fprintf(stderr, "%d%c\n", 100, '%'); 2597 } 2598 2599 /* 2600 * Name: init_session 2601 * Description: Initializes static 'ps' structure with default 2602 * values 2603 * 2604 * Arguments: none 2605 * 2606 * Returns : B_TRUE - success, B_FALSE otherwise 2607 */ 2608 static boolean_t 2609 init_session(void) 2610 { 2611 if ((ps = (WEB_SESSION *) 2612 xmalloc(sizeof (WEB_SESSION))) == NULL) { 2613 return (B_FALSE); 2614 } 2615 (void) memset(ps, 0, sizeof (*ps)); 2616 2617 if ((ps->content = (char *)xmalloc(BLOCK)) == NULL) { 2618 return (B_FALSE); 2619 } 2620 2621 (void) memset(ps->content, 0, BLOCK); 2622 2623 ps->data.cur_pos = 0UL; 2624 ps->data.content_length = 0UL; 2625 ps->url.https = B_FALSE; 2626 ps->uniqfile = NULL; 2627 ps->link = NULL; 2628 ps->dwnld_dir = NULL; 2629 ps->spool = B_TRUE; 2630 ps->errstr = NULL; 2631 ps->keystore = NULL; 2632 2633 return (B_TRUE); 2634 } 2635 2636 /* 2637 * Name: ck_downld_dir_space 2638 * Description: Verify enough space exists in directory to hold file 2639 * 2640 * Arguments: err - where to record any errors. 2641 * dwnld_dir - Directory to check available space in 2642 * bytes_needed - How many bytes are need 2643 * 2644 * Returns : B_TRUE - enough space exists in dwnld_dir to hold 2645 * bytes_needed bytes, otherwise B_FALSE 2646 */ 2647 static boolean_t 2648 ck_dwnld_dir_space(PKG_ERR *err, char *dwnld_dir, ulong_t bytes_needed) 2649 { 2650 u_longlong_t bytes_avail; 2651 u_longlong_t block_pad; 2652 struct statvfs64 status; 2653 2654 if (statvfs64(dwnld_dir, &status)) { 2655 pkgerr_add(err, PKGERR_WEB, gettext(ERR_TMPDIR), dwnld_dir); 2656 return (B_FALSE); 2657 } 2658 2659 block_pad = (status.f_frsize ? status.f_frsize : status.f_bsize); 2660 bytes_avail = status.f_bavail * block_pad; 2661 2662 if ((((u_longlong_t)bytes_needed) + block_pad) > bytes_avail) { 2663 pkgerr_add(err, PKGERR_WEB, gettext(ERR_DISK_SPACE), 2664 dwnld_dir, 2665 (((u_longlong_t)bytes_needed) + block_pad) / 1024ULL, 2666 bytes_avail / 1024ULL); 2667 return (B_FALSE); 2668 } 2669 2670 return (B_TRUE); 2671 } 2672 2673 /* 2674 * Description: 2675 * This function returns a unique file name based on the parts of the 2676 * URI. This is done to enable partially downloaded files to be resumed. 2677 * Arguments: 2678 * dir - The directory that should contain the filename. 2679 * last_modified - A string representing the date of last modification, 2680 * used as part of generating unique name 2681 * Returns: 2682 * A valid filename or NULL. 2683 */ 2684 2685 static char * 2686 get_unique_filename(char *dir, char *last_modified) 2687 { 2688 char *buf, *buf2, *beg_str; 2689 int len; 2690 2691 if ((buf = (char *)xmalloc(PATH_MAX)) == NULL) { 2692 return (NULL); 2693 } 2694 if ((buf2 = (char *)xmalloc(PATH_MAX)) == NULL) { 2695 return (NULL); 2696 } 2697 2698 /* prepare strings for being cat'ed onto */ 2699 buf[0] = buf2[0] = '\0'; 2700 /* 2701 * No validation of the path is done here. We just construct the path 2702 * and it must be validated later 2703 */ 2704 2705 if (dir) { 2706 if (((len = snprintf(buf2, PATH_MAX, "%s/", dir)) < 0) || 2707 (len >= PATH_MAX)) 2708 return (NULL); 2709 } else { 2710 return (NULL); 2711 } 2712 2713 if (ps->url.abspath) 2714 if (strlcat(buf, ps->url.abspath, PATH_MAX) >= PATH_MAX) 2715 return (NULL); 2716 if (ps->url.hport.hostname) 2717 if (isdigit((int)ps->url.hport.hostname[0])) { 2718 if (strlcat(buf, ps->url.hport.hostname, PATH_MAX) 2719 >= PATH_MAX) 2720 return (NULL); 2721 } else { 2722 if ((beg_str = 2723 get_startof_string(ps->url.hport.hostname, '.')) 2724 != NULL) 2725 if (strlcat(buf, beg_str, PATH_MAX) >= PATH_MAX) 2726 return (NULL); 2727 } 2728 if (last_modified != NULL) 2729 if (strlcat(buf, last_modified, PATH_MAX) >= PATH_MAX) 2730 return (NULL); 2731 2732 if ((buf = replace_token(buf, '/', '_')) != NULL) { 2733 if (strlcat(buf2, buf, PATH_MAX) >= PATH_MAX) { 2734 return (NULL); 2735 } else { 2736 if (buf) free(buf); 2737 return (buf2); 2738 } 2739 } else { 2740 if (buf) free(buf); 2741 if (buf2) free(buf2); 2742 return (NULL); 2743 } 2744 } 2745 2746 /* 2747 * Description: 2748 * Removes token(s) consisting of one character from any path. 2749 * Arguments: 2750 * path - The path to search for the token in. 2751 * token - The token to search for 2752 * Returns: 2753 * The path with all tokens removed or NULL. 2754 */ 2755 static char * 2756 replace_token(char *path, char oldtoken, char newtoken) 2757 { 2758 char *newpath, *p; 2759 2760 if ((path == NULL) || (oldtoken == '\0') || (newtoken == '\0')) { 2761 return (NULL); 2762 } 2763 2764 newpath = xstrdup(path); 2765 2766 for (p = newpath; *p != '\0'; p++) { 2767 if (*p == oldtoken) { 2768 *p = newtoken; 2769 } 2770 } 2771 2772 return (newpath); 2773 } 2774 2775 /* 2776 * Name: trim 2777 * Description: Trims whitespace from a string 2778 * has been registered) 2779 * Scope: private 2780 * Arguments: string - string to trim. It is assumed 2781 * this string is writable up to it's entire 2782 * length. 2783 * Returns: none 2784 */ 2785 static void 2786 trim(char *str) 2787 { 2788 int len, i; 2789 if (str == NULL) { 2790 return; 2791 } 2792 2793 len = strlen(str); 2794 /* strip from front */ 2795 while (isspace(*str)) { 2796 for (i = 0; i < len; i++) { 2797 str[i] = str[i+1]; 2798 } 2799 } 2800 2801 /* strip from back */ 2802 len = strlen(str); 2803 while (isspace(str[len-1])) { 2804 len--; 2805 } 2806 str[len] = '\0'; 2807 } 2808 2809 /* 2810 * Description: 2811 * Resolves double quotes 2812 * Arguments: 2813 * str - The string to resolve 2814 * Returns: 2815 * None 2816 */ 2817 static void 2818 dequote(char *str) 2819 { 2820 char *cp; 2821 2822 if ((str == NULL) || (str[0] != '"')) { 2823 /* no quotes */ 2824 return; 2825 } 2826 2827 /* remove first quote */ 2828 (void) memmove(str, str + 1, strlen(str) - 1); 2829 2830 /* 2831 * scan string looking for ending quote. 2832 * escaped quotes like \" don't count 2833 */ 2834 cp = str; 2835 2836 while (*cp != '\0') { 2837 switch (*cp) { 2838 case '\\': 2839 /* found an escaped character */ 2840 /* make sure end of string is not '\' */ 2841 if (*++cp != '\0') { 2842 cp++; 2843 } 2844 break; 2845 2846 case '"': 2847 *cp = '\0'; 2848 break; 2849 default: 2850 cp++; 2851 } 2852 } 2853 } 2854 2855 /* 2856 * Name: get_ENV_proxy 2857 * Description: Retrieves setting of proxy env variable 2858 * 2859 * Arguments: err - where to record any errors. 2860 * proxy - where to store proxy 2861 * 2862 * Returns : B_TRUE - http proxy was found and valid, stored in proxy 2863 * B_FALSE - error, errors recorded in err 2864 */ 2865 static boolean_t 2866 get_ENV_proxy(PKG_ERR *err, char **proxy) 2867 { 2868 char *buf; 2869 2870 if ((buf = getenv("HTTPPROXY")) != NULL) { 2871 if (!path_valid(buf)) { 2872 pkgerr_add(err, PKGERR_WEB, 2873 gettext(ERR_ILL_ENV), "HTTPPROXY", buf); 2874 return (B_FALSE); 2875 } else { 2876 *proxy = buf; 2877 return (B_TRUE); 2878 } 2879 } else { 2880 /* try the other env variable */ 2881 if ((buf = getenv("http_proxy")) != NULL) { 2882 if (!path_valid(buf)) { 2883 pkgerr_add(err, PKGERR_WEB, 2884 gettext(ERR_ILL_ENV), "http_proxy", buf); 2885 return (B_FALSE); 2886 } 2887 if (!strneq(buf, "http://", 7)) { 2888 pkgerr_add(err, PKGERR_WEB, 2889 gettext(ERR_ILL_ENV), "http_proxy", buf); 2890 return (B_FALSE); 2891 } 2892 2893 /* skip over the http:// part of the proxy "url" */ 2894 *proxy = buf + 7; 2895 return (B_TRUE); 2896 } 2897 } 2898 2899 /* either the env variable(s) were set and valid, or not set */ 2900 return (B_TRUE); 2901 } 2902 2903 /* 2904 * Name: get_ENV_proxyport 2905 * Description: Retrieves setting of PROXYPORT env variable 2906 * 2907 * Arguments: err - where to record any errors. 2908 * port - where to store resulting port 2909 * 2910 * Returns : B_TRUE - string found in PROXYPORT variable, converted 2911 * to decimal integer, if it exists 2912 * and is valid. Or, PROXYPORT not set, port set to 1. 2913 * B_FALSE - env variable set, but invalid 2914 * (not a number for example) 2915 */ 2916 static boolean_t 2917 get_ENV_proxyport(PKG_ERR *err, ushort_t *port) 2918 { 2919 char *buf; 2920 ushort_t newport; 2921 buf = getenv("HTTPPROXYPORT"); 2922 if (buf != NULL) { 2923 if (!path_valid(buf)) { 2924 pkgerr_add(err, PKGERR_WEB, 2925 gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf); 2926 return (B_FALSE); 2927 } 2928 if ((newport = atoi(buf)) == 0) { 2929 pkgerr_add(err, PKGERR_WEB, 2930 gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf); 2931 return (B_FALSE); 2932 } 2933 *port = newport; 2934 return (B_TRUE); 2935 } else { 2936 *port = 1; 2937 return (B_TRUE); 2938 } 2939 } 2940 2941 /* 2942 * Name: remove_dwnld_file 2943 * Description: Removes newly-downloaded file if completely downloaded. 2944 * 2945 * Arguments: path - path to file to remove 2946 * 2947 * Returns : B_TRUE - success, B_FALSE otherwise 2948 * if it's '0' (not OK) we simply return it, since the 2949 * verification operation has already determined that the 2950 * cert is invalid. if 'ok' is non-zero, then we do our 2951 * checks, and return 0 or 1 based on if the cert is 2952 * invalid or valid. 2953 */ 2954 static boolean_t 2955 remove_dwnld_file(char *path) 2956 { 2957 if (path && path != NULL) { 2958 /* 2959 * Only remove the downloaded file if it has been completely 2960 * downloaded, or is not eligible for spooling 2961 */ 2962 if ((!ps->spool) || 2963 (ps->data.cur_pos >= ps->data.content_length)) { 2964 (void) unlink(path); 2965 } 2966 } else { 2967 return (B_FALSE); 2968 } 2969 return (B_TRUE); 2970 } 2971 2972 /* 2973 * Name: condense_lastmodifided 2974 * Description: generates a substring of a last-modified string, 2975 * and removes colons. 2976 * 2977 * Arguments: last_modified - string of the form 2978 * "Wed, 23 Oct 2002 21:59:45 GMT" 2979 * 2980 * Returns : 2981 * new string, consisting of hours/minutes/seconds only, 2982 * sans any colons. 2983 */ 2984 char * 2985 condense_lastmodified(char *last_modified) 2986 { 2987 char *p, *p2; 2988 2989 /* 2990 * Last-Modified: Wed, 23 Oct 2002 21:59:45 GMT 2991 * Strip the hours, minutes and seconds, without the ':'s, from 2992 * the above string, void of the ':". 2993 */ 2994 2995 if (last_modified == NULL) 2996 return (NULL); 2997 2998 if ((p = xstrdup(last_modified)) == NULL) 2999 return (NULL); 3000 p2 = (strstr(p, ":") - 2); 3001 p2[8] = '\0'; 3002 return (replace_token(p2, ':', '_')); 3003 } 3004 3005 /* 3006 * Name: backoff 3007 * Description: sleeps for a certain # of seconds after a network 3008 * failure. 3009 * Scope: public 3010 * Arguments: none 3011 * Returns: none 3012 */ 3013 void 3014 backoff() 3015 { 3016 static boolean_t initted = B_FALSE; 3017 int backoff; 3018 long seed; 3019 3020 if (!initted) { 3021 /* seed the rng */ 3022 (void) _get_random_info(&seed, sizeof (seed)); 3023 srand48(seed); 3024 initted = B_TRUE; 3025 } 3026 3027 backoff = (int)(drand48() * (double)cur_backoff); 3028 (void) sleep(backoff); 3029 if (cur_backoff < MAX_BACKOFF) { 3030 /* 3031 * increase maximum time we might wait 3032 * next time so as to fall off over 3033 * time. 3034 */ 3035 cur_backoff *= BACKOFF_FACTOR; 3036 } 3037 } 3038 3039 /* 3040 * Name: reset_backoff 3041 * Description: notifies the backoff service that whatever was 3042 * being backoff succeeded. 3043 * Scope: public 3044 * Arguments: none 3045 * Returns: none 3046 */ 3047 void 3048 reset_backoff() 3049 { 3050 cur_backoff = MIN_BACKOFF; 3051 } 3052 3053 /* 3054 * Name: _get_random_info 3055 * Description: generate an amount of random bits. Currently 3056 * only a small amount (a long long) can be 3057 * generated at one time. 3058 * Scope: private 3059 * Arguments: buf - [RO, *RW] (char *) 3060 * Buffer to copy bits into 3061 * size - amount to copy 3062 * Returns: B_TRUE on success, B_FALSE otherwise. The buffer is filled 3063 * with the amount of bytes of random data specified. 3064 */ 3065 static boolean_t 3066 _get_random_info(void *buf, int size) 3067 { 3068 struct timeval tv; 3069 typedef struct { 3070 long low_time; 3071 long hostid; 3072 } randomness; 3073 randomness r; 3074 3075 /* if the RANDOM file exists, use it */ 3076 if (access(RANDOM, R_OK) == 0) { 3077 if ((RAND_load_file(RANDOM, 1024 * 1024)) > 0) { 3078 if (RAND_bytes((uchar_t *)buf, size) == 1) { 3079 /* success */ 3080 return (B_TRUE); 3081 } 3082 } 3083 } 3084 3085 /* couldn't use RANDOM file, so fallback to time of day and hostid */ 3086 (void) gettimeofday(&tv, (struct timezone *)0); 3087 3088 /* Wouldn't it be nice if we could hash these */ 3089 r.low_time = tv.tv_usec; 3090 r.hostid = gethostid(); 3091 3092 if (sizeof (r) < size) { 3093 /* 3094 * Can't copy correctly 3095 */ 3096 return (B_FALSE); 3097 } 3098 (void) memcpy(buf, &r, size); 3099 return (B_TRUE); 3100 } 3101 3102 /* 3103 * Name: pkg_passphrase_cb 3104 * Description: Default callback that applications can use when 3105 * a passphrase is needed. This routine collects 3106 * a passphrase from the user using the given 3107 * passphrase retrieval method set with 3108 * set_passphrase_passarg(). If the method 3109 * indicates an interactive prompt, then the 3110 * prompt set with set_passphrase_prompt() 3111 * is displayed. 3112 * 3113 * Arguments: buf - Buffer to copy passphrase into 3114 * size - Max amount to copy to buf 3115 * rw - Whether this passphrase is needed 3116 * to read something off disk, or 3117 * write something to disk. Applications 3118 * typically want to ask twice when getting 3119 * a passphrase for writing something. 3120 * data - application-specific data. In this 3121 * callback, data is a pointer to 3122 * a keystore_passphrase_data structure. 3123 * 3124 * Returns: Length of passphrase collected, or -1 on error. 3125 * Errors recorded in 'err' object in the *data. 3126 */ 3127 int 3128 pkg_passphrase_cb(char *buf, int size, int rw, void *data) 3129 { 3130 BIO *pwdbio = NULL; 3131 char passphrase_copy[MAX_PHRASELEN + 1]; 3132 PKG_ERR *err; 3133 int passlen; 3134 char *ws; 3135 char prompt_copy[MAX_VERIFY_MSGLEN]; 3136 char *passphrase; 3137 char *arg; 3138 3139 err = ((keystore_passphrase_data *)data)->err; 3140 3141 if (passarg == NULL) { 3142 arg = "console"; 3143 } else { 3144 arg = passarg; 3145 } 3146 3147 /* default method of collecting password is by prompting */ 3148 if (ci_streq(arg, "console")) { 3149 if ((passphrase = getpassphrase(prompt)) == NULL) { 3150 pkgerr_add(err, PKGERR_BADPASS, 3151 gettext(MSG_NOPASS), arg); 3152 return (-1); 3153 } 3154 3155 if (rw) { 3156 /* 3157 * if the password is being supplied for 3158 * writing something to disk, verify it first 3159 */ 3160 3161 /* make a copy (getpassphrase overwrites) */ 3162 (void) strlcpy(passphrase_copy, passphrase, 3163 MAX_PHRASELEN + 1); 3164 3165 if (((passlen = snprintf(prompt_copy, 3166 MAX_VERIFY_MSGLEN, "%s: %s", 3167 gettext(MSG_PASSWD_AGAIN), 3168 prompt)) < 0) || 3169 (passlen >= (MAX_PHRASELEN + 1))) { 3170 pkgerr_add(err, PKGERR_BADPASS, 3171 gettext(MSG_NOPASS), arg); 3172 return (-1); 3173 } 3174 3175 if ((passphrase = 3176 getpassphrase(prompt_copy)) == NULL) { 3177 pkgerr_add(err, PKGERR_BADPASS, 3178 gettext(MSG_NOPASS), arg); 3179 return (-1); 3180 } 3181 3182 if (!streq(passphrase_copy, passphrase)) { 3183 pkgerr_add(err, PKGERR_READ, 3184 gettext(MSG_PASSWD_NOMATCH)); 3185 return (-1); 3186 } 3187 } 3188 } else if (ci_strneq(arg, "pass:", 5)) { 3189 passphrase = arg + 5; 3190 } else if (ci_strneq(arg, "env:", 4)) { 3191 passphrase = getenv(arg + 4); 3192 } else if (ci_strneq(arg, "file:", 5)) { 3193 3194 /* open file for reading */ 3195 if ((pwdbio = BIO_new_file(arg + 5, "r")) == NULL) { 3196 pkgerr_add(err, PKGERR_EXIST, 3197 gettext(MSG_PASSWD_FILE), arg + 5); 3198 return (-1); 3199 } 3200 3201 /* read first line */ 3202 if (((passlen = BIO_gets(pwdbio, buf, size)) < 1) || 3203 (passlen > size)) { 3204 pkgerr_add(err, PKGERR_READ, gettext(MSG_PASSWD_FILE), 3205 arg + 5); 3206 return (-1); 3207 } 3208 BIO_free_all(pwdbio); 3209 pwdbio = NULL; 3210 3211 if (passlen == size) { 3212 /* 3213 * password was maximum length, so there is 3214 * no null terminator. null-terminate it 3215 */ 3216 buf[size - 1] = '\0'; 3217 } 3218 3219 /* first newline found is end of passwd, so nuke it */ 3220 if ((ws = strchr(buf, '\n')) != NULL) { 3221 *ws = '\0'; 3222 } 3223 return (strlen(buf)); 3224 } else { 3225 /* unrecognized passphrase */ 3226 pkgerr_add(err, PKGERR_BADPASS, 3227 gettext(MSG_BADPASSARG), arg); 3228 return (-1); 3229 } 3230 3231 if (passphrase == NULL) { 3232 /* unable to collect passwd from given source */ 3233 pkgerr_add(err, PKGERR_BADPASS, 3234 gettext(MSG_NOPASS), arg); 3235 return (-1); 3236 } 3237 3238 (void) strlcpy(buf, passphrase, size); 3239 return (strlen(buf)); 3240 } 3241