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