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 28 #include <stdio.h> 29 #include <stdarg.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <sys/types.h> 33 #include <unistd.h> 34 #include <signal.h> 35 #include <locale.h> 36 #include <sys/param.h> 37 #include <openssl/bio.h> 38 #include <openssl/x509v3.h> 39 #include <openssl/ui.h> 40 41 #include <pkglib.h> 42 #include <libinst.h> 43 #include <pkgerr.h> 44 #include <keystore.h> 45 #include "pkgadm.h" 46 #include "pkgadm_msgs.h" 47 48 typedef enum { 49 VerifyFailed, 50 Accept, 51 Reject 52 } VerifyStatus; 53 54 static VerifyStatus verify_trust(X509 *); 55 static boolean_t is_ca_cert(X509 *); 56 57 /* 58 * Name: addcert 59 * Desc: Imports a user certificate into the keystore, along with a 60 * private key. 61 * Returns: 0 on success, non-zero otherwise. 62 */ 63 int 64 addcert(int argc, char **argv) 65 { 66 int i; 67 char keystore_file[MAXPATHLEN] = ""; 68 char *keystore_base = NULL; 69 char *homedir; 70 char *passarg = NULL; 71 char *import_passarg = NULL; 72 char *altroot = NULL; 73 char *prog = NULL; 74 char *alias = NULL; 75 char *infile = NULL; 76 char *inkeyfile = NULL; 77 keystore_encoding_format_t informat = NULL; 78 char *informat_str = NULL; 79 int ret = 1; 80 boolean_t trusted = B_FALSE; 81 boolean_t implicit_trust = B_FALSE; 82 83 FILE *certfile = NULL; 84 FILE *keyfile = NULL; 85 X509 *cert = NULL; 86 STACK_OF(X509) *trustcerts = NULL; 87 EVP_PKEY *key = NULL; 88 PKG_ERR *err = NULL; 89 keystore_handle_t keystore = NULL; 90 91 while ((i = getopt(argc, argv, ":a:k:e:f:n:P:p:R:ty")) != EOF) { 92 switch (i) { 93 case 'a': 94 prog = optarg; 95 break; 96 case 'k': 97 keystore_base = optarg; 98 break; 99 case 'e': 100 inkeyfile = optarg; 101 break; 102 case 'f': 103 informat_str = optarg; 104 break; 105 case 'n': 106 alias = optarg; 107 break; 108 case 'P': 109 passarg = optarg; 110 break; 111 case 'p': 112 import_passarg = optarg; 113 break; 114 case 'R': 115 altroot = optarg; 116 break; 117 case 't': 118 trusted = B_TRUE; 119 break; 120 case 'y': 121 implicit_trust = B_TRUE; 122 break; 123 case ':': 124 log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt); 125 /* LINTED fallthrough intentional */ 126 case '?': 127 default: 128 log_msg(LOG_MSG_ERR, MSG_USAGE); 129 goto cleanup; 130 } 131 } 132 133 if (!trusted && alias == NULL) { 134 /* for untrusted (user) certs, we require a name */ 135 log_msg(LOG_MSG_ERR, MSG_USER_NAME); 136 log_msg(LOG_MSG_ERR, MSG_USAGE); 137 goto cleanup; 138 } else if (trusted && alias != NULL) { 139 /* for trusted certs, we cannot have a name */ 140 log_msg(LOG_MSG_ERR, MSG_TRUSTED_NAME); 141 log_msg(LOG_MSG_ERR, MSG_USAGE); 142 goto cleanup; 143 } 144 145 if (trusted && inkeyfile != NULL) { 146 /* for trusted certs, we cannot have a private key */ 147 log_msg(LOG_MSG_ERR, MSG_TRUSTED_KEY); 148 log_msg(LOG_MSG_ERR, MSG_USAGE); 149 goto cleanup; 150 } 151 152 /* last argument should be the path to the certificate */ 153 if ((argc-optind) > 1) { 154 log_msg(LOG_MSG_ERR, MSG_USAGE); 155 goto cleanup; 156 } else if ((argc-optind) < 1) { 157 infile = "stdin"; 158 certfile = stdin; 159 log_msg(LOG_MSG_DEBUG, "Loading stdin certificate"); 160 } else { 161 infile = argv[optind]; 162 log_msg(LOG_MSG_DEBUG, "Loading <%s> certificate", 163 argv[optind]); 164 if ((certfile = fopen(infile, "r")) == NULL) { 165 log_msg(LOG_MSG_ERR, MSG_OPEN, infile); 166 goto cleanup; 167 } 168 } 169 170 /* 171 * if specific key file supplied, open it, otherwise open 172 * default (stdin) 173 */ 174 if (inkeyfile != NULL) { 175 if ((keyfile = fopen(inkeyfile, "r")) == NULL) { 176 log_msg(LOG_MSG_ERR, MSG_OPEN, inkeyfile); 177 goto cleanup; 178 } 179 } else { 180 inkeyfile = "stdin"; 181 keyfile = stdin; 182 } 183 184 /* set up proper keystore */ 185 if (altroot != NULL) { 186 if (strlcpy(keystore_file, altroot, MAXPATHLEN) >= MAXPATHLEN) { 187 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot); 188 goto cleanup; 189 } 190 191 if (strlcat(keystore_file, "/", MAXPATHLEN) >= MAXPATHLEN) { 192 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot); 193 goto cleanup; 194 } 195 } 196 197 if (keystore_base == NULL) { 198 if (geteuid() == 0 || altroot != NULL) { 199 /* 200 * If we have an alternate 201 * root, then we have no choice but to use 202 * root's keystore on that alternate root, 203 * since there is no way to resolve a 204 * user's home dir given an alternate root 205 */ 206 if (strlcat(keystore_file, PKGSEC, 207 MAXPATHLEN) >= MAXPATHLEN) { 208 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, 209 keystore_file); 210 goto cleanup; 211 } 212 } else { 213 if ((homedir = getenv("HOME")) == NULL) { 214 /* 215 * not superuser, but no home dir, so 216 * use superuser's keystore 217 */ 218 if (strlcat(keystore_file, PKGSEC, 219 MAXPATHLEN) >= MAXPATHLEN) { 220 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, 221 keystore_file); 222 goto cleanup; 223 } 224 } else { 225 if (strlcat(keystore_file, homedir, 226 MAXPATHLEN) >= MAXPATHLEN) { 227 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, 228 homedir); 229 goto cleanup; 230 } 231 if (strlcat(keystore_file, "/.pkg/security", 232 MAXPATHLEN) >= MAXPATHLEN) { 233 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, 234 keystore_file); 235 goto cleanup; 236 } 237 } 238 } 239 } else { 240 if (strlcat(keystore_file, keystore_base, 241 MAXPATHLEN) >= MAXPATHLEN) { 242 log_msg(LOG_MSG_ERR, MSG_TOO_LONG, 243 keystore_base); 244 goto cleanup; 245 } 246 } 247 248 /* figure out input format */ 249 if (informat_str == NULL) { 250 informat = KEYSTORE_FORMAT_PEM; 251 } else { 252 if (ci_streq(informat_str, "pem")) { 253 informat = KEYSTORE_FORMAT_PEM; 254 } else if (ci_streq(informat_str, "der")) { 255 informat = KEYSTORE_FORMAT_DER; 256 } else { 257 log_msg(LOG_MSG_ERR, MSG_BAD_FORMAT, informat_str); 258 goto cleanup; 259 } 260 } 261 262 err = pkgerr_new(); 263 264 if (trusted) { 265 /* load all possible certs */ 266 if (load_all_certs(err, certfile, informat, import_passarg, 267 &trustcerts) != 0) { 268 log_pkgerr(LOG_MSG_ERR, err); 269 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 270 goto cleanup; 271 } 272 273 /* we must have gotten at least one cert, if not, fail */ 274 if (sk_X509_num(trustcerts) < 1) { 275 log_msg(LOG_MSG_ERR, MSG_NO_CERTS, infile); 276 goto cleanup; 277 } 278 } else { 279 /* first, try to load user certificate and key */ 280 if (load_cert_and_key(err, certfile, informat, import_passarg, 281 &key, &cert) != 0) { 282 log_pkgerr(LOG_MSG_ERR, err); 283 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 284 goto cleanup; 285 } 286 287 /* we must have gotten a cert, if not, fail */ 288 if (cert == NULL) { 289 log_msg(LOG_MSG_ERR, MSG_NO_CERTS, infile); 290 goto cleanup; 291 } 292 293 if (key == NULL) { 294 /* 295 * if we are importing a user cert, and did not get 296 * a key, try to load it from the key file 297 */ 298 if (keyfile == NULL) { 299 log_msg(LOG_MSG_ERR, MSG_NEED_KEY, infile); 300 goto cleanup; 301 } else { 302 log_msg(LOG_MSG_DEBUG, 303 "Loading private key <%s>", inkeyfile); 304 if (load_cert_and_key(err, keyfile, informat, 305 import_passarg, 306 &key, NULL) != 0) { 307 log_pkgerr(LOG_MSG_ERR, err); 308 log_msg(LOG_MSG_ERR, 309 MSG_NO_ADDKEY, inkeyfile); 310 goto cleanup; 311 } 312 313 if (key == NULL) { 314 log_msg(LOG_MSG_ERR, MSG_NO_PRIVKEY, 315 inkeyfile); 316 log_msg(LOG_MSG_ERR, 317 MSG_NO_ADDKEY, inkeyfile); 318 goto cleanup; 319 } 320 } 321 } 322 } 323 324 if (trusted) { 325 /* check validity date of all certificates */ 326 for (i = 0; i < sk_X509_num(trustcerts); i++) { 327 /* LINTED pointer cast may result in improper algnmnt */ 328 cert = sk_X509_value(trustcerts, i); 329 if (check_cert(err, cert) != 0) { 330 log_pkgerr(LOG_MSG_ERR, err); 331 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, 332 infile); 333 goto cleanup; 334 } 335 } 336 } else { 337 /* check validity date of user certificate */ 338 if (check_cert_and_key(err, cert, key) != 0) { 339 log_pkgerr(LOG_MSG_ERR, err); 340 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 341 goto cleanup; 342 } 343 } 344 345 if (trusted && !implicit_trust) { 346 /* 347 * if importing more than one cert, must use implicit trust, 348 * because we can't ask the user to individually trust 349 * each one, since there may be many 350 */ 351 if (sk_X509_num(trustcerts) != 1) { 352 log_pkgerr(LOG_MSG_ERR, err); 353 log_msg(LOG_MSG_ERR, MSG_MULTIPLE_TRUST, infile, "-y"); 354 goto cleanup; 355 } else { 356 /* LINTED pointer cast may result in improper algnmnt */ 357 cert = sk_X509_value(trustcerts, 0); 358 } 359 360 /* ask the user */ 361 switch (verify_trust(cert)) { 362 case Accept: 363 /* user accepted */ 364 break; 365 case Reject: 366 /* user aborted operation */ 367 log_msg(LOG_MSG_ERR, MSG_ADDCERT_ABORT); 368 goto cleanup; 369 case VerifyFailed: 370 default: 371 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 372 goto cleanup; 373 } 374 } 375 376 /* now load the key store */ 377 log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file); 378 379 set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT); 380 set_passphrase_passarg(passarg); 381 if (open_keystore(err, keystore_file, prog, pkg_passphrase_cb, 382 KEYSTORE_ACCESS_READWRITE | KEYSTORE_PATH_HARD, &keystore) != 0) { 383 log_pkgerr(LOG_MSG_ERR, err); 384 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 385 goto cleanup; 386 } 387 388 /* now merge the new cert into the keystore */ 389 log_msg(LOG_MSG_DEBUG, "Merging certificate <%s>", 390 get_subject_display_name(cert)); 391 if (trusted) { 392 /* merge all trusted certs found */ 393 for (i = 0; i < sk_X509_num(trustcerts); i++) { 394 /* LINTED pointer cast may result in improper algnmnt */ 395 cert = sk_X509_value(trustcerts, i); 396 if (merge_ca_cert(err, cert, keystore) != 0) { 397 log_pkgerr(LOG_MSG_ERR, err); 398 log_msg(LOG_MSG_ERR, 399 MSG_NO_ADDCERT, infile); 400 goto cleanup; 401 402 } else { 403 log_msg(LOG_MSG_INFO, MSG_TRUSTING, 404 get_subject_display_name(cert)); 405 } 406 } 407 } else { 408 /* merge user cert */ 409 if (merge_cert_and_key(err, cert, key, alias, keystore) != 0) { 410 log_pkgerr(LOG_MSG_ERR, err); 411 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 412 goto cleanup; 413 } 414 } 415 416 /* now write it back out */ 417 log_msg(LOG_MSG_DEBUG, "Closing keystore"); 418 set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT); 419 set_passphrase_passarg(passarg); 420 if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) { 421 log_pkgerr(LOG_MSG_ERR, err); 422 log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile); 423 goto cleanup; 424 } 425 426 if (trusted) { 427 log_msg(LOG_MSG_INFO, MSG_TRUSTED, infile); 428 } else { 429 log_msg(LOG_MSG_INFO, MSG_ADDED, infile, alias); 430 } 431 432 ret = 0; 433 434 /* fallthrough intentional */ 435 cleanup: 436 if (err != NULL) 437 pkgerr_free(err); 438 439 if (certfile != NULL) 440 (void) fclose(certfile); 441 442 if (keyfile != NULL) 443 (void) fclose(keyfile); 444 445 return (ret); 446 } 447 448 /* Asks user to verify certificate data before proceeding */ 449 static VerifyStatus verify_trust(X509 *cert) 450 { 451 char vfy_trust = 'y'; 452 VerifyStatus ret = Accept; 453 PKG_ERR *err; 454 UI *ui = NULL; 455 456 err = pkgerr_new(); 457 /* print cert data */ 458 if (print_cert(err, cert, KEYSTORE_FORMAT_TEXT, 459 get_subject_display_name(cert), B_TRUE, stdout) != 0) { 460 log_pkgerr(LOG_MSG_ERR, err); 461 ret = VerifyFailed; 462 goto cleanup; 463 } 464 465 if ((ui = UI_new()) == NULL) { 466 log_msg(LOG_MSG_ERR, MSG_MEM); 467 ret = VerifyFailed; 468 goto cleanup; 469 } 470 471 /* 472 * The prompt is internationalized, but the valid 473 * response values are fixed, to avoid any complex 474 * multibyte processing that results in bugs 475 */ 476 if (UI_add_input_boolean(ui, MSG_VERIFY_TRUST, 477 "", 478 "yY", "nN", 479 UI_INPUT_FLAG_ECHO, &vfy_trust) <= 0) { 480 log_msg(LOG_MSG_ERR, MSG_MEM); 481 ret = VerifyFailed; 482 goto cleanup; 483 } 484 485 if (UI_process(ui) != 0) { 486 log_msg(LOG_MSG_ERR, MSG_MEM); 487 ret = VerifyFailed; 488 goto cleanup; 489 } 490 491 if (vfy_trust != 'y') { 492 ret = Reject; 493 goto cleanup; 494 } 495 496 /* 497 * if the cert does not appear to be a CA cert 498 * r is not self-signed, verify that as well 499 */ 500 if (!is_ca_cert(cert)) { 501 UI_free(ui); 502 if ((ui = UI_new()) == NULL) { 503 log_msg(LOG_MSG_ERR, MSG_MEM); 504 ret = VerifyFailed; 505 goto cleanup; 506 } 507 508 if (UI_add_input_boolean(ui, 509 MSG_VERIFY_NOT_CA, 510 "", 511 "yY", "nN", 512 UI_INPUT_FLAG_ECHO, &vfy_trust) <= 0) { 513 ret = VerifyFailed; 514 goto cleanup; 515 } 516 517 if (UI_process(ui) != 0) { 518 log_msg(LOG_MSG_ERR, MSG_MEM); 519 ret = VerifyFailed; 520 goto cleanup; 521 } 522 523 if (vfy_trust != 'y') { 524 ret = Reject; 525 goto cleanup; 526 } 527 } 528 529 cleanup: 530 if (ui != NULL) 531 UI_free(ui); 532 533 if (err != NULL) 534 pkgerr_free(err); 535 536 return (ret); 537 } 538 /* 539 * Name: is_ca_cert 540 * Desc: Determines if a given certificate has the attributes 541 * of a CA certificate 542 * Returns: B_TRUE if certificate has attributes of a CA cert 543 * B_FALSE otherwise 544 */ 545 static boolean_t 546 is_ca_cert(X509 *x) 547 { 548 549 /* 550 * X509_check_purpose causes the extensions that we 551 * care about to be decoded and stored in the X509 552 * structure, so we must call it first 553 * before checking for CA extensions in the X509 554 * structure 555 */ 556 (void) X509_check_purpose(x, X509_PURPOSE_ANY, 0); 557 558 /* keyUsage if present should allow cert signing */ 559 if ((x->ex_flags & EXFLAG_KUSAGE) && 560 !(x->ex_kusage & KU_KEY_CERT_SIGN)) { 561 return (B_FALSE); 562 } 563 564 /* If basicConstraints says not a CA then say so */ 565 if (x->ex_flags & EXFLAG_BCONS) { 566 if (!(x->ex_flags & EXFLAG_CA)) { 567 return (B_FALSE); 568 } 569 } 570 571 /* no explicit not-a-CA flags set, so assume that it is */ 572 return (B_TRUE); 573 } 574