Annotation of src/usr.bin/ssh/ssh-add.c, Revision 1.157
1.157 ! djm 1: /* $OpenBSD: ssh-add.c,v 1.156 2020/06/26 05:04:07 djm Exp $ */
1.1 deraadt 2: /*
1.13 deraadt 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.
1.19 markus 7: *
1.22 deraadt 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: *
1.19 markus 14: * SSH2 implementation,
1.41 markus 15: * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
1.22 deraadt 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.
1.13 deraadt 36: */
1.1 deraadt 37:
1.75 stevesk 38: #include <sys/types.h>
39: #include <sys/stat.h>
1.16 markus 40:
1.141 djm 41: #ifdef WITH_OPENSSL
1.19 markus 42: #include <openssl/evp.h>
1.141 djm 43: #endif
1.81 stevesk 44:
1.116 djm 45: #include <errno.h>
1.82 stevesk 46: #include <fcntl.h>
1.81 stevesk 47: #include <pwd.h>
1.88 stevesk 48: #include <stdio.h>
1.87 stevesk 49: #include <stdlib.h>
1.85 stevesk 50: #include <string.h>
1.146 naddy 51: #include <stdarg.h>
1.84 stevesk 52: #include <unistd.h>
1.117 deraadt 53: #include <limits.h>
1.1 deraadt 54:
1.89 deraadt 55: #include "xmalloc.h"
1.27 markus 56: #include "ssh.h"
57: #include "log.h"
1.116 djm 58: #include "sshkey.h"
59: #include "sshbuf.h"
1.18 markus 60: #include "authfd.h"
1.16 markus 61: #include "authfile.h"
1.25 markus 62: #include "pathnames.h"
1.57 stevesk 63: #include "misc.h"
1.110 djm 64: #include "ssherr.h"
1.115 djm 65: #include "digest.h"
1.148 djm 66: #include "ssh-sk.h"
1.157 ! djm 67: #include "sk-api.h"
1.1 deraadt 68:
1.45 jakob 69: /* argv0 */
70: extern char *__progname;
71:
1.48 djm 72: /* Default files to add */
73: static char *default_files[] = {
74: _PATH_SSH_CLIENT_ID_RSA,
75: _PATH_SSH_CLIENT_ID_DSA,
1.99 djm 76: _PATH_SSH_CLIENT_ID_ECDSA,
1.143 djm 77: _PATH_SSH_CLIENT_ID_ECDSA_SK,
1.107 pascal 78: _PATH_SSH_CLIENT_ID_ED25519,
1.144 markus 79: _PATH_SSH_CLIENT_ID_ED25519_SK,
1.135 markus 80: _PATH_SSH_CLIENT_ID_XMSS,
1.48 djm 81: NULL
82: };
83:
1.115 djm 84: static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
85:
1.56 markus 86: /* Default lifetime (0 == forever) */
1.153 dtucker 87: static long lifetime = 0;
1.48 djm 88:
1.65 markus 89: /* User has to confirm key use */
90: static int confirm = 0;
91:
1.135 markus 92: /* Maximum number of signatures (XMSS) */
93: static u_int maxsign = 0;
94: static u_int minleft = 0;
95:
1.124 tim 96: /* we keep a cache of one passphrase */
1.33 markus 97: static char *pass = NULL;
1.39 itojun 98: static void
1.33 markus 99: clear_pass(void)
100: {
101: if (pass) {
1.154 jsg 102: freezero(pass, strlen(pass));
1.33 markus 103: pass = NULL;
104: }
105: }
106:
1.46 djm 107: static int
1.156 djm 108: delete_one(int agent_fd, const struct sshkey *key, const char *comment,
109: const char *path, int qflag)
110: {
111: int r;
112:
113: if ((r = ssh_remove_identity(agent_fd, key)) != 0) {
114: fprintf(stderr, "Could not remove identity \"%s\": %s\n",
115: path, ssh_err(r));
116: return r;
117: }
118: if (!qflag) {
119: fprintf(stderr, "Identity removed: %s %s (%s)\n", path,
120: sshkey_type(key), comment);
121: }
122: return 0;
123: }
124:
125: static int
126: delete_stdin(int agent_fd, int qflag)
127: {
128: char *line = NULL, *cp;
129: size_t linesize = 0;
130: struct sshkey *key = NULL;
131: int lnum = 0, r, ret = -1;
132:
133: while (getline(&line, &linesize, stdin) != -1) {
134: lnum++;
135: sshkey_free(key);
136: key = NULL;
137: line[strcspn(line, "\n")] = '\0';
138: cp = line + strspn(line, " \t");
139: if (*cp == '#' || *cp == '\0')
140: continue;
141: if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
142: fatal("%s: sshkey_new", __func__);
143: if ((r = sshkey_read(key, &cp)) != 0) {
144: error("(stdin):%d: invalid key: %s", lnum, ssh_err(r));
145: continue;
146: }
147: if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 0)
148: ret = 0;
149: }
150: sshkey_free(key);
151: free(line);
152: return ret;
153: }
154:
155: static int
1.134 dlg 156: delete_file(int agent_fd, const char *filename, int key_only, int qflag)
1.1 deraadt 157: {
1.116 djm 158: struct sshkey *public, *cert = NULL;
1.104 djm 159: char *certpath = NULL, *comment = NULL;
1.116 djm 160: int r, ret = -1;
1.1 deraadt 161:
1.156 djm 162: if (strcmp(filename, "-") == 0)
163: return delete_stdin(agent_fd, qflag);
164:
1.116 djm 165: if ((r = sshkey_load_public(filename, &public, &comment)) != 0) {
166: printf("Bad key file %s: %s\n", filename, ssh_err(r));
1.46 djm 167: return -1;
1.12 markus 168: }
1.156 djm 169: if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
1.46 djm 170: ret = 0;
171:
1.104 djm 172: if (key_only)
173: goto out;
174:
175: /* Now try to delete the corresponding certificate too */
176: free(comment);
1.105 markus 177: comment = NULL;
1.104 djm 178: xasprintf(&certpath, "%s-cert.pub", filename);
1.120 halex 179: if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) {
180: if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
181: error("Failed to load certificate \"%s\": %s",
182: certpath, ssh_err(r));
1.104 djm 183: goto out;
1.120 halex 184: }
185:
1.116 djm 186: if (!sshkey_equal_public(cert, public))
1.104 djm 187: fatal("Certificate %s does not match private key %s",
188: certpath, filename);
189:
1.156 djm 190: if (delete_one(agent_fd, cert, comment, certpath, qflag) == 0)
1.104 djm 191: ret = 0;
192:
193: out:
1.127 mmcc 194: sshkey_free(cert);
195: sshkey_free(public);
1.104 djm 196: free(certpath);
197: free(comment);
1.47 deraadt 198:
1.46 djm 199: return ret;
1.1 deraadt 200: }
201:
1.19 markus 202: /* Send a request to remove all identities. */
1.46 djm 203: static int
1.136 djm 204: delete_all(int agent_fd, int qflag)
1.1 deraadt 205: {
1.46 djm 206: int ret = -1;
1.19 markus 207:
1.130 djm 208: /*
209: * Since the agent might be forwarded, old or non-OpenSSH, when asked
210: * to remove all keys, attempt to remove both protocol v.1 and v.2
211: * keys.
212: */
1.121 markus 213: if (ssh_remove_all_identities(agent_fd, 2) == 0)
1.46 djm 214: ret = 0;
1.121 markus 215: /* ignore error-code for ssh1 */
216: ssh_remove_all_identities(agent_fd, 1);
1.19 markus 217:
1.136 djm 218: if (ret != 0)
219: fprintf(stderr, "Failed to remove all identities.\n");
220: else if (!qflag)
1.12 markus 221: fprintf(stderr, "All identities removed.\n");
1.46 djm 222:
223: return ret;
1.1 deraadt 224: }
225:
1.46 djm 226: static int
1.143 djm 227: add_file(int agent_fd, const char *filename, int key_only, int qflag,
228: const char *skprovider)
1.1 deraadt 229: {
1.116 djm 230: struct sshkey *private, *cert;
1.36 markus 231: char *comment = NULL;
1.102 djm 232: char msg[1024], *certpath = NULL;
1.116 djm 233: int r, fd, ret = -1;
1.135 markus 234: size_t i;
235: u_int32_t left;
1.116 djm 236: struct sshbuf *keyblob;
1.135 markus 237: struct ssh_identitylist *idlist;
1.12 markus 238:
1.101 djm 239: if (strcmp(filename, "-") == 0) {
240: fd = STDIN_FILENO;
241: filename = "(stdin)";
1.140 deraadt 242: } else if ((fd = open(filename, O_RDONLY)) == -1) {
1.19 markus 243: perror(filename);
1.46 djm 244: return -1;
1.19 markus 245: }
1.76 dtucker 246:
247: /*
248: * Since we'll try to load a keyfile multiple times, permission errors
249: * will occur multiple times, so check perms first and bail if wrong.
250: */
1.101 djm 251: if (fd != STDIN_FILENO) {
1.116 djm 252: if (sshkey_perm_ok(fd, filename) != 0) {
1.101 djm 253: close(fd);
254: return -1;
255: }
256: }
1.151 djm 257: if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) {
1.116 djm 258: fprintf(stderr, "Error loading key \"%s\": %s\n",
259: filename, ssh_err(r));
260: sshbuf_free(keyblob);
1.101 djm 261: close(fd);
262: return -1;
263: }
1.76 dtucker 264: close(fd);
265:
1.12 markus 266: /* At first, try empty passphrase */
1.125 tim 267: if ((r = sshkey_parse_private_fileblob(keyblob, "", &private,
268: &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
1.116 djm 269: fprintf(stderr, "Error loading key \"%s\": %s\n",
270: filename, ssh_err(r));
271: goto fail_load;
272: }
1.33 markus 273: /* try last */
1.110 djm 274: if (private == NULL && pass != NULL) {
1.125 tim 275: if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private,
276: &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
1.116 djm 277: fprintf(stderr, "Error loading key \"%s\": %s\n",
278: filename, ssh_err(r));
279: goto fail_load;
280: }
1.110 djm 281: }
1.31 markus 282: if (private == NULL) {
1.33 markus 283: /* clear passphrase since it did not work */
284: clear_pass();
1.124 tim 285: snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ",
286: filename, confirm ? " (will confirm each use)" : "");
1.12 markus 287: for (;;) {
1.40 markus 288: pass = read_passphrase(msg, RP_ALLOW_STDIN);
1.116 djm 289: if (strcmp(pass, "") == 0)
290: goto fail_load;
291: if ((r = sshkey_parse_private_fileblob(keyblob, pass,
1.125 tim 292: &private, &comment)) == 0)
1.116 djm 293: break;
294: else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
295: fprintf(stderr,
296: "Error loading key \"%s\": %s\n",
297: filename, ssh_err(r));
298: fail_load:
1.35 markus 299: clear_pass();
1.116 djm 300: sshbuf_free(keyblob);
1.46 djm 301: return -1;
1.12 markus 302: }
1.33 markus 303: clear_pass();
1.68 markus 304: snprintf(msg, sizeof msg,
1.124 tim 305: "Bad passphrase, try again for %s%s: ", filename,
1.119 halex 306: confirm ? " (will confirm each use)" : "");
1.12 markus 307: }
308: }
1.124 tim 309: if (comment == NULL || *comment == '\0')
310: comment = xstrdup(filename);
1.116 djm 311: sshbuf_free(keyblob);
1.60 markus 312:
1.135 markus 313: /* For XMSS */
314: if ((r = sshkey_set_filename(private, filename)) != 0) {
315: fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
316: filename, comment);
317: goto out;
318: }
319: if (maxsign && minleft &&
320: (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
321: for (i = 0; i < idlist->nkeys; i++) {
322: if (!sshkey_equal_public(idlist->keys[i], private))
323: continue;
324: left = sshkey_signatures_left(idlist->keys[i]);
325: if (left < minleft) {
326: fprintf(stderr,
327: "Only %d signatures left.\n", left);
328: break;
329: }
330: fprintf(stderr, "Skipping update: ");
331: if (left == minleft) {
332: fprintf(stderr,
333: "required signatures left (%d).\n", left);
334: } else {
335: fprintf(stderr,
336: "more signatures left (%d) than"
337: " required (%d).\n", left, minleft);
338: }
339: ssh_free_identitylist(idlist);
340: goto out;
341: }
342: ssh_free_identitylist(idlist);
343: }
344:
1.157 ! djm 345: if (sshkey_is_sk(private)) {
! 346: if (skprovider == NULL) {
! 347: fprintf(stderr, "Cannot load FIDO key %s "
! 348: "without provider\n", filename);
! 349: goto out;
! 350: }
! 351: if ((private->sk_flags & SSH_SK_USER_VERIFICATION_REQD) != 0) {
! 352: fprintf(stderr, "FIDO verify-required key %s is not "
! 353: "currently supported by ssh-agent\n", filename);
! 354: goto out;
! 355: }
! 356: } else {
! 357: /* Don't send provider constraint for other keys */
! 358: skprovider = NULL;
1.143 djm 359: }
360:
1.116 djm 361: if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
1.143 djm 362: lifetime, confirm, maxsign, skprovider)) == 0) {
1.60 markus 363: ret = 0;
1.136 djm 364: if (!qflag) {
365: fprintf(stderr, "Identity added: %s (%s)\n",
366: filename, comment);
367: if (lifetime != 0) {
368: fprintf(stderr,
1.153 dtucker 369: "Lifetime set to %ld seconds\n", lifetime);
1.136 djm 370: }
371: if (confirm != 0) {
372: fprintf(stderr, "The user must confirm "
373: "each use of the key\n");
374: }
375: }
1.60 markus 376: } else {
1.116 djm 377: fprintf(stderr, "Could not add identity \"%s\": %s\n",
378: filename, ssh_err(r));
1.56 markus 379: }
380:
1.102 djm 381: /* Skip trying to load the cert if requested */
382: if (key_only)
383: goto out;
1.93 djm 384:
385: /* Now try to add the certificate flavour too */
386: xasprintf(&certpath, "%s-cert.pub", filename);
1.116 djm 387: if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
388: if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
389: error("Failed to load certificate \"%s\": %s",
390: certpath, ssh_err(r));
1.96 djm 391: goto out;
1.116 djm 392: }
1.96 djm 393:
1.116 djm 394: if (!sshkey_equal_public(cert, private)) {
1.96 djm 395: error("Certificate %s does not match private key %s",
396: certpath, filename);
1.116 djm 397: sshkey_free(cert);
1.96 djm 398: goto out;
399: }
1.93 djm 400:
1.96 djm 401: /* Graft with private bits */
1.123 djm 402: if ((r = sshkey_to_certified(private)) != 0) {
1.116 djm 403: error("%s: sshkey_to_certified: %s", __func__, ssh_err(r));
404: sshkey_free(cert);
405: goto out;
406: }
407: if ((r = sshkey_cert_copy(cert, private)) != 0) {
1.132 markus 408: error("%s: sshkey_cert_copy: %s", __func__, ssh_err(r));
1.116 djm 409: sshkey_free(cert);
1.96 djm 410: goto out;
1.94 otto 411: }
1.116 djm 412: sshkey_free(cert);
1.93 djm 413:
1.116 djm 414: if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
1.143 djm 415: lifetime, confirm, maxsign, skprovider)) != 0) {
1.116 djm 416: error("Certificate %s (%s) add failed: %s", certpath,
417: private->cert->key_id, ssh_err(r));
418: goto out;
1.96 djm 419: }
1.136 djm 420: /* success */
421: if (!qflag) {
422: fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
423: private->cert->key_id);
424: if (lifetime != 0) {
1.153 dtucker 425: fprintf(stderr, "Lifetime set to %ld seconds\n",
1.136 djm 426: lifetime);
427: }
428: if (confirm != 0) {
429: fprintf(stderr, "The user must confirm each use "
430: "of the key\n");
431: }
432: }
433:
1.96 djm 434: out:
1.116 djm 435: free(certpath);
1.106 djm 436: free(comment);
1.116 djm 437: sshkey_free(private);
1.47 deraadt 438:
1.46 djm 439: return ret;
1.1 deraadt 440: }
441:
1.46 djm 442: static int
1.136 djm 443: update_card(int agent_fd, int add, const char *id, int qflag)
1.42 markus 444: {
1.108 djm 445: char *pin = NULL;
1.116 djm 446: int r, ret = -1;
1.53 rees 447:
1.108 djm 448: if (add) {
449: if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
450: RP_ALLOW_STDIN)) == NULL)
451: return -1;
452: }
1.53 rees 453:
1.116 djm 454: if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
455: lifetime, confirm)) == 0) {
1.66 markus 456: ret = 0;
1.136 djm 457: if (!qflag) {
458: fprintf(stderr, "Card %s: %s\n",
459: add ? "added" : "removed", id);
460: }
1.46 djm 461: } else {
1.116 djm 462: fprintf(stderr, "Could not %s card \"%s\": %s\n",
463: add ? "add" : "remove", id, ssh_err(r));
1.66 markus 464: ret = -1;
1.46 djm 465: }
1.106 djm 466: free(pin);
1.66 markus 467: return ret;
1.42 markus 468: }
469:
1.50 markus 470: static int
1.137 djm 471: test_key(int agent_fd, const char *filename)
472: {
473: struct sshkey *key = NULL;
474: u_char *sig = NULL;
475: size_t slen = 0;
476: int r, ret = -1;
477: char data[1024];
478:
479: if ((r = sshkey_load_public(filename, &key, NULL)) != 0) {
480: error("Couldn't read public key %s: %s", filename, ssh_err(r));
481: return -1;
482: }
483: arc4random_buf(data, sizeof(data));
484: if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data),
485: NULL, 0)) != 0) {
486: error("Agent signature failed for %s: %s",
487: filename, ssh_err(r));
488: goto done;
489: }
490: if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
1.147 djm 491: NULL, 0, NULL)) != 0) {
1.137 djm 492: error("Signature verification failed for %s: %s",
493: filename, ssh_err(r));
494: goto done;
495: }
496: /* success */
497: ret = 0;
498: done:
499: free(sig);
500: sshkey_free(key);
501: return ret;
502: }
503:
504: static int
1.116 djm 505: list_identities(int agent_fd, int do_fp)
1.1 deraadt 506: {
1.116 djm 507: char *fp;
1.131 naddy 508: int r;
1.116 djm 509: struct ssh_identitylist *idlist;
1.135 markus 510: u_int32_t left;
1.116 djm 511: size_t i;
1.12 markus 512:
1.131 naddy 513: if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
514: if (r != SSH_ERR_AGENT_NO_IDENTITIES)
515: fprintf(stderr, "error fetching identities: %s\n",
516: ssh_err(r));
517: else
518: printf("The agent has no identities.\n");
519: return -1;
520: }
521: for (i = 0; i < idlist->nkeys; i++) {
522: if (do_fp) {
523: fp = sshkey_fingerprint(idlist->keys[i],
524: fingerprint_hash, SSH_FP_DEFAULT);
525: printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]),
526: fp == NULL ? "(null)" : fp, idlist->comments[i],
527: sshkey_type(idlist->keys[i]));
528: free(fp);
529: } else {
530: if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) {
531: fprintf(stderr, "sshkey_write: %s\n",
532: ssh_err(r));
533: continue;
1.12 markus 534: }
1.135 markus 535: fprintf(stdout, " %s", idlist->comments[i]);
536: left = sshkey_signatures_left(idlist->keys[i]);
537: if (left > 0)
538: fprintf(stdout,
539: " [signatures left %d]", left);
540: fprintf(stdout, "\n");
1.12 markus 541: }
1.50 markus 542: }
1.131 naddy 543: ssh_free_identitylist(idlist);
1.50 markus 544: return 0;
1.1 deraadt 545: }
546:
1.48 djm 547: static int
1.116 djm 548: lock_agent(int agent_fd, int lock)
1.54 markus 549: {
550: char prompt[100], *p1, *p2;
1.116 djm 551: int r, passok = 1, ret = -1;
1.61 deraadt 552:
1.54 markus 553: strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
554: p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
555: if (lock) {
556: strlcpy(prompt, "Again: ", sizeof prompt);
557: p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
558: if (strcmp(p1, p2) != 0) {
559: fprintf(stderr, "Passwords do not match.\n");
560: passok = 0;
561: }
1.154 jsg 562: freezero(p2, strlen(p2));
1.54 markus 563: }
1.116 djm 564: if (passok) {
565: if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) {
566: fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
567: ret = 0;
568: } else {
569: fprintf(stderr, "Failed to %slock agent: %s\n",
570: lock ? "" : "un", ssh_err(r));
571: }
572: }
1.154 jsg 573: freezero(p1, strlen(p1));
1.62 markus 574: return (ret);
1.54 markus 575: }
576:
577: static int
1.148 djm 578: load_resident_keys(int agent_fd, const char *skprovider, int qflag)
579: {
580: struct sshkey **keys;
581: size_t nkeys, i;
582: int r, ok = 0;
583: char *fp;
584:
1.152 naddy 585: pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
1.149 djm 586: if ((r = sshsk_load_resident(skprovider, NULL, pass,
587: &keys, &nkeys)) != 0) {
1.148 djm 588: error("Unable to load resident keys: %s", ssh_err(r));
589: return r;
590: }
591: for (i = 0; i < nkeys; i++) {
592: if ((fp = sshkey_fingerprint(keys[i],
593: fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
594: fatal("%s: sshkey_fingerprint failed", __func__);
595: if ((r = ssh_add_identity_constrained(agent_fd, keys[i], "",
596: lifetime, confirm, maxsign, skprovider)) != 0) {
597: error("Unable to add key %s %s",
598: sshkey_type(keys[i]), fp);
599: free(fp);
600: ok = r;
601: continue;
602: }
603: if (ok == 0)
604: ok = 1;
605: if (!qflag) {
606: fprintf(stderr, "Resident identity added: %s %s\n",
607: sshkey_type(keys[i]), fp);
608: if (lifetime != 0) {
609: fprintf(stderr,
1.153 dtucker 610: "Lifetime set to %ld seconds\n", lifetime);
1.148 djm 611: }
612: if (confirm != 0) {
613: fprintf(stderr, "The user must confirm "
614: "each use of the key\n");
615: }
616: }
617: free(fp);
618: sshkey_free(keys[i]);
619: }
620: free(keys);
621: if (nkeys == 0)
622: return SSH_ERR_KEY_NOT_FOUND;
623: return ok == 1 ? 0 : ok;
624: }
625:
626: static int
1.143 djm 627: do_file(int agent_fd, int deleting, int key_only, char *file, int qflag,
628: const char *skprovider)
1.48 djm 629: {
630: if (deleting) {
1.134 dlg 631: if (delete_file(agent_fd, file, key_only, qflag) == -1)
1.48 djm 632: return -1;
633: } else {
1.143 djm 634: if (add_file(agent_fd, file, key_only, qflag, skprovider) == -1)
1.48 djm 635: return -1;
636: }
637: return 0;
638: }
639:
1.42 markus 640: static void
641: usage(void)
642: {
1.150 naddy 643: fprintf(stderr,
644: "usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-S provider] [-t life]\n"
645: #ifdef WITH_XMSS
646: " [-M maxsign] [-m minleft]\n"
647: #endif
648: " [file ...]\n"
649: " ssh-add -s pkcs11\n"
650: " ssh-add -e pkcs11\n"
651: " ssh-add -T pubkey ...\n"
652: );
1.42 markus 653: }
654:
1.2 provos 655: int
1.7 markus 656: main(int argc, char **argv)
1.1 deraadt 657: {
1.43 markus 658: extern char *optarg;
659: extern int optind;
1.116 djm 660: int agent_fd;
1.143 djm 661: char *pkcs11provider = NULL, *skprovider = NULL;
1.148 djm 662: int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0;
1.137 djm 663: int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0;
1.138 djm 664: SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
665: LogLevel log_level = SYSLOG_LEVEL_INFO;
1.73 djm 666:
667: /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
668: sanitise_stdfd();
1.12 markus 669:
1.141 djm 670: #ifdef WITH_OPENSSL
1.100 djm 671: OpenSSL_add_all_algorithms();
1.141 djm 672: #endif
1.138 djm 673: log_init(__progname, log_level, log_facility, 1);
674:
1.114 millert 675: setvbuf(stdout, NULL, _IOLBF, 0);
1.19 markus 676:
1.116 djm 677: /* First, get a connection to the authentication agent. */
678: switch (r = ssh_get_authentication_socket(&agent_fd)) {
679: case 0:
680: break;
681: case SSH_ERR_AGENT_NOT_PRESENT:
682: fprintf(stderr, "Could not open a connection to your "
683: "authentication agent.\n");
684: exit(2);
685: default:
686: fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r));
1.50 markus 687: exit(2);
1.12 markus 688: }
1.116 djm 689:
1.143 djm 690: skprovider = getenv("SSH_SK_PROVIDER");
691:
1.150 naddy 692: while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:M:m:qs:S:t:")) != -1) {
1.43 markus 693: switch (ch) {
1.138 djm 694: case 'v':
695: if (log_level == SYSLOG_LEVEL_INFO)
696: log_level = SYSLOG_LEVEL_DEBUG1;
697: else if (log_level < SYSLOG_LEVEL_DEBUG3)
698: log_level++;
699: break;
1.115 djm 700: case 'E':
701: fingerprint_hash = ssh_digest_alg_by_name(optarg);
702: if (fingerprint_hash == -1)
703: fatal("Invalid hash algorithm \"%s\"", optarg);
704: break;
1.102 djm 705: case 'k':
706: key_only = 1;
707: break;
1.150 naddy 708: case 'K':
709: do_download = 1;
710: break;
1.43 markus 711: case 'l':
712: case 'L':
1.115 djm 713: if (lflag != 0)
714: fatal("-%c flag already specified", lflag);
715: lflag = ch;
1.148 djm 716: break;
1.54 markus 717: case 'x':
718: case 'X':
1.115 djm 719: if (xflag != 0)
720: fatal("-%c flag already specified", xflag);
721: xflag = ch;
722: break;
1.65 markus 723: case 'c':
724: confirm = 1;
1.135 markus 725: break;
726: case 'm':
727: minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL);
728: if (minleft == 0) {
729: usage();
730: ret = 1;
731: goto done;
732: }
733: break;
734: case 'M':
735: maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL);
736: if (maxsign == 0) {
737: usage();
738: ret = 1;
739: goto done;
740: }
1.43 markus 741: break;
742: case 'd':
1.12 markus 743: deleting = 1;
1.43 markus 744: break;
745: case 'D':
1.115 djm 746: Dflag = 1;
747: break;
1.43 markus 748: case 's':
1.92 markus 749: pkcs11provider = optarg;
1.43 markus 750: break;
1.143 djm 751: case 'S':
752: skprovider = optarg;
753: break;
1.43 markus 754: case 'e':
1.47 deraadt 755: deleting = 1;
1.92 markus 756: pkcs11provider = optarg;
1.56 markus 757: break;
758: case 't':
1.153 dtucker 759: if ((lifetime = convtime(optarg)) == -1 ||
1.155 dtucker 760: lifetime < 0 || (u_long)lifetime > UINT32_MAX) {
1.57 stevesk 761: fprintf(stderr, "Invalid lifetime\n");
762: ret = 1;
763: goto done;
764: }
1.43 markus 765: break;
1.134 dlg 766: case 'q':
767: qflag = 1;
768: break;
1.137 djm 769: case 'T':
770: Tflag = 1;
771: break;
1.43 markus 772: default:
773: usage();
1.46 djm 774: ret = 1;
775: goto done;
1.42 markus 776: }
777: }
1.138 djm 778: log_init(__progname, log_level, log_facility, 1);
1.115 djm 779:
780: if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
781: fatal("Invalid combination of actions");
782: else if (xflag) {
1.116 djm 783: if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1)
1.115 djm 784: ret = 1;
785: goto done;
786: } else if (lflag) {
1.116 djm 787: if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1)
1.115 djm 788: ret = 1;
789: goto done;
790: } else if (Dflag) {
1.136 djm 791: if (delete_all(agent_fd, qflag) == -1)
1.115 djm 792: ret = 1;
793: goto done;
794: }
1.145 djm 795:
796: if (skprovider == NULL)
797: skprovider = "internal";
1.115 djm 798:
1.43 markus 799: argc -= optind;
800: argv += optind;
1.137 djm 801: if (Tflag) {
802: if (argc <= 0)
803: fatal("no keys to test");
804: for (r = i = 0; i < argc; i++)
805: r |= test_key(agent_fd, argv[i]);
806: ret = r == 0 ? 0 : 1;
807: goto done;
808: }
1.92 markus 809: if (pkcs11provider != NULL) {
1.136 djm 810: if (update_card(agent_fd, !deleting, pkcs11provider,
811: qflag) == -1)
1.46 djm 812: ret = 1;
1.43 markus 813: goto done;
1.12 markus 814: }
1.148 djm 815: if (do_download) {
816: if (skprovider == NULL)
817: fatal("Cannot download keys without provider");
818: if (load_resident_keys(agent_fd, skprovider, qflag) != 0)
819: ret = 1;
820: goto done;
821: }
1.43 markus 822: if (argc == 0) {
1.117 deraadt 823: char buf[PATH_MAX];
1.48 djm 824: struct passwd *pw;
1.52 markus 825: struct stat st;
826: int count = 0;
1.48 djm 827:
828: if ((pw = getpwuid(getuid())) == NULL) {
1.20 deraadt 829: fprintf(stderr, "No user found with uid %u\n",
830: (u_int)getuid());
1.46 djm 831: ret = 1;
832: goto done;
1.12 markus 833: }
1.48 djm 834:
1.71 deraadt 835: for (i = 0; default_files[i]; i++) {
1.51 markus 836: snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
1.48 djm 837: default_files[i]);
1.140 deraadt 838: if (stat(buf, &st) == -1)
1.52 markus 839: continue;
1.134 dlg 840: if (do_file(agent_fd, deleting, key_only, buf,
1.143 djm 841: qflag, skprovider) == -1)
1.46 djm 842: ret = 1;
1.52 markus 843: else
844: count++;
1.46 djm 845: }
1.52 markus 846: if (count == 0)
847: ret = 1;
1.43 markus 848: } else {
1.71 deraadt 849: for (i = 0; i < argc; i++) {
1.116 djm 850: if (do_file(agent_fd, deleting, key_only,
1.143 djm 851: argv[i], qflag, skprovider) == -1)
1.48 djm 852: ret = 1;
1.43 markus 853: }
1.12 markus 854: }
1.148 djm 855: done:
1.33 markus 856: clear_pass();
1.116 djm 857: ssh_close_authentication_socket(agent_fd);
1.46 djm 858: return ret;
1.1 deraadt 859: }