Annotation of src/usr.bin/ssh/ssh-add.c, Revision 1.56
1.1 deraadt 1: /*
1.13 deraadt 2: * Author: Tatu Ylonen <ylo@cs.hut.fi>
3: * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4: * All rights reserved
5: * Adds an identity to the authentication server, or removes an identity.
1.19 markus 6: *
1.22 deraadt 7: * As far as I am concerned, the code I have written for this software
8: * can be used freely for any purpose. Any derived versions of this
9: * software must be clearly marked as such, and if the derived work is
10: * incompatible with the protocol description in the RFC file, it must be
11: * called by a name other than "ssh" or "Secure Shell".
12: *
1.19 markus 13: * SSH2 implementation,
1.41 markus 14: * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
1.22 deraadt 15: *
16: * Redistribution and use in source and binary forms, with or without
17: * modification, are permitted provided that the following conditions
18: * are met:
19: * 1. Redistributions of source code must retain the above copyright
20: * notice, this list of conditions and the following disclaimer.
21: * 2. Redistributions in binary form must reproduce the above copyright
22: * notice, this list of conditions and the following disclaimer in the
23: * documentation and/or other materials provided with the distribution.
24: *
25: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.13 deraadt 35: */
1.1 deraadt 36:
37: #include "includes.h"
1.56 ! markus 38: RCSID("$OpenBSD: ssh-add.c,v 1.55 2002/06/05 20:56:39 markus Exp $");
1.16 markus 39:
1.19 markus 40: #include <openssl/evp.h>
1.1 deraadt 41:
1.27 markus 42: #include "ssh.h"
1.1 deraadt 43: #include "rsa.h"
1.27 markus 44: #include "log.h"
1.1 deraadt 45: #include "xmalloc.h"
1.16 markus 46: #include "key.h"
1.18 markus 47: #include "authfd.h"
1.16 markus 48: #include "authfile.h"
1.25 markus 49: #include "pathnames.h"
1.27 markus 50: #include "readpass.h"
1.1 deraadt 51:
1.45 jakob 52: /* argv0 */
53: extern char *__progname;
54:
1.48 djm 55: /* Default files to add */
56: static char *default_files[] = {
57: _PATH_SSH_CLIENT_ID_RSA,
58: _PATH_SSH_CLIENT_ID_DSA,
1.51 markus 59: _PATH_SSH_CLIENT_IDENTITY,
1.48 djm 60: NULL
61: };
62:
1.56 ! markus 63: /* Default lifetime (0 == forever) */
! 64: static u_int lifetime = 0;
1.48 djm 65:
1.33 markus 66: /* we keep a cache of one passphrases */
67: static char *pass = NULL;
1.39 itojun 68: static void
1.33 markus 69: clear_pass(void)
70: {
71: if (pass) {
72: memset(pass, 0, strlen(pass));
73: xfree(pass);
74: pass = NULL;
75: }
76: }
77:
1.46 djm 78: static int
1.7 markus 79: delete_file(AuthenticationConnection *ac, const char *filename)
1.1 deraadt 80: {
1.16 markus 81: Key *public;
1.32 markus 82: char *comment = NULL;
1.46 djm 83: int ret = -1;
1.1 deraadt 84:
1.31 markus 85: public = key_load_public(filename, &comment);
86: if (public == NULL) {
87: printf("Bad key file %s\n", filename);
1.46 djm 88: return -1;
1.12 markus 89: }
1.46 djm 90: if (ssh_remove_identity(ac, public)) {
1.12 markus 91: fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment);
1.46 djm 92: ret = 0;
93: } else
1.12 markus 94: fprintf(stderr, "Could not remove identity: %s\n", filename);
1.46 djm 95:
1.16 markus 96: key_free(public);
1.12 markus 97: xfree(comment);
1.47 deraadt 98:
1.46 djm 99: return ret;
1.1 deraadt 100: }
101:
1.19 markus 102: /* Send a request to remove all identities. */
1.46 djm 103: static int
1.7 markus 104: delete_all(AuthenticationConnection *ac)
1.1 deraadt 105: {
1.46 djm 106: int ret = -1;
1.19 markus 107:
1.46 djm 108: if (ssh_remove_all_identities(ac, 1))
109: ret = 0;
1.19 markus 110: /* ignore error-code for ssh2 */
111: ssh_remove_all_identities(ac, 2);
112:
1.46 djm 113: if (ret == 0)
1.12 markus 114: fprintf(stderr, "All identities removed.\n");
115: else
1.24 markus 116: fprintf(stderr, "Failed to remove all identities.\n");
1.46 djm 117:
118: return ret;
1.1 deraadt 119: }
120:
1.46 djm 121: static int
1.7 markus 122: add_file(AuthenticationConnection *ac, const char *filename)
1.1 deraadt 123: {
1.19 markus 124: struct stat st;
1.16 markus 125: Key *private;
1.36 markus 126: char *comment = NULL;
127: char msg[1024];
1.46 djm 128: int ret = -1;
1.12 markus 129:
1.19 markus 130: if (stat(filename, &st) < 0) {
131: perror(filename);
1.46 djm 132: return -1;
1.19 markus 133: }
1.12 markus 134: /* At first, try empty passphrase */
1.31 markus 135: private = key_load_private(filename, "", &comment);
136: if (comment == NULL)
137: comment = xstrdup(filename);
1.33 markus 138: /* try last */
139: if (private == NULL && pass != NULL)
140: private = key_load_private(filename, pass, NULL);
1.31 markus 141: if (private == NULL) {
1.33 markus 142: /* clear passphrase since it did not work */
143: clear_pass();
1.37 markus 144: snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ",
1.36 markus 145: comment);
1.12 markus 146: for (;;) {
1.40 markus 147: pass = read_passphrase(msg, RP_ALLOW_STDIN);
1.12 markus 148: if (strcmp(pass, "") == 0) {
1.35 markus 149: clear_pass();
1.31 markus 150: xfree(comment);
1.46 djm 151: return -1;
1.12 markus 152: }
1.31 markus 153: private = key_load_private(filename, pass, &comment);
154: if (private != NULL)
1.12 markus 155: break;
1.33 markus 156: clear_pass();
1.37 markus 157: strlcpy(msg, "Bad passphrase, try again: ", sizeof msg);
1.12 markus 158: }
159: }
1.46 djm 160: if (ssh_add_identity(ac, private, comment)) {
1.31 markus 161: fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
1.46 djm 162: ret = 0;
163: } else
1.12 markus 164: fprintf(stderr, "Could not add identity: %s\n", filename);
1.46 djm 165:
1.56 ! markus 166: if (ret == 0 && lifetime != 0) {
! 167: if (ssh_lifetime_identity(ac, private, lifetime)) {
! 168: fprintf(stderr,
! 169: "Lifetime set to %d seconds for: %s (%s)\n",
! 170: lifetime, filename, comment);
! 171: } else {
! 172: fprintf(stderr,
! 173: "Could not set lifetime for identity: %s\n",
! 174: filename);
! 175: }
! 176: }
! 177:
1.31 markus 178: xfree(comment);
1.16 markus 179: key_free(private);
1.47 deraadt 180:
1.46 djm 181: return ret;
1.1 deraadt 182: }
183:
1.46 djm 184: static int
1.44 markus 185: update_card(AuthenticationConnection *ac, int add, const char *id)
1.42 markus 186: {
1.53 rees 187: char *pin;
188:
189: pin = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN);
190: if (pin == NULL)
191: return -1;
192:
193: if (ssh_update_card(ac, add, id, pin)) {
1.44 markus 194: fprintf(stderr, "Card %s: %s\n",
1.47 deraadt 195: add ? "added" : "removed", id);
1.46 djm 196: return 0;
197: } else {
1.44 markus 198: fprintf(stderr, "Could not %s card: %s\n",
1.47 deraadt 199: add ? "add" : "remove", id);
1.46 djm 200: return -1;
201: }
1.42 markus 202: }
203:
1.50 markus 204: static int
1.30 markus 205: list_identities(AuthenticationConnection *ac, int do_fp)
1.1 deraadt 206: {
1.19 markus 207: Key *key;
1.30 markus 208: char *comment, *fp;
1.19 markus 209: int had_identities = 0;
210: int version;
1.12 markus 211:
1.19 markus 212: for (version = 1; version <= 2; version++) {
213: for (key = ssh_get_first_identity(ac, &comment, version);
1.47 deraadt 214: key != NULL;
215: key = ssh_get_next_identity(ac, &comment, version)) {
1.19 markus 216: had_identities = 1;
1.30 markus 217: if (do_fp) {
218: fp = key_fingerprint(key, SSH_FP_MD5,
219: SSH_FP_HEX);
1.23 markus 220: printf("%d %s %s (%s)\n",
1.30 markus 221: key_size(key), fp, comment, key_type(key));
222: xfree(fp);
1.12 markus 223: } else {
1.19 markus 224: if (!key_write(key, stdout))
225: fprintf(stderr, "key_write failed");
226: fprintf(stdout, " %s\n", comment);
1.12 markus 227: }
1.19 markus 228: key_free(key);
229: xfree(comment);
1.12 markus 230: }
231: }
1.50 markus 232: if (!had_identities) {
1.12 markus 233: printf("The agent has no identities.\n");
1.50 markus 234: return -1;
235: }
236: return 0;
1.1 deraadt 237: }
238:
1.48 djm 239: static int
1.54 markus 240: lock_agent(AuthenticationConnection *ac, int lock)
241: {
242: char prompt[100], *p1, *p2;
243: int passok = 1, ret = -1;
244:
245: strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
246: p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
247: if (lock) {
248: strlcpy(prompt, "Again: ", sizeof prompt);
249: p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
250: if (strcmp(p1, p2) != 0) {
251: fprintf(stderr, "Passwords do not match.\n");
252: passok = 0;
253: }
254: memset(p2, 0, strlen(p2));
255: xfree(p2);
256: }
257: if (passok && ssh_lock_agent(ac, lock, p1)) {
258: fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
259: ret = 0;
260: } else
261: fprintf(stderr, "Failed to %slock agent.\n", lock ? "" : "un");
262: memset(p1, 0, strlen(p1));
263: xfree(p1);
264: return -1;
265: }
266:
267: static int
1.48 djm 268: do_file(AuthenticationConnection *ac, int deleting, char *file)
269: {
270: if (deleting) {
271: if (delete_file(ac, file) == -1)
272: return -1;
273: } else {
274: if (add_file(ac, file) == -1)
275: return -1;
276: }
277: return 0;
278: }
279:
1.42 markus 280: static void
281: usage(void)
282: {
1.45 jakob 283: fprintf(stderr, "Usage: %s [options]\n", __progname);
284: fprintf(stderr, "Options:\n");
285: fprintf(stderr, " -l List fingerprints of all identities.\n");
286: fprintf(stderr, " -L List public key parameters of all identities.\n");
287: fprintf(stderr, " -d Delete identity.\n");
288: fprintf(stderr, " -D Delete all identities.\n");
1.55 markus 289: fprintf(stderr, " -x Lock agent.\n");
290: fprintf(stderr, " -x Unlock agent.\n");
1.56 ! markus 291: fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n");
1.45 jakob 292: #ifdef SMARTCARD
293: fprintf(stderr, " -s reader Add key in smartcard reader.\n");
294: fprintf(stderr, " -e reader Remove key in smartcard reader.\n");
295: #endif
1.42 markus 296: }
297:
1.2 provos 298: int
1.7 markus 299: main(int argc, char **argv)
1.1 deraadt 300: {
1.43 markus 301: extern char *optarg;
302: extern int optind;
1.12 markus 303: AuthenticationConnection *ac = NULL;
1.44 markus 304: char *sc_reader_id = NULL;
1.46 djm 305: int i, ch, deleting = 0, ret = 0;
1.12 markus 306:
1.28 stevesk 307: SSLeay_add_all_algorithms();
1.19 markus 308:
1.12 markus 309: /* At first, get a connection to the authentication agent. */
310: ac = ssh_get_authentication_connection();
311: if (ac == NULL) {
312: fprintf(stderr, "Could not open a connection to your authentication agent.\n");
1.50 markus 313: exit(2);
1.12 markus 314: }
1.56 ! markus 315: while ((ch = getopt(argc, argv, "lLdDxXe:s:t:")) != -1) {
1.43 markus 316: switch (ch) {
317: case 'l':
318: case 'L':
1.50 markus 319: if (list_identities(ac, ch == 'l' ? 1 : 0) == -1)
1.54 markus 320: ret = 1;
321: goto done;
322: break;
323: case 'x':
324: case 'X':
325: if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1)
1.50 markus 326: ret = 1;
1.43 markus 327: goto done;
328: break;
329: case 'd':
1.12 markus 330: deleting = 1;
1.43 markus 331: break;
332: case 'D':
1.46 djm 333: if (delete_all(ac) == -1)
334: ret = 1;
1.43 markus 335: goto done;
336: break;
337: case 's':
1.44 markus 338: sc_reader_id = optarg;
1.43 markus 339: break;
340: case 'e':
1.47 deraadt 341: deleting = 1;
1.44 markus 342: sc_reader_id = optarg;
1.56 ! markus 343: break;
! 344: case 't':
! 345: lifetime = atoi(optarg);
1.43 markus 346: break;
347: default:
348: usage();
1.46 djm 349: ret = 1;
350: goto done;
1.42 markus 351: }
352: }
1.43 markus 353: argc -= optind;
354: argv += optind;
1.44 markus 355: if (sc_reader_id != NULL) {
1.46 djm 356: if (update_card(ac, !deleting, sc_reader_id) == -1)
357: ret = 1;
1.43 markus 358: goto done;
1.12 markus 359: }
1.43 markus 360: if (argc == 0) {
1.48 djm 361: char buf[MAXPATHLEN];
362: struct passwd *pw;
1.52 markus 363: struct stat st;
364: int count = 0;
1.48 djm 365:
366: if ((pw = getpwuid(getuid())) == NULL) {
1.20 deraadt 367: fprintf(stderr, "No user found with uid %u\n",
368: (u_int)getuid());
1.46 djm 369: ret = 1;
370: goto done;
1.12 markus 371: }
1.48 djm 372:
373: for(i = 0; default_files[i]; i++) {
1.51 markus 374: snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
1.48 djm 375: default_files[i]);
1.52 markus 376: if (stat(buf, &st) < 0)
377: continue;
1.48 djm 378: if (do_file(ac, deleting, buf) == -1)
1.46 djm 379: ret = 1;
1.52 markus 380: else
381: count++;
1.46 djm 382: }
1.52 markus 383: if (count == 0)
384: ret = 1;
1.43 markus 385: } else {
1.48 djm 386: for(i = 0; i < argc; i++) {
1.49 deraadt 387: if (do_file(ac, deleting, argv[i]) == -1)
1.48 djm 388: ret = 1;
1.43 markus 389: }
1.12 markus 390: }
1.33 markus 391: clear_pass();
1.43 markus 392:
393: done:
1.12 markus 394: ssh_close_authentication_connection(ac);
1.46 djm 395: return ret;
1.1 deraadt 396: }