1 /* $OpenBSD: ssh-add.c,v 1.171 2024/01/08 00:30:39 djm Exp $ */ 2 /* 3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5 * All rights reserved 6 * Adds an identity to the authentication server, or removes an identity. 7 * 8 * As far as I am concerned, the code I have written for this software 9 * can be used freely for any purpose. Any derived versions of this 10 * software must be clearly marked as such, and if the derived work is 11 * incompatible with the protocol description in the RFC file, it must be 12 * called by a name other than "ssh" or "Secure Shell". 13 * 14 * SSH2 implementation, 15 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 41 #ifdef WITH_OPENSSL 42 #include <openssl/evp.h> 43 #endif 44 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <stdarg.h> 52 #include <unistd.h> 53 #include <limits.h> 54 55 #include "xmalloc.h" 56 #include "ssh.h" 57 #include "log.h" 58 #include "sshkey.h" 59 #include "sshbuf.h" 60 #include "authfd.h" 61 #include "authfile.h" 62 #include "pathnames.h" 63 #include "misc.h" 64 #include "ssherr.h" 65 #include "digest.h" 66 #include "ssh-sk.h" 67 #include "sk-api.h" 68 #include "hostfile.h" 69 70 /* argv0 */ 71 extern char *__progname; 72 73 /* Default files to add */ 74 static char *default_files[] = { 75 _PATH_SSH_CLIENT_ID_RSA, 76 _PATH_SSH_CLIENT_ID_ECDSA, 77 _PATH_SSH_CLIENT_ID_ECDSA_SK, 78 _PATH_SSH_CLIENT_ID_ED25519, 79 _PATH_SSH_CLIENT_ID_ED25519_SK, 80 _PATH_SSH_CLIENT_ID_XMSS, 81 _PATH_SSH_CLIENT_ID_DSA, 82 NULL 83 }; 84 85 static int fingerprint_hash = SSH_FP_HASH_DEFAULT; 86 87 /* Default lifetime (0 == forever) */ 88 static int lifetime = 0; 89 90 /* User has to confirm key use */ 91 static int confirm = 0; 92 93 /* Maximum number of signatures (XMSS) */ 94 static u_int maxsign = 0; 95 static u_int minleft = 0; 96 97 /* we keep a cache of one passphrase */ 98 static char *pass = NULL; 99 static void 100 clear_pass(void) 101 { 102 if (pass) { 103 freezero(pass, strlen(pass)); 104 pass = NULL; 105 } 106 } 107 108 static int 109 delete_one(int agent_fd, const struct sshkey *key, const char *comment, 110 const char *path, int qflag) 111 { 112 int r; 113 114 if ((r = ssh_remove_identity(agent_fd, key)) != 0) { 115 fprintf(stderr, "Could not remove identity \"%s\": %s\n", 116 path, ssh_err(r)); 117 return r; 118 } 119 if (!qflag) { 120 fprintf(stderr, "Identity removed: %s %s (%s)\n", path, 121 sshkey_type(key), comment ? comment : "no comment"); 122 } 123 return 0; 124 } 125 126 static int 127 delete_stdin(int agent_fd, int qflag, int key_only, int cert_only) 128 { 129 char *line = NULL, *cp; 130 size_t linesize = 0; 131 struct sshkey *key = NULL; 132 int lnum = 0, r, ret = -1; 133 134 while (getline(&line, &linesize, stdin) != -1) { 135 lnum++; 136 sshkey_free(key); 137 key = NULL; 138 line[strcspn(line, "\n")] = '\0'; 139 cp = line + strspn(line, " \t"); 140 if (*cp == '#' || *cp == '\0') 141 continue; 142 if ((key = sshkey_new(KEY_UNSPEC)) == NULL) 143 fatal_f("sshkey_new"); 144 if ((r = sshkey_read(key, &cp)) != 0) { 145 error_r(r, "(stdin):%d: invalid key", lnum); 146 continue; 147 } 148 if ((!key_only && !cert_only) || 149 (key_only && !sshkey_is_cert(key)) || 150 (cert_only && sshkey_is_cert(key))) { 151 if (delete_one(agent_fd, key, cp, 152 "(stdin)", qflag) == 0) 153 ret = 0; 154 } 155 } 156 sshkey_free(key); 157 free(line); 158 return ret; 159 } 160 161 static int 162 delete_file(int agent_fd, const char *filename, int key_only, 163 int cert_only, int qflag) 164 { 165 struct sshkey *public, *cert = NULL; 166 char *certpath = NULL, *comment = NULL; 167 int r, ret = -1; 168 169 if (strcmp(filename, "-") == 0) 170 return delete_stdin(agent_fd, qflag, key_only, cert_only); 171 172 if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { 173 printf("Bad key file %s: %s\n", filename, ssh_err(r)); 174 return -1; 175 } 176 if ((!key_only && !cert_only) || 177 (key_only && !sshkey_is_cert(public)) || 178 (cert_only && sshkey_is_cert(public))) { 179 if (delete_one(agent_fd, public, comment, filename, qflag) == 0) 180 ret = 0; 181 } 182 183 if (key_only) 184 goto out; 185 186 /* Now try to delete the corresponding certificate too */ 187 free(comment); 188 comment = NULL; 189 xasprintf(&certpath, "%s-cert.pub", filename); 190 if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) { 191 if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 192 error_r(r, "Failed to load certificate \"%s\"", certpath); 193 goto out; 194 } 195 196 if (!sshkey_equal_public(cert, public)) 197 fatal("Certificate %s does not match private key %s", 198 certpath, filename); 199 200 if (delete_one(agent_fd, cert, comment, certpath, qflag) == 0) 201 ret = 0; 202 203 out: 204 sshkey_free(cert); 205 sshkey_free(public); 206 free(certpath); 207 free(comment); 208 209 return ret; 210 } 211 212 /* Send a request to remove all identities. */ 213 static int 214 delete_all(int agent_fd, int qflag) 215 { 216 int ret = -1; 217 218 /* 219 * Since the agent might be forwarded, old or non-OpenSSH, when asked 220 * to remove all keys, attempt to remove both protocol v.1 and v.2 221 * keys. 222 */ 223 if (ssh_remove_all_identities(agent_fd, 2) == 0) 224 ret = 0; 225 /* ignore error-code for ssh1 */ 226 ssh_remove_all_identities(agent_fd, 1); 227 228 if (ret != 0) 229 fprintf(stderr, "Failed to remove all identities.\n"); 230 else if (!qflag) 231 fprintf(stderr, "All identities removed.\n"); 232 233 return ret; 234 } 235 236 static int 237 add_file(int agent_fd, const char *filename, int key_only, int cert_only, 238 int qflag, const char *skprovider, 239 struct dest_constraint **dest_constraints, 240 size_t ndest_constraints) 241 { 242 struct sshkey *private, *cert; 243 char *comment = NULL; 244 char msg[1024], *certpath = NULL; 245 int r, fd, ret = -1; 246 size_t i; 247 u_int32_t left; 248 struct sshbuf *keyblob; 249 struct ssh_identitylist *idlist; 250 251 if (strcmp(filename, "-") == 0) { 252 fd = STDIN_FILENO; 253 filename = "(stdin)"; 254 } else if ((fd = open(filename, O_RDONLY)) == -1) { 255 perror(filename); 256 return -1; 257 } 258 259 /* 260 * Since we'll try to load a keyfile multiple times, permission errors 261 * will occur multiple times, so check perms first and bail if wrong. 262 */ 263 if (fd != STDIN_FILENO) { 264 if (sshkey_perm_ok(fd, filename) != 0) { 265 close(fd); 266 return -1; 267 } 268 } 269 if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) { 270 fprintf(stderr, "Error loading key \"%s\": %s\n", 271 filename, ssh_err(r)); 272 sshbuf_free(keyblob); 273 close(fd); 274 return -1; 275 } 276 close(fd); 277 278 /* At first, try empty passphrase */ 279 if ((r = sshkey_parse_private_fileblob(keyblob, "", &private, 280 &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 281 fprintf(stderr, "Error loading key \"%s\": %s\n", 282 filename, ssh_err(r)); 283 goto fail_load; 284 } 285 /* try last */ 286 if (private == NULL && pass != NULL) { 287 if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private, 288 &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 289 fprintf(stderr, "Error loading key \"%s\": %s\n", 290 filename, ssh_err(r)); 291 goto fail_load; 292 } 293 } 294 if (private == NULL) { 295 /* clear passphrase since it did not work */ 296 clear_pass(); 297 snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ", 298 filename, confirm ? " (will confirm each use)" : ""); 299 for (;;) { 300 pass = read_passphrase(msg, RP_ALLOW_STDIN); 301 if (strcmp(pass, "") == 0) 302 goto fail_load; 303 if ((r = sshkey_parse_private_fileblob(keyblob, pass, 304 &private, &comment)) == 0) 305 break; 306 else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 307 fprintf(stderr, 308 "Error loading key \"%s\": %s\n", 309 filename, ssh_err(r)); 310 fail_load: 311 clear_pass(); 312 sshbuf_free(keyblob); 313 return -1; 314 } 315 clear_pass(); 316 snprintf(msg, sizeof msg, 317 "Bad passphrase, try again for %s%s: ", filename, 318 confirm ? " (will confirm each use)" : ""); 319 } 320 } 321 if (comment == NULL || *comment == '\0') 322 comment = xstrdup(filename); 323 sshbuf_free(keyblob); 324 325 /* For XMSS */ 326 if ((r = sshkey_set_filename(private, filename)) != 0) { 327 fprintf(stderr, "Could not add filename to private key: %s (%s)\n", 328 filename, comment); 329 goto out; 330 } 331 if (maxsign && minleft && 332 (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) { 333 for (i = 0; i < idlist->nkeys; i++) { 334 if (!sshkey_equal_public(idlist->keys[i], private)) 335 continue; 336 left = sshkey_signatures_left(idlist->keys[i]); 337 if (left < minleft) { 338 fprintf(stderr, 339 "Only %d signatures left.\n", left); 340 break; 341 } 342 fprintf(stderr, "Skipping update: "); 343 if (left == minleft) { 344 fprintf(stderr, 345 "required signatures left (%d).\n", left); 346 } else { 347 fprintf(stderr, 348 "more signatures left (%d) than" 349 " required (%d).\n", left, minleft); 350 } 351 ssh_free_identitylist(idlist); 352 goto out; 353 } 354 ssh_free_identitylist(idlist); 355 } 356 357 if (sshkey_is_sk(private)) { 358 if (skprovider == NULL) { 359 fprintf(stderr, "Cannot load FIDO key %s " 360 "without provider\n", filename); 361 goto out; 362 } 363 } else { 364 /* Don't send provider constraint for other keys */ 365 skprovider = NULL; 366 } 367 368 if (!cert_only && 369 (r = ssh_add_identity_constrained(agent_fd, private, comment, 370 lifetime, confirm, maxsign, skprovider, 371 dest_constraints, ndest_constraints)) == 0) { 372 ret = 0; 373 if (!qflag) { 374 fprintf(stderr, "Identity added: %s (%s)\n", 375 filename, comment); 376 if (lifetime != 0) { 377 fprintf(stderr, 378 "Lifetime set to %d seconds\n", lifetime); 379 } 380 if (confirm != 0) { 381 fprintf(stderr, "The user must confirm " 382 "each use of the key\n"); 383 } 384 } 385 } else { 386 fprintf(stderr, "Could not add identity \"%s\": %s\n", 387 filename, ssh_err(r)); 388 } 389 390 /* Skip trying to load the cert if requested */ 391 if (key_only) 392 goto out; 393 394 /* Now try to add the certificate flavour too */ 395 xasprintf(&certpath, "%s-cert.pub", filename); 396 if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { 397 if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 398 error_r(r, "Failed to load certificate \"%s\"", 399 certpath); 400 goto out; 401 } 402 403 if (!sshkey_equal_public(cert, private)) { 404 error("Certificate %s does not match private key %s", 405 certpath, filename); 406 sshkey_free(cert); 407 goto out; 408 } 409 410 /* Graft with private bits */ 411 if ((r = sshkey_to_certified(private)) != 0) { 412 error_fr(r, "sshkey_to_certified"); 413 sshkey_free(cert); 414 goto out; 415 } 416 if ((r = sshkey_cert_copy(cert, private)) != 0) { 417 error_fr(r, "sshkey_cert_copy"); 418 sshkey_free(cert); 419 goto out; 420 } 421 sshkey_free(cert); 422 423 if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 424 lifetime, confirm, maxsign, skprovider, 425 dest_constraints, ndest_constraints)) != 0) { 426 error_r(r, "Certificate %s (%s) add failed", certpath, 427 private->cert->key_id); 428 goto out; 429 } 430 /* success */ 431 if (!qflag) { 432 fprintf(stderr, "Certificate added: %s (%s)\n", certpath, 433 private->cert->key_id); 434 if (lifetime != 0) { 435 fprintf(stderr, "Lifetime set to %d seconds\n", 436 lifetime); 437 } 438 if (confirm != 0) { 439 fprintf(stderr, "The user must confirm each use " 440 "of the key\n"); 441 } 442 } 443 444 out: 445 free(certpath); 446 free(comment); 447 sshkey_free(private); 448 449 return ret; 450 } 451 452 static int 453 update_card(int agent_fd, int add, const char *id, int qflag, 454 int key_only, int cert_only, 455 struct dest_constraint **dest_constraints, size_t ndest_constraints, 456 struct sshkey **certs, size_t ncerts) 457 { 458 char *pin = NULL; 459 int r, ret = -1; 460 461 if (key_only) 462 ncerts = 0; 463 464 if (add) { 465 if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", 466 RP_ALLOW_STDIN)) == NULL) 467 return -1; 468 } 469 470 if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, 471 lifetime, confirm, dest_constraints, ndest_constraints, 472 cert_only, certs, ncerts)) == 0) { 473 ret = 0; 474 if (!qflag) { 475 fprintf(stderr, "Card %s: %s\n", 476 add ? "added" : "removed", id); 477 } 478 } else { 479 fprintf(stderr, "Could not %s card \"%s\": %s\n", 480 add ? "add" : "remove", id, ssh_err(r)); 481 ret = -1; 482 } 483 free(pin); 484 return ret; 485 } 486 487 static int 488 test_key(int agent_fd, const char *filename) 489 { 490 struct sshkey *key = NULL; 491 u_char *sig = NULL; 492 const char *alg = NULL; 493 size_t slen = 0; 494 int r, ret = -1; 495 char data[1024]; 496 497 if ((r = sshkey_load_public(filename, &key, NULL)) != 0) { 498 error_r(r, "Couldn't read public key %s", filename); 499 return -1; 500 } 501 if (sshkey_type_plain(key->type) == KEY_RSA) 502 alg = "rsa-sha2-256"; 503 arc4random_buf(data, sizeof(data)); 504 if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data), 505 alg, 0)) != 0) { 506 error_r(r, "Agent signature failed for %s", filename); 507 goto done; 508 } 509 if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), 510 alg, 0, NULL)) != 0) { 511 error_r(r, "Signature verification failed for %s", filename); 512 goto done; 513 } 514 /* success */ 515 ret = 0; 516 done: 517 free(sig); 518 sshkey_free(key); 519 return ret; 520 } 521 522 static int 523 list_identities(int agent_fd, int do_fp) 524 { 525 char *fp; 526 int r; 527 struct ssh_identitylist *idlist; 528 u_int32_t left; 529 size_t i; 530 531 if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { 532 if (r != SSH_ERR_AGENT_NO_IDENTITIES) 533 fprintf(stderr, "error fetching identities: %s\n", 534 ssh_err(r)); 535 else 536 printf("The agent has no identities.\n"); 537 return -1; 538 } 539 for (i = 0; i < idlist->nkeys; i++) { 540 if (do_fp) { 541 fp = sshkey_fingerprint(idlist->keys[i], 542 fingerprint_hash, SSH_FP_DEFAULT); 543 printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]), 544 fp == NULL ? "(null)" : fp, idlist->comments[i], 545 sshkey_type(idlist->keys[i])); 546 free(fp); 547 } else { 548 if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) { 549 fprintf(stderr, "sshkey_write: %s\n", 550 ssh_err(r)); 551 continue; 552 } 553 fprintf(stdout, " %s", idlist->comments[i]); 554 left = sshkey_signatures_left(idlist->keys[i]); 555 if (left > 0) 556 fprintf(stdout, 557 " [signatures left %d]", left); 558 fprintf(stdout, "\n"); 559 } 560 } 561 ssh_free_identitylist(idlist); 562 return 0; 563 } 564 565 static int 566 lock_agent(int agent_fd, int lock) 567 { 568 char prompt[100], *p1, *p2; 569 int r, passok = 1, ret = -1; 570 571 strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); 572 p1 = read_passphrase(prompt, RP_ALLOW_STDIN); 573 if (lock) { 574 strlcpy(prompt, "Again: ", sizeof prompt); 575 p2 = read_passphrase(prompt, RP_ALLOW_STDIN); 576 if (strcmp(p1, p2) != 0) { 577 fprintf(stderr, "Passwords do not match.\n"); 578 passok = 0; 579 } 580 freezero(p2, strlen(p2)); 581 } 582 if (passok) { 583 if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) { 584 fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); 585 ret = 0; 586 } else { 587 fprintf(stderr, "Failed to %slock agent: %s\n", 588 lock ? "" : "un", ssh_err(r)); 589 } 590 } 591 freezero(p1, strlen(p1)); 592 return (ret); 593 } 594 595 static int 596 load_resident_keys(int agent_fd, const char *skprovider, int qflag, 597 struct dest_constraint **dest_constraints, size_t ndest_constraints) 598 { 599 struct sshsk_resident_key **srks; 600 size_t nsrks, i; 601 struct sshkey *key; 602 int r, ok = 0; 603 char *fp; 604 605 pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); 606 if ((r = sshsk_load_resident(skprovider, NULL, pass, 0, 607 &srks, &nsrks)) != 0) { 608 error_r(r, "Unable to load resident keys"); 609 return r; 610 } 611 for (i = 0; i < nsrks; i++) { 612 key = srks[i]->key; 613 if ((fp = sshkey_fingerprint(key, 614 fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 615 fatal_f("sshkey_fingerprint failed"); 616 if ((r = ssh_add_identity_constrained(agent_fd, key, "", 617 lifetime, confirm, maxsign, skprovider, 618 dest_constraints, ndest_constraints)) != 0) { 619 error("Unable to add key %s %s", 620 sshkey_type(key), fp); 621 free(fp); 622 ok = r; 623 continue; 624 } 625 if (ok == 0) 626 ok = 1; 627 if (!qflag) { 628 fprintf(stderr, "Resident identity added: %s %s\n", 629 sshkey_type(key), fp); 630 if (lifetime != 0) { 631 fprintf(stderr, 632 "Lifetime set to %d seconds\n", lifetime); 633 } 634 if (confirm != 0) { 635 fprintf(stderr, "The user must confirm " 636 "each use of the key\n"); 637 } 638 } 639 free(fp); 640 } 641 sshsk_free_resident_keys(srks, nsrks); 642 if (nsrks == 0) 643 return SSH_ERR_KEY_NOT_FOUND; 644 return ok == 1 ? 0 : ok; 645 } 646 647 static int 648 do_file(int agent_fd, int deleting, int key_only, int cert_only, 649 char *file, int qflag, const char *skprovider, 650 struct dest_constraint **dest_constraints, size_t ndest_constraints) 651 { 652 if (deleting) { 653 if (delete_file(agent_fd, file, key_only, 654 cert_only, qflag) == -1) 655 return -1; 656 } else { 657 if (add_file(agent_fd, file, key_only, cert_only, qflag, 658 skprovider, dest_constraints, ndest_constraints) == -1) 659 return -1; 660 } 661 return 0; 662 } 663 664 /* Append string 's' to a NULL-terminated array of strings */ 665 static void 666 stringlist_append(char ***listp, const char *s) 667 { 668 size_t i = 0; 669 670 if (*listp == NULL) 671 *listp = xcalloc(2, sizeof(**listp)); 672 else { 673 for (i = 0; (*listp)[i] != NULL; i++) 674 ; /* count */ 675 *listp = xrecallocarray(*listp, i + 1, i + 2, sizeof(**listp)); 676 } 677 (*listp)[i] = xstrdup(s); 678 } 679 680 static void 681 parse_dest_constraint_hop(const char *s, struct dest_constraint_hop *dch, 682 char **hostkey_files) 683 { 684 char *user = NULL, *host, *os, *path; 685 size_t i; 686 struct hostkeys *hostkeys; 687 const struct hostkey_entry *hke; 688 int r, want_ca; 689 690 memset(dch, '\0', sizeof(*dch)); 691 os = xstrdup(s); 692 if ((host = strchr(os, '@')) == NULL) 693 host = os; 694 else { 695 *host++ = '\0'; 696 user = os; 697 } 698 cleanhostname(host); 699 /* Trivial case: username@ (all hosts) */ 700 if (*host == '\0') { 701 if (user == NULL) { 702 fatal("Invalid key destination constraint \"%s\": " 703 "does not specify user or host", s); 704 } 705 dch->user = xstrdup(user); 706 /* other fields left blank */ 707 free(os); 708 return; 709 } 710 if (hostkey_files == NULL) 711 fatal_f("no hostkey files"); 712 /* Otherwise we need to look up the keys for this hostname */ 713 hostkeys = init_hostkeys(); 714 for (i = 0; hostkey_files[i]; i++) { 715 path = tilde_expand_filename(hostkey_files[i], getuid()); 716 debug2_f("looking up host keys for \"%s\" in %s", host, path); 717 load_hostkeys(hostkeys, host, path, 0); 718 free(path); 719 } 720 dch->user = user == NULL ? NULL : xstrdup(user); 721 dch->hostname = xstrdup(host); 722 for (i = 0; i < hostkeys->num_entries; i++) { 723 hke = hostkeys->entries + i; 724 want_ca = hke->marker == MRK_CA; 725 if (hke->marker != MRK_NONE && !want_ca) 726 continue; 727 debug3_f("%s%s%s: adding %s %skey from %s:%lu as key %u", 728 user == NULL ? "": user, user == NULL ? "" : "@", 729 host, sshkey_type(hke->key), want_ca ? "CA " : "", 730 hke->file, hke->line, dch->nkeys); 731 dch->keys = xrecallocarray(dch->keys, dch->nkeys, 732 dch->nkeys + 1, sizeof(*dch->keys)); 733 dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys, 734 dch->nkeys + 1, sizeof(*dch->key_is_ca)); 735 if ((r = sshkey_from_private(hke->key, 736 &(dch->keys[dch->nkeys]))) != 0) 737 fatal_fr(r, "sshkey_from_private"); 738 dch->key_is_ca[dch->nkeys] = want_ca; 739 dch->nkeys++; 740 } 741 if (dch->nkeys == 0) 742 fatal("No host keys found for destination \"%s\"", host); 743 free_hostkeys(hostkeys); 744 free(os); 745 return; 746 } 747 748 static void 749 parse_dest_constraint(const char *s, struct dest_constraint ***dcp, 750 size_t *ndcp, char **hostkey_files) 751 { 752 struct dest_constraint *dc; 753 char *os, *cp; 754 755 dc = xcalloc(1, sizeof(*dc)); 756 os = xstrdup(s); 757 if ((cp = strchr(os, '>')) == NULL) { 758 /* initial hop; no 'from' hop specified */ 759 parse_dest_constraint_hop(os, &dc->to, hostkey_files); 760 } else { 761 /* two hops specified */ 762 *(cp++) = '\0'; 763 parse_dest_constraint_hop(os, &dc->from, hostkey_files); 764 parse_dest_constraint_hop(cp, &dc->to, hostkey_files); 765 if (dc->from.user != NULL) { 766 fatal("Invalid key constraint %s: cannot specify " 767 "user on 'from' host", os); 768 } 769 } 770 /* XXX eliminate or error on duplicates */ 771 debug2_f("constraint %zu: %s%s%s (%u keys) > %s%s%s (%u keys)", *ndcp, 772 dc->from.user ? dc->from.user : "", dc->from.user ? "@" : "", 773 dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys, 774 dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "", 775 dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys); 776 *dcp = xrecallocarray(*dcp, *ndcp, *ndcp + 1, sizeof(**dcp)); 777 (*dcp)[(*ndcp)++] = dc; 778 free(os); 779 } 780 781 782 static void 783 usage(void) 784 { 785 fprintf(stderr, 786 "usage: ssh-add [-CcDdKkLlqvXx] [-E fingerprint_hash] [-H hostkey_file]\n" 787 " [-h destination_constraint] [-S provider] [-t life]\n" 788 #ifdef WITH_XMSS 789 " [-M maxsign] [-m minleft]\n" 790 #endif 791 " [file ...]\n" 792 " ssh-add -s pkcs11 [-Cv] [certificate ...]\n" 793 " ssh-add -e pkcs11\n" 794 " ssh-add -T pubkey ...\n" 795 ); 796 } 797 798 int 799 main(int argc, char **argv) 800 { 801 extern char *optarg; 802 extern int optind; 803 int agent_fd; 804 char *pkcs11provider = NULL, *skprovider = NULL; 805 char **dest_constraint_strings = NULL, **hostkey_files = NULL; 806 int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0; 807 int do_download = 0, xflag = 0, lflag = 0, Dflag = 0; 808 int qflag = 0, Tflag = 0; 809 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 810 LogLevel log_level = SYSLOG_LEVEL_INFO; 811 struct sshkey *k, **certs = NULL; 812 struct dest_constraint **dest_constraints = NULL; 813 size_t ndest_constraints = 0, ncerts = 0; 814 815 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 816 sanitise_stdfd(); 817 818 #ifdef WITH_OPENSSL 819 OpenSSL_add_all_algorithms(); 820 #endif 821 log_init(__progname, log_level, log_facility, 1); 822 823 setvbuf(stdout, NULL, _IOLBF, 0); 824 825 /* First, get a connection to the authentication agent. */ 826 switch (r = ssh_get_authentication_socket(&agent_fd)) { 827 case 0: 828 break; 829 case SSH_ERR_AGENT_NOT_PRESENT: 830 fprintf(stderr, "Could not open a connection to your " 831 "authentication agent.\n"); 832 exit(2); 833 default: 834 fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r)); 835 exit(2); 836 } 837 838 skprovider = getenv("SSH_SK_PROVIDER"); 839 840 while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) { 841 switch (ch) { 842 case 'v': 843 if (log_level == SYSLOG_LEVEL_INFO) 844 log_level = SYSLOG_LEVEL_DEBUG1; 845 else if (log_level < SYSLOG_LEVEL_DEBUG3) 846 log_level++; 847 break; 848 case 'E': 849 fingerprint_hash = ssh_digest_alg_by_name(optarg); 850 if (fingerprint_hash == -1) 851 fatal("Invalid hash algorithm \"%s\"", optarg); 852 break; 853 case 'H': 854 stringlist_append(&hostkey_files, optarg); 855 break; 856 case 'h': 857 stringlist_append(&dest_constraint_strings, optarg); 858 break; 859 case 'k': 860 key_only = 1; 861 break; 862 case 'C': 863 cert_only = 1; 864 break; 865 case 'K': 866 do_download = 1; 867 break; 868 case 'l': 869 case 'L': 870 if (lflag != 0) 871 fatal("-%c flag already specified", lflag); 872 lflag = ch; 873 break; 874 case 'x': 875 case 'X': 876 if (xflag != 0) 877 fatal("-%c flag already specified", xflag); 878 xflag = ch; 879 break; 880 case 'c': 881 confirm = 1; 882 break; 883 case 'm': 884 minleft = (u_int)strtonum(optarg, 1, UINT_MAX, NULL); 885 if (minleft == 0) { 886 usage(); 887 ret = 1; 888 goto done; 889 } 890 break; 891 case 'M': 892 maxsign = (u_int)strtonum(optarg, 1, UINT_MAX, NULL); 893 if (maxsign == 0) { 894 usage(); 895 ret = 1; 896 goto done; 897 } 898 break; 899 case 'd': 900 deleting = 1; 901 break; 902 case 'D': 903 Dflag = 1; 904 break; 905 case 's': 906 pkcs11provider = optarg; 907 break; 908 case 'S': 909 skprovider = optarg; 910 break; 911 case 'e': 912 deleting = 1; 913 pkcs11provider = optarg; 914 break; 915 case 't': 916 if ((lifetime = convtime(optarg)) == -1 || 917 lifetime < 0 || (u_long)lifetime > UINT32_MAX) { 918 fprintf(stderr, "Invalid lifetime\n"); 919 ret = 1; 920 goto done; 921 } 922 break; 923 case 'q': 924 qflag = 1; 925 break; 926 case 'T': 927 Tflag = 1; 928 break; 929 default: 930 usage(); 931 ret = 1; 932 goto done; 933 } 934 } 935 log_init(__progname, log_level, log_facility, 1); 936 937 if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) 938 fatal("Invalid combination of actions"); 939 else if (xflag) { 940 if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) 941 ret = 1; 942 goto done; 943 } else if (lflag) { 944 if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1) 945 ret = 1; 946 goto done; 947 } else if (Dflag) { 948 if (delete_all(agent_fd, qflag) == -1) 949 ret = 1; 950 goto done; 951 } 952 953 if (skprovider == NULL) 954 skprovider = "internal"; 955 if (hostkey_files == NULL) { 956 /* use defaults from readconf.c */ 957 stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE); 958 stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE2); 959 stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE); 960 stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE2); 961 } 962 if (dest_constraint_strings != NULL) { 963 for (i = 0; dest_constraint_strings[i] != NULL; i++) { 964 parse_dest_constraint(dest_constraint_strings[i], 965 &dest_constraints, &ndest_constraints, hostkey_files); 966 } 967 } 968 969 argc -= optind; 970 argv += optind; 971 if (Tflag) { 972 if (argc <= 0) 973 fatal("no keys to test"); 974 for (r = i = 0; i < argc; i++) 975 r |= test_key(agent_fd, argv[i]); 976 ret = r == 0 ? 0 : 1; 977 goto done; 978 } 979 if (pkcs11provider != NULL) { 980 for (i = 0; i < argc; i++) { 981 if ((r = sshkey_load_public(argv[i], &k, NULL)) != 0) 982 fatal_fr(r, "load certificate %s", argv[i]); 983 certs = xrecallocarray(certs, ncerts, ncerts + 1, 984 sizeof(*certs)); 985 debug2("%s: %s", argv[i], sshkey_ssh_name(k)); 986 certs[ncerts++] = k; 987 } 988 debug2_f("loaded %zu certificates", ncerts); 989 if (update_card(agent_fd, !deleting, pkcs11provider, 990 qflag, key_only, cert_only, 991 dest_constraints, ndest_constraints, 992 certs, ncerts) == -1) 993 ret = 1; 994 goto done; 995 } 996 if (do_download) { 997 if (skprovider == NULL) 998 fatal("Cannot download keys without provider"); 999 if (load_resident_keys(agent_fd, skprovider, qflag, 1000 dest_constraints, ndest_constraints) != 0) 1001 ret = 1; 1002 goto done; 1003 } 1004 if (argc == 0) { 1005 char buf[PATH_MAX]; 1006 struct passwd *pw; 1007 struct stat st; 1008 int count = 0; 1009 1010 if ((pw = getpwuid(getuid())) == NULL) { 1011 fprintf(stderr, "No user found with uid %u\n", 1012 (u_int)getuid()); 1013 ret = 1; 1014 goto done; 1015 } 1016 1017 for (i = 0; default_files[i]; i++) { 1018 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, 1019 default_files[i]); 1020 if (stat(buf, &st) == -1) 1021 continue; 1022 if (do_file(agent_fd, deleting, key_only, cert_only, 1023 buf, qflag, skprovider, 1024 dest_constraints, ndest_constraints) == -1) 1025 ret = 1; 1026 else 1027 count++; 1028 } 1029 if (count == 0) 1030 ret = 1; 1031 } else { 1032 for (i = 0; i < argc; i++) { 1033 if (do_file(agent_fd, deleting, key_only, cert_only, 1034 argv[i], qflag, skprovider, 1035 dest_constraints, ndest_constraints) == -1) 1036 ret = 1; 1037 } 1038 } 1039 done: 1040 clear_pass(); 1041 ssh_close_authentication_socket(agent_fd); 1042 return ret; 1043 } 1044