1 /* $OpenBSD: ssh-add.c,v 1.120 2015/02/21 21:46:57 halex Exp $ */ 2 /* 3 * Author: Tatu Ylonen <ylo (at) cs.hut.fi> 4 * Copyright (c) 1995 Tatu Ylonen <ylo (at) 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 #include <openssl/evp.h> 44 #include "openbsd-compat/openssl-compat.h" 45 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <pwd.h> 49 #include <stdarg.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <limits.h> 55 56 #include "xmalloc.h" 57 #include "ssh.h" 58 #include "rsa.h" 59 #include "log.h" 60 #include "sshkey.h" 61 #include "sshbuf.h" 62 #include "authfd.h" 63 #include "authfile.h" 64 #include "pathnames.h" 65 #include "misc.h" 66 #include "ssherr.h" 67 #include "digest.h" 68 69 /* argv0 */ 70 extern char *__progname; 71 72 /* Default files to add */ 73 static char *default_files[] = { 74 #ifdef WITH_OPENSSL 75 _PATH_SSH_CLIENT_ID_RSA, 76 _PATH_SSH_CLIENT_ID_DSA, 77 #ifdef OPENSSL_HAS_ECC 78 _PATH_SSH_CLIENT_ID_ECDSA, 79 #endif 80 #endif /* WITH_OPENSSL */ 81 _PATH_SSH_CLIENT_ID_ED25519, 82 _PATH_SSH_CLIENT_IDENTITY, 83 NULL 84 }; 85 86 static int fingerprint_hash = SSH_FP_HASH_DEFAULT; 87 88 /* Default lifetime (0 == forever) */ 89 static int lifetime = 0; 90 91 /* User has to confirm key use */ 92 static int confirm = 0; 93 94 /* we keep a cache of one passphrases */ 95 static char *pass = NULL; 96 static void 97 clear_pass(void) 98 { 99 if (pass) { 100 explicit_bzero(pass, strlen(pass)); 101 free(pass); 102 pass = NULL; 103 } 104 } 105 106 static int 107 delete_file(int agent_fd, const char *filename, int key_only) 108 { 109 struct sshkey *public, *cert = NULL; 110 char *certpath = NULL, *comment = NULL; 111 int r, ret = -1; 112 113 if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { 114 printf("Bad key file %s: %s\n", filename, ssh_err(r)); 115 return -1; 116 } 117 if ((r = ssh_remove_identity(agent_fd, public)) == 0) { 118 fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); 119 ret = 0; 120 } else 121 fprintf(stderr, "Could not remove identity \"%s\": %s\n", 122 filename, ssh_err(r)); 123 124 if (key_only) 125 goto out; 126 127 /* Now try to delete the corresponding certificate too */ 128 free(comment); 129 comment = NULL; 130 xasprintf(&certpath, "%s-cert.pub", filename); 131 if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) { 132 if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 133 error("Failed to load certificate \"%s\": %s", 134 certpath, ssh_err(r)); 135 goto out; 136 } 137 138 if (!sshkey_equal_public(cert, public)) 139 fatal("Certificate %s does not match private key %s", 140 certpath, filename); 141 142 if ((r = ssh_remove_identity(agent_fd, cert)) == 0) { 143 fprintf(stderr, "Identity removed: %s (%s)\n", certpath, 144 comment); 145 ret = 0; 146 } else 147 fprintf(stderr, "Could not remove identity \"%s\": %s\n", 148 certpath, ssh_err(r)); 149 150 out: 151 if (cert != NULL) 152 sshkey_free(cert); 153 if (public != NULL) 154 sshkey_free(public); 155 free(certpath); 156 free(comment); 157 158 return ret; 159 } 160 161 /* Send a request to remove all identities. */ 162 static int 163 delete_all(int agent_fd) 164 { 165 int ret = -1; 166 167 if (ssh_remove_all_identities(agent_fd, 1) == 0) 168 ret = 0; 169 /* ignore error-code for ssh2 */ 170 /* XXX revisit */ 171 ssh_remove_all_identities(agent_fd, 2); 172 173 if (ret == 0) 174 fprintf(stderr, "All identities removed.\n"); 175 else 176 fprintf(stderr, "Failed to remove all identities.\n"); 177 178 return ret; 179 } 180 181 static int 182 add_file(int agent_fd, const char *filename, int key_only) 183 { 184 struct sshkey *private, *cert; 185 char *comment = NULL; 186 char msg[1024], *certpath = NULL; 187 int r, fd, ret = -1; 188 struct sshbuf *keyblob; 189 190 if (strcmp(filename, "-") == 0) { 191 fd = STDIN_FILENO; 192 filename = "(stdin)"; 193 } else if ((fd = open(filename, O_RDONLY)) < 0) { 194 perror(filename); 195 return -1; 196 } 197 198 /* 199 * Since we'll try to load a keyfile multiple times, permission errors 200 * will occur multiple times, so check perms first and bail if wrong. 201 */ 202 if (fd != STDIN_FILENO) { 203 if (sshkey_perm_ok(fd, filename) != 0) { 204 close(fd); 205 return -1; 206 } 207 } 208 if ((keyblob = sshbuf_new()) == NULL) 209 fatal("%s: sshbuf_new failed", __func__); 210 if ((r = sshkey_load_file(fd, keyblob)) != 0) { 211 fprintf(stderr, "Error loading key \"%s\": %s\n", 212 filename, ssh_err(r)); 213 sshbuf_free(keyblob); 214 close(fd); 215 return -1; 216 } 217 close(fd); 218 219 /* At first, try empty passphrase */ 220 if ((r = sshkey_parse_private_fileblob(keyblob, "", filename, 221 &private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 222 fprintf(stderr, "Error loading key \"%s\": %s\n", 223 filename, ssh_err(r)); 224 goto fail_load; 225 } 226 /* try last */ 227 if (private == NULL && pass != NULL) { 228 if ((r = sshkey_parse_private_fileblob(keyblob, pass, filename, 229 &private, &comment)) != 0 && 230 r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 231 fprintf(stderr, "Error loading key \"%s\": %s\n", 232 filename, ssh_err(r)); 233 goto fail_load; 234 } 235 } 236 if (comment == NULL) 237 comment = xstrdup(filename); 238 if (private == NULL) { 239 /* clear passphrase since it did not work */ 240 clear_pass(); 241 snprintf(msg, sizeof msg, "Enter passphrase for %.200s%s: ", 242 comment, confirm ? " (will confirm each use)" : ""); 243 for (;;) { 244 pass = read_passphrase(msg, RP_ALLOW_STDIN); 245 if (strcmp(pass, "") == 0) 246 goto fail_load; 247 if ((r = sshkey_parse_private_fileblob(keyblob, pass, 248 filename, &private, NULL)) == 0) 249 break; 250 else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { 251 fprintf(stderr, 252 "Error loading key \"%s\": %s\n", 253 filename, ssh_err(r)); 254 fail_load: 255 clear_pass(); 256 free(comment); 257 sshbuf_free(keyblob); 258 return -1; 259 } 260 clear_pass(); 261 snprintf(msg, sizeof msg, 262 "Bad passphrase, try again for %.200s%s: ", comment, 263 confirm ? " (will confirm each use)" : ""); 264 } 265 } 266 sshbuf_free(keyblob); 267 268 if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 269 lifetime, confirm)) == 0) { 270 fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); 271 ret = 0; 272 if (lifetime != 0) 273 fprintf(stderr, 274 "Lifetime set to %d seconds\n", lifetime); 275 if (confirm != 0) 276 fprintf(stderr, 277 "The user must confirm each use of the key\n"); 278 } else { 279 fprintf(stderr, "Could not add identity \"%s\": %s\n", 280 filename, ssh_err(r)); 281 } 282 283 /* Skip trying to load the cert if requested */ 284 if (key_only) 285 goto out; 286 287 /* Now try to add the certificate flavour too */ 288 xasprintf(&certpath, "%s-cert.pub", filename); 289 if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { 290 if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 291 error("Failed to load certificate \"%s\": %s", 292 certpath, ssh_err(r)); 293 goto out; 294 } 295 296 if (!sshkey_equal_public(cert, private)) { 297 error("Certificate %s does not match private key %s", 298 certpath, filename); 299 sshkey_free(cert); 300 goto out; 301 } 302 303 /* Graft with private bits */ 304 if ((r = sshkey_to_certified(private, 305 sshkey_cert_is_legacy(cert))) != 0) { 306 error("%s: sshkey_to_certified: %s", __func__, ssh_err(r)); 307 sshkey_free(cert); 308 goto out; 309 } 310 if ((r = sshkey_cert_copy(cert, private)) != 0) { 311 error("%s: key_cert_copy: %s", __func__, ssh_err(r)); 312 sshkey_free(cert); 313 goto out; 314 } 315 sshkey_free(cert); 316 317 if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 318 lifetime, confirm)) != 0) { 319 error("Certificate %s (%s) add failed: %s", certpath, 320 private->cert->key_id, ssh_err(r)); 321 goto out; 322 } 323 fprintf(stderr, "Certificate added: %s (%s)\n", certpath, 324 private->cert->key_id); 325 if (lifetime != 0) 326 fprintf(stderr, "Lifetime set to %d seconds\n", lifetime); 327 if (confirm != 0) 328 fprintf(stderr, "The user must confirm each use of the key\n"); 329 out: 330 free(certpath); 331 free(comment); 332 sshkey_free(private); 333 334 return ret; 335 } 336 337 static int 338 update_card(int agent_fd, int add, const char *id) 339 { 340 char *pin = NULL; 341 int r, ret = -1; 342 343 if (add) { 344 if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", 345 RP_ALLOW_STDIN)) == NULL) 346 return -1; 347 } 348 349 if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, 350 lifetime, confirm)) == 0) { 351 fprintf(stderr, "Card %s: %s\n", 352 add ? "added" : "removed", id); 353 ret = 0; 354 } else { 355 fprintf(stderr, "Could not %s card \"%s\": %s\n", 356 add ? "add" : "remove", id, ssh_err(r)); 357 ret = -1; 358 } 359 free(pin); 360 return ret; 361 } 362 363 static int 364 list_identities(int agent_fd, int do_fp) 365 { 366 char *fp; 367 int version, r, had_identities = 0; 368 struct ssh_identitylist *idlist; 369 size_t i; 370 371 for (version = 1; version <= 2; version++) { 372 if ((r = ssh_fetch_identitylist(agent_fd, version, 373 &idlist)) != 0) { 374 if (r != SSH_ERR_AGENT_NO_IDENTITIES) 375 fprintf(stderr, "error fetching identities for " 376 "protocol %d: %s\n", version, ssh_err(r)); 377 continue; 378 } 379 for (i = 0; i < idlist->nkeys; i++) { 380 had_identities = 1; 381 if (do_fp) { 382 fp = sshkey_fingerprint(idlist->keys[i], 383 fingerprint_hash, SSH_FP_DEFAULT); 384 printf("%d %s %s (%s)\n", 385 sshkey_size(idlist->keys[i]), 386 fp == NULL ? "(null)" : fp, 387 idlist->comments[i], 388 sshkey_type(idlist->keys[i])); 389 free(fp); 390 } else { 391 if ((r = sshkey_write(idlist->keys[i], 392 stdout)) != 0) { 393 fprintf(stderr, "sshkey_write: %s\n", 394 ssh_err(r)); 395 continue; 396 } 397 fprintf(stdout, " %s\n", idlist->comments[i]); 398 } 399 } 400 ssh_free_identitylist(idlist); 401 } 402 if (!had_identities) { 403 printf("The agent has no identities.\n"); 404 return -1; 405 } 406 return 0; 407 } 408 409 static int 410 lock_agent(int agent_fd, int lock) 411 { 412 char prompt[100], *p1, *p2; 413 int r, passok = 1, ret = -1; 414 415 strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); 416 p1 = read_passphrase(prompt, RP_ALLOW_STDIN); 417 if (lock) { 418 strlcpy(prompt, "Again: ", sizeof prompt); 419 p2 = read_passphrase(prompt, RP_ALLOW_STDIN); 420 if (strcmp(p1, p2) != 0) { 421 fprintf(stderr, "Passwords do not match.\n"); 422 passok = 0; 423 } 424 explicit_bzero(p2, strlen(p2)); 425 free(p2); 426 } 427 if (passok) { 428 if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) { 429 fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); 430 ret = 0; 431 } else { 432 fprintf(stderr, "Failed to %slock agent: %s\n", 433 lock ? "" : "un", ssh_err(r)); 434 } 435 } 436 explicit_bzero(p1, strlen(p1)); 437 free(p1); 438 return (ret); 439 } 440 441 static int 442 do_file(int agent_fd, int deleting, int key_only, char *file) 443 { 444 if (deleting) { 445 if (delete_file(agent_fd, file, key_only) == -1) 446 return -1; 447 } else { 448 if (add_file(agent_fd, file, key_only) == -1) 449 return -1; 450 } 451 return 0; 452 } 453 454 static void 455 usage(void) 456 { 457 fprintf(stderr, "usage: %s [options] [file ...]\n", __progname); 458 fprintf(stderr, "Options:\n"); 459 fprintf(stderr, " -l List fingerprints of all identities.\n"); 460 fprintf(stderr, " -E hash Specify hash algorithm used for fingerprints.\n"); 461 fprintf(stderr, " -L List public key parameters of all identities.\n"); 462 fprintf(stderr, " -k Load only keys and not certificates.\n"); 463 fprintf(stderr, " -c Require confirmation to sign using identities\n"); 464 fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); 465 fprintf(stderr, " -d Delete identity.\n"); 466 fprintf(stderr, " -D Delete all identities.\n"); 467 fprintf(stderr, " -x Lock agent.\n"); 468 fprintf(stderr, " -X Unlock agent.\n"); 469 fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); 470 fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); 471 } 472 473 int 474 main(int argc, char **argv) 475 { 476 extern char *optarg; 477 extern int optind; 478 int agent_fd; 479 char *pkcs11provider = NULL; 480 int r, i, ch, deleting = 0, ret = 0, key_only = 0; 481 int xflag = 0, lflag = 0, Dflag = 0; 482 483 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 484 sanitise_stdfd(); 485 486 __progname = ssh_get_progname(argv[0]); 487 seed_rng(); 488 489 #ifdef WITH_OPENSSL 490 OpenSSL_add_all_algorithms(); 491 #endif 492 493 setvbuf(stdout, NULL, _IOLBF, 0); 494 495 /* First, get a connection to the authentication agent. */ 496 switch (r = ssh_get_authentication_socket(&agent_fd)) { 497 case 0: 498 break; 499 case SSH_ERR_AGENT_NOT_PRESENT: 500 fprintf(stderr, "Could not open a connection to your " 501 "authentication agent.\n"); 502 exit(2); 503 default: 504 fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r)); 505 exit(2); 506 } 507 508 while ((ch = getopt(argc, argv, "klLcdDxXE:e:s:t:")) != -1) { 509 switch (ch) { 510 case 'E': 511 fingerprint_hash = ssh_digest_alg_by_name(optarg); 512 if (fingerprint_hash == -1) 513 fatal("Invalid hash algorithm \"%s\"", optarg); 514 break; 515 case 'k': 516 key_only = 1; 517 break; 518 case 'l': 519 case 'L': 520 if (lflag != 0) 521 fatal("-%c flag already specified", lflag); 522 lflag = ch; 523 break; 524 case 'x': 525 case 'X': 526 if (xflag != 0) 527 fatal("-%c flag already specified", xflag); 528 xflag = ch; 529 break; 530 case 'c': 531 confirm = 1; 532 break; 533 case 'd': 534 deleting = 1; 535 break; 536 case 'D': 537 Dflag = 1; 538 break; 539 case 's': 540 pkcs11provider = optarg; 541 break; 542 case 'e': 543 deleting = 1; 544 pkcs11provider = optarg; 545 break; 546 case 't': 547 if ((lifetime = convtime(optarg)) == -1) { 548 fprintf(stderr, "Invalid lifetime\n"); 549 ret = 1; 550 goto done; 551 } 552 break; 553 default: 554 usage(); 555 ret = 1; 556 goto done; 557 } 558 } 559 560 if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) 561 fatal("Invalid combination of actions"); 562 else if (xflag) { 563 if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) 564 ret = 1; 565 goto done; 566 } else if (lflag) { 567 if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1) 568 ret = 1; 569 goto done; 570 } else if (Dflag) { 571 if (delete_all(agent_fd) == -1) 572 ret = 1; 573 goto done; 574 } 575 576 argc -= optind; 577 argv += optind; 578 if (pkcs11provider != NULL) { 579 if (update_card(agent_fd, !deleting, pkcs11provider) == -1) 580 ret = 1; 581 goto done; 582 } 583 if (argc == 0) { 584 char buf[PATH_MAX]; 585 struct passwd *pw; 586 struct stat st; 587 int count = 0; 588 589 if ((pw = getpwuid(getuid())) == NULL) { 590 fprintf(stderr, "No user found with uid %u\n", 591 (u_int)getuid()); 592 ret = 1; 593 goto done; 594 } 595 596 for (i = 0; default_files[i]; i++) { 597 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, 598 default_files[i]); 599 if (stat(buf, &st) < 0) 600 continue; 601 if (do_file(agent_fd, deleting, key_only, buf) == -1) 602 ret = 1; 603 else 604 count++; 605 } 606 if (count == 0) 607 ret = 1; 608 } else { 609 for (i = 0; i < argc; i++) { 610 if (do_file(agent_fd, deleting, key_only, 611 argv[i]) == -1) 612 ret = 1; 613 } 614 } 615 clear_pass(); 616 617 done: 618 ssh_close_authentication_socket(agent_fd); 619 return ret; 620 } 621