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