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