1 /* 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. 6 * 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 * 13 * SSH2 implementation, 14 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 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. 35 */ 36 37 #include "includes.h" 38 RCSID("$OpenBSD: ssh-add.c,v 1.50 2002/01/29 14:27:57 markus Exp $"); 39 RCSID("$FreeBSD$"); 40 41 #include <openssl/evp.h> 42 43 #include "ssh.h" 44 #include "rsa.h" 45 #include "log.h" 46 #include "xmalloc.h" 47 #include "key.h" 48 #include "authfd.h" 49 #include "authfile.h" 50 #include "pathnames.h" 51 #include "readpass.h" 52 53 /* argv0 */ 54 extern char *__progname; 55 56 /* Default files to add */ 57 static char *default_files[] = { 58 _PATH_SSH_CLIENT_ID_RSA, 59 _PATH_SSH_CLIENT_ID_DSA, 60 _PATH_SSH_CLIENT_IDENTITY, 61 NULL 62 }; 63 64 65 /* we keep a cache of one passphrases */ 66 static char *pass = NULL; 67 static void 68 clear_pass(void) 69 { 70 if (pass) { 71 memset(pass, 0, strlen(pass)); 72 xfree(pass); 73 pass = NULL; 74 } 75 } 76 77 static int 78 delete_file(AuthenticationConnection *ac, const char *filename) 79 { 80 Key *public; 81 char *comment = NULL; 82 int ret = -1; 83 84 public = key_load_public(filename, &comment); 85 if (public == NULL) { 86 printf("Bad key file %s\n", filename); 87 return -1; 88 } 89 if (ssh_remove_identity(ac, public)) { 90 fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); 91 ret = 0; 92 } else 93 fprintf(stderr, "Could not remove identity: %s\n", filename); 94 95 key_free(public); 96 xfree(comment); 97 98 return ret; 99 } 100 101 /* Send a request to remove all identities. */ 102 static int 103 delete_all(AuthenticationConnection *ac) 104 { 105 int ret = -1; 106 107 if (ssh_remove_all_identities(ac, 1)) 108 ret = 0; 109 /* ignore error-code for ssh2 */ 110 ssh_remove_all_identities(ac, 2); 111 112 if (ret == 0) 113 fprintf(stderr, "All identities removed.\n"); 114 else 115 fprintf(stderr, "Failed to remove all identities.\n"); 116 117 return ret; 118 } 119 120 static int 121 add_file(AuthenticationConnection *ac, const char *filename) 122 { 123 struct stat st; 124 Key *private; 125 char *comment = NULL; 126 char msg[1024]; 127 int ret = -1; 128 129 if (stat(filename, &st) < 0) { 130 perror(filename); 131 return -1; 132 } 133 /* At first, try empty passphrase */ 134 private = key_load_private(filename, "", &comment); 135 if (comment == NULL) 136 comment = xstrdup(filename); 137 /* try last */ 138 if (private == NULL && pass != NULL) 139 private = key_load_private(filename, pass, NULL); 140 if (private == NULL) { 141 /* clear passphrase since it did not work */ 142 clear_pass(); 143 snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ", 144 comment); 145 for (;;) { 146 pass = read_passphrase(msg, RP_ALLOW_STDIN); 147 if (strcmp(pass, "") == 0) { 148 clear_pass(); 149 xfree(comment); 150 return -1; 151 } 152 private = key_load_private(filename, pass, &comment); 153 if (private != NULL) 154 break; 155 clear_pass(); 156 strlcpy(msg, "Bad passphrase, try again: ", sizeof msg); 157 } 158 } 159 if (ssh_add_identity(ac, private, comment)) { 160 fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); 161 ret = 0; 162 } else 163 fprintf(stderr, "Could not add identity: %s\n", filename); 164 165 xfree(comment); 166 key_free(private); 167 168 return ret; 169 } 170 171 static int 172 update_card(AuthenticationConnection *ac, int add, const char *id) 173 { 174 if (ssh_update_card(ac, add, id)) { 175 fprintf(stderr, "Card %s: %s\n", 176 add ? "added" : "removed", id); 177 return 0; 178 } else { 179 fprintf(stderr, "Could not %s card: %s\n", 180 add ? "add" : "remove", id); 181 return -1; 182 } 183 } 184 185 static int 186 list_identities(AuthenticationConnection *ac, int do_fp) 187 { 188 Key *key; 189 char *comment, *fp; 190 int had_identities = 0; 191 int version; 192 193 for (version = 1; version <= 2; version++) { 194 for (key = ssh_get_first_identity(ac, &comment, version); 195 key != NULL; 196 key = ssh_get_next_identity(ac, &comment, version)) { 197 had_identities = 1; 198 if (do_fp) { 199 fp = key_fingerprint(key, SSH_FP_MD5, 200 SSH_FP_HEX); 201 printf("%d %s %s (%s)\n", 202 key_size(key), fp, comment, key_type(key)); 203 xfree(fp); 204 } else { 205 if (!key_write(key, stdout)) 206 fprintf(stderr, "key_write failed"); 207 fprintf(stdout, " %s\n", comment); 208 } 209 key_free(key); 210 xfree(comment); 211 } 212 } 213 if (!had_identities) { 214 printf("The agent has no identities.\n"); 215 return -1; 216 } 217 return 0; 218 } 219 220 static int 221 do_file(AuthenticationConnection *ac, int deleting, char *file) 222 { 223 if (deleting) { 224 if (delete_file(ac, file) == -1) 225 return -1; 226 } else { 227 if (add_file(ac, file) == -1) 228 return -1; 229 } 230 return 0; 231 } 232 233 static void 234 usage(void) 235 { 236 fprintf(stderr, "Usage: %s [options]\n", __progname); 237 fprintf(stderr, "Options:\n"); 238 fprintf(stderr, " -l List fingerprints of all identities.\n"); 239 fprintf(stderr, " -L List public key parameters of all identities.\n"); 240 fprintf(stderr, " -d Delete identity.\n"); 241 fprintf(stderr, " -D Delete all identities.\n"); 242 #ifdef SMARTCARD 243 fprintf(stderr, " -s reader Add key in smartcard reader.\n"); 244 fprintf(stderr, " -e reader Remove key in smartcard reader.\n"); 245 #endif 246 } 247 248 int 249 main(int argc, char **argv) 250 { 251 extern char *optarg; 252 extern int optind; 253 AuthenticationConnection *ac = NULL; 254 char *sc_reader_id = NULL; 255 int i, ch, deleting = 0, ret = 0; 256 257 SSLeay_add_all_algorithms(); 258 259 /* At first, get a connection to the authentication agent. */ 260 ac = ssh_get_authentication_connection(); 261 if (ac == NULL) { 262 fprintf(stderr, "Could not open a connection to your authentication agent.\n"); 263 exit(2); 264 } 265 while ((ch = getopt(argc, argv, "lLdDe:s:")) != -1) { 266 switch (ch) { 267 case 'l': 268 case 'L': 269 if (list_identities(ac, ch == 'l' ? 1 : 0) == -1) 270 ret = 1; 271 goto done; 272 break; 273 case 'd': 274 deleting = 1; 275 break; 276 case 'D': 277 if (delete_all(ac) == -1) 278 ret = 1; 279 goto done; 280 break; 281 case 's': 282 sc_reader_id = optarg; 283 break; 284 case 'e': 285 deleting = 1; 286 sc_reader_id = optarg; 287 break; 288 default: 289 usage(); 290 ret = 1; 291 goto done; 292 } 293 } 294 argc -= optind; 295 argv += optind; 296 if (sc_reader_id != NULL) { 297 if (update_card(ac, !deleting, sc_reader_id) == -1) 298 ret = 1; 299 goto done; 300 } 301 if (argc == 0) { 302 char buf[MAXPATHLEN]; 303 struct passwd *pw; 304 305 if ((pw = getpwuid(getuid())) == NULL) { 306 fprintf(stderr, "No user found with uid %u\n", 307 (u_int)getuid()); 308 ret = 1; 309 goto done; 310 } 311 312 for(i = 0; default_files[i]; i++) { 313 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, 314 default_files[i]); 315 if (do_file(ac, deleting, buf) == -1) 316 ret = 1; 317 } 318 } else { 319 for(i = 0; i < argc; i++) { 320 if (do_file(ac, deleting, argv[i]) == -1) 321 ret = 1; 322 } 323 } 324 clear_pass(); 325 326 done: 327 ssh_close_authentication_connection(ac); 328 return ret; 329 } 330