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