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