1 /* 2 * Copyright 2008 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 #include <openssl/evp.h> 45 46 #include "ssh.h" 47 #include "rsa.h" 48 #include "log.h" 49 #include "xmalloc.h" 50 #include "key.h" 51 #include "authfd.h" 52 #include "authfile.h" 53 #include "pathnames.h" 54 #include "readpass.h" 55 #include "misc.h" 56 57 #ifdef HAVE___PROGNAME 58 extern char *__progname; 59 #else 60 char *__progname; 61 #endif 62 63 /* argv0 */ 64 extern char *__progname; 65 66 /* Default files to add */ 67 static char *default_files[] = { 68 _PATH_SSH_CLIENT_ID_RSA, 69 _PATH_SSH_CLIENT_ID_DSA, 70 _PATH_SSH_CLIENT_IDENTITY, 71 NULL 72 }; 73 74 /* Default lifetime (0 == forever) */ 75 static int lifetime = 0; 76 77 /* we keep a cache of one passphrases */ 78 static char *pass = NULL; 79 static void 80 clear_pass(void) 81 { 82 if (pass) { 83 memset(pass, 0, strlen(pass)); 84 xfree(pass); 85 pass = NULL; 86 } 87 } 88 89 static int 90 delete_file(AuthenticationConnection *ac, const char *filename) 91 { 92 Key *public; 93 char *comment = NULL; 94 int ret = -1; 95 96 public = key_load_public(filename, &comment); 97 if (public == NULL) { 98 printf(gettext("Bad key file %s\n"), filename); 99 return -1; 100 } 101 if (ssh_remove_identity(ac, public)) { 102 fprintf(stderr, gettext("Identity removed: %s (%s)\n"), 103 filename, comment); 104 ret = 0; 105 } else 106 fprintf(stderr, gettext("Could not remove identity: %s\n"), 107 filename); 108 109 key_free(public); 110 xfree(comment); 111 112 return ret; 113 } 114 115 /* Send a request to remove all identities. */ 116 static int 117 delete_all(AuthenticationConnection *ac) 118 { 119 int ret = -1; 120 121 if (ssh_remove_all_identities(ac, 1)) 122 ret = 0; 123 /* ignore error-code for ssh2 */ 124 ssh_remove_all_identities(ac, 2); 125 126 if (ret == 0) 127 fprintf(stderr, gettext("All identities removed.\n")); 128 else 129 fprintf(stderr, gettext("Failed to remove all identities.\n")); 130 131 return ret; 132 } 133 134 static int 135 add_file(AuthenticationConnection *ac, const char *filename) 136 { 137 struct stat st; 138 Key *private; 139 char *comment = NULL; 140 char msg[1024]; 141 int ret = -1; 142 143 if (stat(filename, &st) < 0) { 144 perror(filename); 145 return -1; 146 } 147 /* At first, try empty passphrase */ 148 private = key_load_private(filename, "", &comment); 149 if (comment == NULL) 150 comment = xstrdup(filename); 151 /* try last */ 152 if (private == NULL && pass != NULL) 153 private = key_load_private(filename, pass, NULL); 154 if (private == NULL) { 155 /* clear passphrase since it did not work */ 156 clear_pass(); 157 snprintf(msg, sizeof msg, 158 gettext("Enter passphrase for %.200s: "), comment); 159 for (;;) { 160 pass = read_passphrase(msg, RP_ALLOW_STDIN); 161 if (strcmp(pass, "") == 0) { 162 clear_pass(); 163 xfree(comment); 164 return -1; 165 } 166 private = key_load_private(filename, pass, &comment); 167 if (private != NULL) 168 break; 169 clear_pass(); 170 strlcpy(msg, gettext("Bad passphrase, try again: "), 171 sizeof msg); 172 } 173 } 174 175 if (ssh_add_identity_constrained(ac, private, comment, lifetime)) { 176 fprintf(stderr, gettext("Identity added: %s (%s)\n"), 177 filename, comment); 178 ret = 0; 179 if (lifetime != 0) 180 fprintf(stderr, 181 gettext("Lifetime set to %d seconds\n"), lifetime); 182 } else if (ssh_add_identity(ac, private, comment)) { 183 fprintf(stderr, gettext("Identity added: %s (%s)\n"), 184 filename, comment); 185 ret = 0; 186 } else { 187 fprintf(stderr, gettext("Could not add identity: %s\n"), 188 filename); 189 } 190 191 xfree(comment); 192 key_free(private); 193 194 return ret; 195 } 196 197 static int 198 list_identities(AuthenticationConnection *ac, int do_fp) 199 { 200 Key *key; 201 char *comment, *fp; 202 int had_identities = 0; 203 int version; 204 205 for (version = 1; version <= 2; version++) { 206 for (key = ssh_get_first_identity(ac, &comment, version); 207 key != NULL; 208 key = ssh_get_next_identity(ac, &comment, version)) { 209 had_identities = 1; 210 if (do_fp) { 211 fp = key_fingerprint(key, SSH_FP_MD5, 212 SSH_FP_HEX); 213 printf("%d %s %s (%s)\n", 214 key_size(key), fp, comment, key_type(key)); 215 xfree(fp); 216 } else { 217 if (!key_write(key, stdout)) 218 fprintf(stderr, 219 gettext("key_write failed")); 220 fprintf(stdout, " %s\n", comment); 221 } 222 key_free(key); 223 xfree(comment); 224 } 225 } 226 if (!had_identities) { 227 printf(gettext("The agent has no identities.\n")); 228 return -1; 229 } 230 return 0; 231 } 232 233 static int 234 lock_agent(AuthenticationConnection *ac, int lock) 235 { 236 char prompt[100], *p1, *p2; 237 int passok = 1, ret = -1; 238 239 strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); 240 p1 = read_passphrase(prompt, RP_ALLOW_STDIN); 241 if (lock) { 242 strlcpy(prompt, "Again: ", sizeof prompt); 243 p2 = read_passphrase(prompt, RP_ALLOW_STDIN); 244 if (strcmp(p1, p2) != 0) { 245 fprintf(stderr, gettext("Passwords do not match.\n")); 246 passok = 0; 247 } 248 memset(p2, 0, strlen(p2)); 249 xfree(p2); 250 } 251 if (passok && ssh_lock_agent(ac, lock, p1)) { 252 if (lock) 253 fprintf(stderr, gettext("Agent locked.\n")); 254 else 255 fprintf(stderr, gettext("Agent unlocked.\n")); 256 ret = 0; 257 } else { 258 if (lock) 259 fprintf(stderr, gettext("Failed to lock agent.\n")); 260 else 261 fprintf(stderr, gettext("Failed to unlock agent.\n")); 262 } 263 memset(p1, 0, strlen(p1)); 264 xfree(p1); 265 return (ret); 266 } 267 268 static int 269 do_file(AuthenticationConnection *ac, int deleting, char *file) 270 { 271 if (deleting) { 272 if (delete_file(ac, file) == -1) 273 return -1; 274 } else { 275 if (add_file(ac, file) == -1) 276 return -1; 277 } 278 return 0; 279 } 280 281 static void 282 usage(void) 283 { 284 fprintf(stderr, 285 gettext( "Usage: %s [options]\n" 286 "Options:\n" 287 " -l List fingerprints of all identities.\n" 288 " -L List public key parameters of all identities.\n" 289 " -d Delete identity.\n" 290 " -D Delete all identities.\n" 291 " -x Lock agent.\n" 292 " -X Unlock agent.\n" 293 " -t life Set lifetime (seconds) when adding identities.\n" 294 ), __progname); 295 } 296 297 int 298 main(int argc, char **argv) 299 { 300 extern char *optarg; 301 extern int optind; 302 AuthenticationConnection *ac = NULL; 303 int i, ch, deleting = 0, ret = 0; 304 305 __progname = get_progname(argv[0]); 306 307 (void) g11n_setlocale(LC_ALL, ""); 308 309 init_rng(); 310 seed_rng(); 311 312 SSLeay_add_all_algorithms(); 313 314 /* At first, get a connection to the authentication agent. */ 315 ac = ssh_get_authentication_connection(); 316 if (ac == NULL) { 317 fprintf(stderr, gettext("Could not open a connection " 318 "to your authentication agent.\n")); 319 exit(2); 320 } 321 while ((ch = getopt(argc, argv, "lLdDxXe:s:t:")) != -1) { 322 switch (ch) { 323 case 'l': 324 case 'L': 325 if (list_identities(ac, ch == 'l' ? 1 : 0) == -1) 326 ret = 1; 327 goto done; 328 break; 329 case 'x': 330 case 'X': 331 if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1) 332 ret = 1; 333 goto done; 334 break; 335 case 'd': 336 deleting = 1; 337 break; 338 case 'D': 339 if (delete_all(ac) == -1) 340 ret = 1; 341 goto done; 342 break; 343 case 't': 344 if ((lifetime = convtime(optarg)) == -1) { 345 fprintf(stderr, gettext("Invalid lifetime\n")); 346 ret = 1; 347 goto done; 348 } 349 break; 350 default: 351 usage(); 352 ret = 1; 353 goto done; 354 } 355 } 356 argc -= optind; 357 argv += optind; 358 if (argc == 0) { 359 char buf[MAXPATHLEN]; 360 struct passwd *pw; 361 struct stat st; 362 int count = 0; 363 364 if ((pw = getpwuid(getuid())) == NULL) { 365 fprintf(stderr, gettext("No user found with uid %u\n"), 366 (u_int)getuid()); 367 ret = 1; 368 goto done; 369 } 370 371 for(i = 0; default_files[i]; i++) { 372 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, 373 default_files[i]); 374 if (stat(buf, &st) < 0) 375 continue; 376 if (do_file(ac, deleting, buf) == -1) 377 ret = 1; 378 else 379 count++; 380 } 381 if (count == 0) 382 ret = 1; 383 } else { 384 for(i = 0; i < argc; i++) { 385 if (do_file(ac, deleting, argv[i]) == -1) 386 ret = 1; 387 } 388 } 389 clear_pass(); 390 391 done: 392 ssh_close_authentication_connection(ac); 393 return ret; 394 }