Annotation of src/crypto/external/bsd/openssh/dist/ssh-add.c, Revision 1.6.2.1
1.6.2.1 ! tls 1: /* $NetBSD$ */
! 2: /* $OpenBSD: ssh-add.c,v 1.105 2012/12/05 15:42:52 markus Exp $ */
1.1 christos 3: /*
4: * Author: Tatu Ylonen <ylo@cs.hut.fi>
5: * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6: * All rights reserved
7: * Adds an identity to the authentication server, or removes an identity.
8: *
9: * As far as I am concerned, the code I have written for this software
10: * can be used freely for any purpose. Any derived versions of this
11: * software must be clearly marked as such, and if the derived work is
12: * incompatible with the protocol description in the RFC file, it must be
13: * called by a name other than "ssh" or "Secure Shell".
14: *
15: * SSH2 implementation,
16: * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
17: *
18: * Redistribution and use in source and binary forms, with or without
19: * modification, are permitted provided that the following conditions
20: * are met:
21: * 1. Redistributions of source code must retain the above copyright
22: * notice, this list of conditions and the following disclaimer.
23: * 2. Redistributions in binary form must reproduce the above copyright
24: * notice, this list of conditions and the following disclaimer in the
25: * documentation and/or other materials provided with the distribution.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37: */
38:
1.2 christos 39: #include "includes.h"
1.6.2.1 ! tls 40: __RCSID("$NetBSD$");
1.1 christos 41: #include <sys/types.h>
42: #include <sys/stat.h>
43: #include <sys/param.h>
44:
45: #include <openssl/evp.h>
46:
47: #include <fcntl.h>
48: #include <pwd.h>
49: #include <stdio.h>
50: #include <stdlib.h>
51: #include <string.h>
52: #include <unistd.h>
53:
54: #include "xmalloc.h"
55: #include "ssh.h"
56: #include "rsa.h"
57: #include "log.h"
58: #include "key.h"
59: #include "buffer.h"
60: #include "authfd.h"
61: #include "authfile.h"
62: #include "pathnames.h"
63: #include "misc.h"
64:
65: /* argv0 */
66: extern char *__progname;
67:
68: /* Default files to add */
1.4 christos 69: static const char *default_files[] = {
1.1 christos 70: _PATH_SSH_CLIENT_ID_RSA,
71: _PATH_SSH_CLIENT_ID_DSA,
1.4 christos 72: _PATH_SSH_CLIENT_ID_ECDSA,
1.1 christos 73: _PATH_SSH_CLIENT_IDENTITY,
74: NULL
75: };
76:
77: /* Default lifetime (0 == forever) */
78: static int lifetime = 0;
79:
80: /* User has to confirm key use */
81: static int confirm = 0;
82:
83: /* we keep a cache of one passphrases */
84: static char *pass = NULL;
85: static void
86: clear_pass(void)
87: {
88: if (pass) {
89: memset(pass, 0, strlen(pass));
90: xfree(pass);
91: pass = NULL;
92: }
93: }
94:
95: static int
1.6.2.1 ! tls 96: delete_file(AuthenticationConnection *ac, const char *filename, int key_only)
1.1 christos 97: {
1.6.2.1 ! tls 98: Key *public = NULL, *cert = NULL;
! 99: char *certpath = NULL, *comment = NULL;
1.1 christos 100: int ret = -1;
101:
102: public = key_load_public(filename, &comment);
103: if (public == NULL) {
104: printf("Bad key file %s\n", filename);
105: return -1;
106: }
107: if (ssh_remove_identity(ac, public)) {
108: fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment);
109: ret = 0;
110: } else
111: fprintf(stderr, "Could not remove identity: %s\n", filename);
112:
1.6.2.1 ! tls 113: if (key_only)
! 114: goto out;
! 115:
! 116: /* Now try to delete the corresponding certificate too */
! 117: free(comment);
! 118: comment = NULL;
! 119: xasprintf(&certpath, "%s-cert.pub", filename);
! 120: if ((cert = key_load_public(certpath, &comment)) == NULL)
! 121: goto out;
! 122: if (!key_equal_public(cert, public))
! 123: fatal("Certificate %s does not match private key %s",
! 124: certpath, filename);
! 125:
! 126: if (ssh_remove_identity(ac, cert)) {
! 127: fprintf(stderr, "Identity removed: %s (%s)\n", certpath,
! 128: comment);
! 129: ret = 0;
! 130: } else
! 131: fprintf(stderr, "Could not remove identity: %s\n", certpath);
! 132:
! 133: out:
! 134: if (cert != NULL)
! 135: key_free(cert);
! 136: if (public != NULL)
! 137: key_free(public);
! 138: free(certpath);
! 139: free(comment);
1.1 christos 140:
141: return ret;
142: }
143:
144: /* Send a request to remove all identities. */
145: static int
146: delete_all(AuthenticationConnection *ac)
147: {
148: int ret = -1;
149:
150: if (ssh_remove_all_identities(ac, 1))
151: ret = 0;
152: /* ignore error-code for ssh2 */
153: ssh_remove_all_identities(ac, 2);
154:
155: if (ret == 0)
156: fprintf(stderr, "All identities removed.\n");
157: else
158: fprintf(stderr, "Failed to remove all identities.\n");
159:
160: return ret;
161: }
162:
163: static int
1.6 christos 164: add_file(AuthenticationConnection *ac, const char *filename, int key_only)
1.1 christos 165: {
1.3 adam 166: Key *private, *cert;
1.1 christos 167: char *comment = NULL;
1.6 christos 168: char msg[1024], *certpath = NULL;
1.1 christos 169: int fd, perms_ok, ret = -1;
1.5 christos 170: Buffer keyblob;
1.1 christos 171:
1.5 christos 172: if (strcmp(filename, "-") == 0) {
173: fd = STDIN_FILENO;
174: filename = "(stdin)";
175: } else if ((fd = open(filename, O_RDONLY)) < 0) {
1.1 christos 176: perror(filename);
177: return -1;
178: }
179:
180: /*
181: * Since we'll try to load a keyfile multiple times, permission errors
182: * will occur multiple times, so check perms first and bail if wrong.
183: */
1.5 christos 184: if (fd != STDIN_FILENO) {
185: perms_ok = key_perm_ok(fd, filename);
186: if (!perms_ok) {
187: close(fd);
188: return -1;
189: }
190: }
191: buffer_init(&keyblob);
192: if (!key_load_file(fd, filename, &keyblob)) {
193: buffer_free(&keyblob);
194: close(fd);
195: return -1;
196: }
1.1 christos 197: close(fd);
198:
199: /* At first, try empty passphrase */
1.5 christos 200: private = key_parse_private(&keyblob, filename, "", &comment);
1.1 christos 201: if (comment == NULL)
202: comment = xstrdup(filename);
203: /* try last */
204: if (private == NULL && pass != NULL)
1.5 christos 205: private = key_parse_private(&keyblob, filename, pass, NULL);
1.1 christos 206: if (private == NULL) {
207: /* clear passphrase since it did not work */
208: clear_pass();
209: snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ",
210: comment);
211: for (;;) {
212: pass = read_passphrase(msg, RP_ALLOW_STDIN);
213: if (strcmp(pass, "") == 0) {
214: clear_pass();
215: xfree(comment);
1.5 christos 216: buffer_free(&keyblob);
1.1 christos 217: return -1;
218: }
1.5 christos 219: private = key_parse_private(&keyblob, filename, pass,
220: &comment);
1.1 christos 221: if (private != NULL)
222: break;
223: clear_pass();
224: snprintf(msg, sizeof msg,
225: "Bad passphrase, try again for %.200s: ", comment);
226: }
227: }
1.5 christos 228: buffer_free(&keyblob);
1.1 christos 229:
230: if (ssh_add_identity_constrained(ac, private, comment, lifetime,
231: confirm)) {
232: fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
233: ret = 0;
234: if (lifetime != 0)
235: fprintf(stderr,
236: "Lifetime set to %d seconds\n", lifetime);
237: if (confirm != 0)
238: fprintf(stderr,
1.3 adam 239: "The user must confirm each use of the key\n");
1.1 christos 240: } else {
241: fprintf(stderr, "Could not add identity: %s\n", filename);
242: }
243:
1.6 christos 244: /* Skip trying to load the cert if requested */
245: if (key_only)
246: goto out;
1.3 adam 247:
248: /* Now try to add the certificate flavour too */
249: xasprintf(&certpath, "%s-cert.pub", filename);
250: if ((cert = key_load_public(certpath, NULL)) == NULL)
251: goto out;
252:
253: if (!key_equal_public(cert, private)) {
254: error("Certificate %s does not match private key %s",
255: certpath, filename);
256: key_free(cert);
257: goto out;
258: }
259:
260: /* Graft with private bits */
261: if (key_to_certified(private, key_cert_is_legacy(cert)) != 0) {
262: error("%s: key_to_certified failed", __func__);
263: key_free(cert);
264: goto out;
265: }
266: key_cert_copy(cert, private);
267: key_free(cert);
268:
269: if (!ssh_add_identity_constrained(ac, private, comment,
270: lifetime, confirm)) {
271: error("Certificate %s (%s) add failed", certpath,
272: private->cert->key_id);
273: }
274: fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
275: private->cert->key_id);
276: if (lifetime != 0)
277: fprintf(stderr, "Lifetime set to %d seconds\n", lifetime);
278: if (confirm != 0)
279: fprintf(stderr, "The user must confirm each use of the key\n");
280: out:
1.6 christos 281: if (certpath != NULL)
282: xfree(certpath);
1.1 christos 283: xfree(comment);
284: key_free(private);
285:
286: return ret;
287: }
288:
289: static int
290: update_card(AuthenticationConnection *ac, int add, const char *id)
291: {
292: char *pin;
293: int ret = -1;
294:
1.3 adam 295: pin = read_passphrase("Enter passphrase for PKCS#11: ", RP_ALLOW_STDIN);
1.1 christos 296: if (pin == NULL)
297: return -1;
298:
299: if (ssh_update_card(ac, add, id, pin, lifetime, confirm)) {
300: fprintf(stderr, "Card %s: %s\n",
301: add ? "added" : "removed", id);
302: ret = 0;
303: } else {
304: fprintf(stderr, "Could not %s card: %s\n",
305: add ? "add" : "remove", id);
306: ret = -1;
307: }
308: xfree(pin);
309: return ret;
310: }
311:
312: static int
313: list_identities(AuthenticationConnection *ac, int do_fp)
314: {
315: Key *key;
316: char *comment, *fp;
317: int had_identities = 0;
318: int version;
319:
320: for (version = 1; version <= 2; version++) {
321: for (key = ssh_get_first_identity(ac, &comment, version);
322: key != NULL;
323: key = ssh_get_next_identity(ac, &comment, version)) {
324: had_identities = 1;
325: if (do_fp) {
326: fp = key_fingerprint(key, SSH_FP_MD5,
327: SSH_FP_HEX);
328: printf("%d %s %s (%s)\n",
329: key_size(key), fp, comment, key_type(key));
330: xfree(fp);
331: } else {
332: if (!key_write(key, stdout))
333: fprintf(stderr, "key_write failed");
334: fprintf(stdout, " %s\n", comment);
335: }
336: key_free(key);
337: xfree(comment);
338: }
339: }
340: if (!had_identities) {
341: printf("The agent has no identities.\n");
342: return -1;
343: }
344: return 0;
345: }
346:
347: static int
348: lock_agent(AuthenticationConnection *ac, int lock)
349: {
350: char prompt[100], *p1, *p2;
351: int passok = 1, ret = -1;
352:
353: strlcpy(prompt, "Enter lock password: ", sizeof(prompt));
354: p1 = read_passphrase(prompt, RP_ALLOW_STDIN);
355: if (lock) {
356: strlcpy(prompt, "Again: ", sizeof prompt);
357: p2 = read_passphrase(prompt, RP_ALLOW_STDIN);
358: if (strcmp(p1, p2) != 0) {
359: fprintf(stderr, "Passwords do not match.\n");
360: passok = 0;
361: }
362: memset(p2, 0, strlen(p2));
363: xfree(p2);
364: }
365: if (passok && ssh_lock_agent(ac, lock, p1)) {
366: fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un");
367: ret = 0;
368: } else
369: fprintf(stderr, "Failed to %slock agent.\n", lock ? "" : "un");
370: memset(p1, 0, strlen(p1));
371: xfree(p1);
372: return (ret);
373: }
374:
375: static int
1.6 christos 376: do_file(AuthenticationConnection *ac, int deleting, int key_only, char *file)
1.1 christos 377: {
378: if (deleting) {
1.6.2.1 ! tls 379: if (delete_file(ac, file, key_only) == -1)
1.1 christos 380: return -1;
381: } else {
1.6 christos 382: if (add_file(ac, file, key_only) == -1)
1.1 christos 383: return -1;
384: }
385: return 0;
386: }
387:
388: static void
389: usage(void)
390: {
391: fprintf(stderr, "usage: %s [options] [file ...]\n", __progname);
392: fprintf(stderr, "Options:\n");
393: fprintf(stderr, " -l List fingerprints of all identities.\n");
394: fprintf(stderr, " -L List public key parameters of all identities.\n");
1.6 christos 395: fprintf(stderr, " -k Load only keys and not certificates.\n");
396: fprintf(stderr, " -c Require confirmation to sign using identities\n");
397: fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n");
1.1 christos 398: fprintf(stderr, " -d Delete identity.\n");
399: fprintf(stderr, " -D Delete all identities.\n");
400: fprintf(stderr, " -x Lock agent.\n");
401: fprintf(stderr, " -X Unlock agent.\n");
1.3 adam 402: fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n");
403: fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n");
1.1 christos 404: }
405:
406: int
407: main(int argc, char **argv)
408: {
409: extern char *optarg;
410: extern int optind;
411: AuthenticationConnection *ac = NULL;
1.3 adam 412: char *pkcs11provider = NULL;
1.6 christos 413: int i, ch, deleting = 0, ret = 0, key_only = 0;
1.1 christos 414:
415: /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
416: sanitise_stdfd();
417:
1.4 christos 418: OpenSSL_add_all_algorithms();
1.1 christos 419:
420: /* At first, get a connection to the authentication agent. */
421: ac = ssh_get_authentication_connection();
422: if (ac == NULL) {
423: fprintf(stderr,
424: "Could not open a connection to your authentication agent.\n");
425: exit(2);
426: }
1.6 christos 427: while ((ch = getopt(argc, argv, "klLcdDxXe:s:t:")) != -1) {
1.1 christos 428: switch (ch) {
1.6 christos 429: case 'k':
430: key_only = 1;
431: break;
1.1 christos 432: case 'l':
433: case 'L':
434: if (list_identities(ac, ch == 'l' ? 1 : 0) == -1)
435: ret = 1;
436: goto done;
437: case 'x':
438: case 'X':
439: if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1)
440: ret = 1;
441: goto done;
442: case 'c':
443: confirm = 1;
444: break;
445: case 'd':
446: deleting = 1;
447: break;
448: case 'D':
449: if (delete_all(ac) == -1)
450: ret = 1;
451: goto done;
452: case 's':
1.3 adam 453: pkcs11provider = optarg;
1.1 christos 454: break;
455: case 'e':
456: deleting = 1;
1.3 adam 457: pkcs11provider = optarg;
1.1 christos 458: break;
459: case 't':
460: if ((lifetime = convtime(optarg)) == -1) {
461: fprintf(stderr, "Invalid lifetime\n");
462: ret = 1;
463: goto done;
464: }
465: break;
466: default:
467: usage();
468: ret = 1;
469: goto done;
470: }
471: }
472: argc -= optind;
473: argv += optind;
1.3 adam 474: if (pkcs11provider != NULL) {
475: if (update_card(ac, !deleting, pkcs11provider) == -1)
1.1 christos 476: ret = 1;
477: goto done;
478: }
479: if (argc == 0) {
480: char buf[MAXPATHLEN];
481: struct passwd *pw;
482: struct stat st;
483: int count = 0;
484:
485: if ((pw = getpwuid(getuid())) == NULL) {
486: fprintf(stderr, "No user found with uid %u\n",
487: (u_int)getuid());
488: ret = 1;
489: goto done;
490: }
491:
492: for (i = 0; default_files[i]; i++) {
493: snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir,
494: default_files[i]);
495: if (stat(buf, &st) < 0)
496: continue;
1.6 christos 497: if (do_file(ac, deleting, key_only, buf) == -1)
1.1 christos 498: ret = 1;
499: else
500: count++;
501: }
502: if (count == 0)
503: ret = 1;
504: } else {
505: for (i = 0; i < argc; i++) {
1.6 christos 506: if (do_file(ac, deleting, key_only, argv[i]) == -1)
1.1 christos 507: ret = 1;
508: }
509: }
510: clear_pass();
511:
512: done:
513: ssh_close_authentication_connection(ac);
514: return ret;
515: }
CVSweb <webmaster@jp.NetBSD.org>