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