1 /* $NetBSD: ssh-add.c,v 1.26 2022/02/23 19:07:20 christos Exp $ */ 2 /* $OpenBSD: ssh-add.c,v 1.165 2022/02/04 02:49:17 dtucker 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.26 2022/02/23 19:07:20 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); 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 size_t slen = 0; 477 int r, ret = -1; 478 u_char data[1024]; 479 480 if ((r = sshkey_load_public(filename, &key, NULL)) != 0) { 481 error_r(r, "Couldn't read public key %s", filename); 482 return -1; 483 } 484 arc4random_buf(data, sizeof(data)); 485 if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data), 486 NULL, 0)) != 0) { 487 error_r(r, "Agent signature failed for %s", filename); 488 goto done; 489 } 490 if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), 491 NULL, 0, NULL)) != 0) { 492 error_r(r, "Signature verification failed for %s", filename); 493 goto done; 494 } 495 /* success */ 496 ret = 0; 497 done: 498 free(sig); 499 sshkey_free(key); 500 return ret; 501 } 502 503 static int 504 list_identities(int agent_fd, int do_fp) 505 { 506 char *fp; 507 int r; 508 struct ssh_identitylist *idlist; 509 u_int32_t left; 510 size_t i; 511 512 if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { 513 if (r != SSH_ERR_AGENT_NO_IDENTITIES) 514 fprintf(stderr, "error fetching identities: %s\n", 515 ssh_err(r)); 516 else 517 printf("The agent has no identities.\n"); 518 return -1; 519 } 520 for (i = 0; i < idlist->nkeys; i++) { 521 if (do_fp) { 522 fp = sshkey_fingerprint(idlist->keys[i], 523 fingerprint_hash, SSH_FP_DEFAULT); 524 printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]), 525 fp == NULL ? "(null)" : fp, idlist->comments[i], 526 sshkey_type(idlist->keys[i])); 527 free(fp); 528 } else { 529 if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) { 530 fprintf(stderr, "sshkey_write: %s\n", 531 ssh_err(r)); 532 continue; 533 } 534 fprintf(stdout, " %s", idlist->comments[i]); 535 left = sshkey_signatures_left(idlist->keys[i]); 536 if (left > 0) 537 fprintf(stdout, 538 " [signatures left %d]", left); 539 fprintf(stdout, "\n"); 540 } 541 } 542 ssh_free_identitylist(idlist); 543 return 0; 544 } 545 546 static int 547 lock_agent(int agent_fd, int lock) 548 { 549 char prompt[100], *p1, *p2; 550 int r, passok = 1, ret = -1; 551 552 strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); 553 p1 = read_passphrase(prompt, RP_ALLOW_STDIN); 554 if (lock) { 555 strlcpy(prompt, "Again: ", sizeof prompt); 556 p2 = read_passphrase(prompt, RP_ALLOW_STDIN); 557 if (strcmp(p1, p2) != 0) { 558 fprintf(stderr, "Passwords do not match.\n"); 559 passok = 0; 560 } 561 freezero(p2, strlen(p2)); 562 } 563 if (passok) { 564 if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) { 565 fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); 566 ret = 0; 567 } else { 568 fprintf(stderr, "Failed to %slock agent: %s\n", 569 lock ? "" : "un", ssh_err(r)); 570 } 571 } 572 freezero(p1, strlen(p1)); 573 return (ret); 574 } 575 576 static int 577 load_resident_keys(int agent_fd, const char *skprovider, int qflag, 578 struct dest_constraint **dest_constraints, size_t ndest_constraints) 579 { 580 struct sshsk_resident_key **srks; 581 size_t nsrks, i; 582 struct sshkey *key; 583 int r, ok = 0; 584 char *fp; 585 586 pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); 587 if ((r = sshsk_load_resident(skprovider, NULL, pass, 0, 588 &srks, &nsrks)) != 0) { 589 error_r(r, "Unable to load resident keys"); 590 return r; 591 } 592 for (i = 0; i < nsrks; i++) { 593 key = srks[i]->key; 594 if ((fp = sshkey_fingerprint(key, 595 fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 596 fatal_f("sshkey_fingerprint failed"); 597 if ((r = ssh_add_identity_constrained(agent_fd, key, "", 598 lifetime, confirm, maxsign, skprovider, 599 dest_constraints, ndest_constraints)) != 0) { 600 error("Unable to add key %s %s", 601 sshkey_type(key), fp); 602 free(fp); 603 ok = r; 604 continue; 605 } 606 if (ok == 0) 607 ok = 1; 608 if (!qflag) { 609 fprintf(stderr, "Resident identity added: %s %s\n", 610 sshkey_type(key), fp); 611 if (lifetime != 0) { 612 fprintf(stderr, 613 "Lifetime set to %d seconds\n", lifetime); 614 } 615 if (confirm != 0) { 616 fprintf(stderr, "The user must confirm " 617 "each use of the key\n"); 618 } 619 } 620 free(fp); 621 } 622 sshsk_free_resident_keys(srks, nsrks); 623 if (nsrks == 0) 624 return SSH_ERR_KEY_NOT_FOUND; 625 return ok == 1 ? 0 : ok; 626 } 627 628 static int 629 do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, 630 const char *skprovider, struct dest_constraint **dest_constraints, 631 size_t ndest_constraints) 632 { 633 if (deleting) { 634 if (delete_file(agent_fd, file, key_only, qflag) == -1) 635 return -1; 636 } else { 637 if (add_file(agent_fd, file, key_only, qflag, skprovider, 638 dest_constraints, ndest_constraints) == -1) 639 return -1; 640 } 641 return 0; 642 } 643 644 /* Append string 's' to a NULL-terminated array of strings */ 645 static void 646 stringlist_append(char ***listp, const char *s) 647 { 648 size_t i = 0; 649 650 if (*listp == NULL) 651 *listp = xcalloc(2, sizeof(**listp)); 652 else { 653 for (i = 0; (*listp)[i] != NULL; i++) 654 ; /* count */ 655 *listp = xrecallocarray(*listp, i + 1, i + 2, sizeof(**listp)); 656 } 657 (*listp)[i] = xstrdup(s); 658 } 659 660 static void 661 parse_dest_constraint_hop(const char *s, struct dest_constraint_hop *dch, 662 char **hostkey_files) 663 { 664 char *user = NULL, *host, *os, *path; 665 size_t i; 666 struct hostkeys *hostkeys; 667 const struct hostkey_entry *hke; 668 int r, want_ca; 669 670 memset(dch, '\0', sizeof(*dch)); 671 os = xstrdup(s); 672 if ((host = strchr(os, '@')) == NULL) 673 host = os; 674 else { 675 *host++ = '\0'; 676 user = os; 677 } 678 cleanhostname(host); 679 /* Trivial case: username@ (all hosts) */ 680 if (*host == '\0') { 681 if (user == NULL) { 682 fatal("Invalid key destination constraint \"%s\": " 683 "does not specify user or host", s); 684 } 685 dch->user = xstrdup(user); 686 /* other fields left blank */ 687 free(os); 688 return; 689 } 690 if (hostkey_files == NULL) 691 fatal_f("no hostkey files"); 692 /* Otherwise we need to look up the keys for this hostname */ 693 hostkeys = init_hostkeys(); 694 for (i = 0; hostkey_files[i]; i++) { 695 path = tilde_expand_filename(hostkey_files[i], getuid()); 696 debug2_f("looking up host keys for \"%s\" in %s", host, path); 697 load_hostkeys(hostkeys, host, path, 0); 698 free(path); 699 } 700 dch->user = user == NULL ? NULL : xstrdup(user); 701 dch->hostname = xstrdup(host); 702 for (i = 0; i < hostkeys->num_entries; i++) { 703 hke = hostkeys->entries + i; 704 want_ca = hke->marker == MRK_CA; 705 if (hke->marker != MRK_NONE && !want_ca) 706 continue; 707 debug3_f("%s%s%s: adding %s %skey from %s:%lu as key %u", 708 user == NULL ? "": user, user == NULL ? "" : "@", 709 host, sshkey_type(hke->key), want_ca ? "CA " : "", 710 hke->file, hke->line, dch->nkeys); 711 dch->keys = xrecallocarray(dch->keys, dch->nkeys, 712 dch->nkeys + 1, sizeof(*dch->keys)); 713 dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys, 714 dch->nkeys + 1, sizeof(*dch->key_is_ca)); 715 if ((r = sshkey_from_private(hke->key, 716 &(dch->keys[dch->nkeys]))) != 0) 717 fatal_fr(r, "sshkey_from_private"); 718 dch->key_is_ca[dch->nkeys] = want_ca; 719 dch->nkeys++; 720 } 721 if (dch->nkeys == 0) 722 fatal("No host keys found for destination \"%s\"", host); 723 free_hostkeys(hostkeys); 724 free(os); 725 return; 726 } 727 728 static void 729 parse_dest_constraint(const char *s, struct dest_constraint ***dcp, 730 size_t *ndcp, char **hostkey_files) 731 { 732 struct dest_constraint *dc; 733 char *os, *cp; 734 735 dc = xcalloc(1, sizeof(*dc)); 736 os = xstrdup(s); 737 if ((cp = strchr(os, '>')) == NULL) { 738 /* initial hop; no 'from' hop specified */ 739 parse_dest_constraint_hop(os, &dc->to, hostkey_files); 740 } else { 741 /* two hops specified */ 742 *(cp++) = '\0'; 743 parse_dest_constraint_hop(os, &dc->from, hostkey_files); 744 parse_dest_constraint_hop(cp, &dc->to, hostkey_files); 745 if (dc->from.user != NULL) { 746 fatal("Invalid key constraint %s: cannot specify " 747 "user on 'from' host", os); 748 } 749 } 750 /* XXX eliminate or error on duplicates */ 751 debug2_f("constraint %zu: %s%s%s (%u keys) > %s%s%s (%u keys)", *ndcp, 752 dc->from.user ? dc->from.user : "", dc->from.user ? "@" : "", 753 dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys, 754 dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "", 755 dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys); 756 *dcp = xrecallocarray(*dcp, *ndcp, *ndcp + 1, sizeof(**dcp)); 757 (*dcp)[(*ndcp)++] = dc; 758 free(os); 759 } 760 761 762 static void 763 usage(void) 764 { 765 fprintf(stderr, 766 "usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-H hostkey_file]\n" 767 " [-h destination_constraint] [-S provider] [-t life]\n" 768 #ifdef WITH_XMSS 769 " [-M maxsign] [-m minleft]\n" 770 #endif 771 " [file ...]\n" 772 " ssh-add -s pkcs11\n" 773 " ssh-add -e pkcs11\n" 774 " ssh-add -T pubkey ...\n" 775 ); 776 } 777 778 int 779 main(int argc, char **argv) 780 { 781 extern char *optarg; 782 extern int optind; 783 int agent_fd; 784 char *pkcs11provider = NULL; 785 const char *skprovider = NULL; 786 char **dest_constraint_strings = NULL, **hostkey_files = NULL; 787 int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; 788 int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; 789 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 790 LogLevel log_level = SYSLOG_LEVEL_INFO; 791 struct dest_constraint **dest_constraints = NULL; 792 size_t ndest_constraints = 0; 793 794 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 795 sanitise_stdfd(); 796 797 #ifdef WITH_OPENSSL 798 OpenSSL_add_all_algorithms(); 799 #endif 800 log_init(__progname, log_level, log_facility, 1); 801 802 setvbuf(stdout, NULL, _IOLBF, 0); 803 804 /* First, get a connection to the authentication agent. */ 805 switch (r = ssh_get_authentication_socket(&agent_fd)) { 806 case 0: 807 break; 808 case SSH_ERR_AGENT_NOT_PRESENT: 809 fprintf(stderr, "Could not open a connection to your " 810 "authentication agent.\n"); 811 exit(2); 812 default: 813 fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r)); 814 exit(2); 815 } 816 817 skprovider = getenv("SSH_SK_PROVIDER"); 818 819 while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) { 820 switch (ch) { 821 case 'v': 822 if (log_level == SYSLOG_LEVEL_INFO) 823 log_level = SYSLOG_LEVEL_DEBUG1; 824 else if (log_level < SYSLOG_LEVEL_DEBUG3) 825 log_level++; 826 break; 827 case 'E': 828 fingerprint_hash = ssh_digest_alg_by_name(optarg); 829 if (fingerprint_hash == -1) 830 fatal("Invalid hash algorithm \"%s\"", optarg); 831 break; 832 case 'H': 833 stringlist_append(&hostkey_files, optarg); 834 break; 835 case 'h': 836 stringlist_append(&dest_constraint_strings, optarg); 837 break; 838 case 'k': 839 key_only = 1; 840 break; 841 case 'K': 842 do_download = 1; 843 break; 844 case 'l': 845 case 'L': 846 if (lflag != 0) 847 fatal("-%c flag already specified", lflag); 848 lflag = ch; 849 break; 850 case 'x': 851 case 'X': 852 if (xflag != 0) 853 fatal("-%c flag already specified", xflag); 854 xflag = ch; 855 break; 856 case 'c': 857 confirm = 1; 858 break; 859 case 'm': 860 minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL); 861 if (minleft == 0) { 862 usage(); 863 ret = 1; 864 goto done; 865 } 866 break; 867 case 'M': 868 maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL); 869 if (maxsign == 0) { 870 usage(); 871 ret = 1; 872 goto done; 873 } 874 break; 875 case 'd': 876 deleting = 1; 877 break; 878 case 'D': 879 Dflag = 1; 880 break; 881 case 's': 882 pkcs11provider = optarg; 883 break; 884 case 'S': 885 skprovider = optarg; 886 break; 887 case 'e': 888 deleting = 1; 889 pkcs11provider = optarg; 890 break; 891 case 't': 892 if ((lifetime = convtime(optarg)) == -1 || 893 lifetime < 0 || (u_long)lifetime > UINT32_MAX) { 894 fprintf(stderr, "Invalid lifetime\n"); 895 ret = 1; 896 goto done; 897 } 898 break; 899 case 'q': 900 qflag = 1; 901 break; 902 case 'T': 903 Tflag = 1; 904 break; 905 default: 906 usage(); 907 ret = 1; 908 goto done; 909 } 910 } 911 log_init(__progname, log_level, log_facility, 1); 912 913 if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) 914 fatal("Invalid combination of actions"); 915 else if (xflag) { 916 if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) 917 ret = 1; 918 goto done; 919 } else if (lflag) { 920 if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1) 921 ret = 1; 922 goto done; 923 } else if (Dflag) { 924 if (delete_all(agent_fd, qflag) == -1) 925 ret = 1; 926 goto done; 927 } 928 929 if (skprovider == NULL) 930 skprovider = "internal"; 931 if (hostkey_files == NULL) { 932 /* use defaults from readconf.c */ 933 stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE); 934 stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE2); 935 stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE); 936 stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE2); 937 } 938 if (dest_constraint_strings != NULL) { 939 for (i = 0; dest_constraint_strings[i] != NULL; i++) { 940 parse_dest_constraint(dest_constraint_strings[i], 941 &dest_constraints, &ndest_constraints, hostkey_files); 942 } 943 } 944 945 argc -= optind; 946 argv += optind; 947 if (Tflag) { 948 if (argc <= 0) 949 fatal("no keys to test"); 950 for (r = i = 0; i < argc; i++) 951 r |= test_key(agent_fd, argv[i]); 952 ret = r == 0 ? 0 : 1; 953 goto done; 954 } 955 if (pkcs11provider != NULL) { 956 if (update_card(agent_fd, !deleting, pkcs11provider, 957 qflag, dest_constraints, ndest_constraints) == -1) 958 ret = 1; 959 goto done; 960 } 961 if (do_download) { 962 if (skprovider == NULL) 963 fatal("Cannot download keys without provider"); 964 if (load_resident_keys(agent_fd, skprovider, qflag, 965 dest_constraints, ndest_constraints) != 0) 966 ret = 1; 967 goto done; 968 } 969 if (argc == 0) { 970 char buf[PATH_MAX]; 971 struct passwd *pw; 972 struct stat st; 973 int count = 0; 974 975 if ((pw = getpwuid(getuid())) == NULL) { 976 fprintf(stderr, "No user found with uid %u\n", 977 (u_int)getuid()); 978 ret = 1; 979 goto done; 980 } 981 982 for (i = 0; default_files[i]; i++) { 983 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, 984 default_files[i]); 985 if (stat(buf, &st) == -1) 986 continue; 987 if (do_file(agent_fd, deleting, key_only, buf, 988 qflag, skprovider, 989 dest_constraints, ndest_constraints) == -1) 990 ret = 1; 991 else 992 count++; 993 } 994 if (count == 0) 995 ret = 1; 996 } else { 997 for (i = 0; i < argc; i++) { 998 if (do_file(agent_fd, deleting, key_only, 999 argv[i], qflag, skprovider, 1000 dest_constraints, ndest_constraints) == -1) 1001 ret = 1; 1002 } 1003 } 1004 done: 1005 clear_pass(); 1006 ssh_close_authentication_socket(agent_fd); 1007 return ret; 1008 } 1009