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