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