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