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 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.22 2000/09/07 20:27:54 deraadt Exp $"); 39 40 #include <openssl/evp.h> 41 #include <openssl/rsa.h> 42 #include <openssl/dsa.h> 43 44 #include "rsa.h" 45 #include "ssh.h" 46 #include "xmalloc.h" 47 #include "key.h" 48 #include "authfd.h" 49 #include "authfile.h" 50 51 void 52 delete_file(AuthenticationConnection *ac, const char *filename) 53 { 54 Key *public; 55 char *comment; 56 57 public = key_new(KEY_RSA); 58 if (!load_public_key(filename, public, &comment)) { 59 key_free(public); 60 public = key_new(KEY_DSA); 61 if (!try_load_public_key(filename, public, &comment)) { 62 printf("Bad key file %s\n", filename); 63 return; 64 } 65 } 66 if (ssh_remove_identity(ac, public)) 67 fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); 68 else 69 fprintf(stderr, "Could not remove identity: %s\n", filename); 70 key_free(public); 71 xfree(comment); 72 } 73 74 /* Send a request to remove all identities. */ 75 void 76 delete_all(AuthenticationConnection *ac) 77 { 78 int success = 1; 79 80 if (!ssh_remove_all_identities(ac, 1)) 81 success = 0; 82 /* ignore error-code for ssh2 */ 83 ssh_remove_all_identities(ac, 2); 84 85 if (success) 86 fprintf(stderr, "All identities removed.\n"); 87 else 88 fprintf(stderr, "Failed to remove all identitities.\n"); 89 } 90 91 char * 92 ssh_askpass(char *askpass, char *msg) 93 { 94 pid_t pid; 95 size_t len; 96 char *nl, *pass; 97 int p[2], status; 98 char buf[1024]; 99 100 if (askpass == NULL) 101 fatal("internal error: askpass undefined"); 102 if (pipe(p) < 0) 103 fatal("ssh_askpass: pipe: %s", strerror(errno)); 104 if ((pid = fork()) < 0) 105 fatal("ssh_askpass: fork: %s", strerror(errno)); 106 if (pid == 0) { 107 close(p[0]); 108 if (dup2(p[1], STDOUT_FILENO) < 0) 109 fatal("ssh_askpass: dup2: %s", strerror(errno)); 110 execlp(askpass, askpass, msg, (char *) 0); 111 fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); 112 } 113 close(p[1]); 114 len = read(p[0], buf, sizeof buf); 115 close(p[0]); 116 while (waitpid(pid, &status, 0) < 0) 117 if (errno != EINTR) 118 break; 119 if (len <= 1) 120 return xstrdup(""); 121 nl = strchr(buf, '\n'); 122 if (nl) 123 *nl = '\0'; 124 pass = xstrdup(buf); 125 memset(buf, 0, sizeof(buf)); 126 return pass; 127 } 128 129 void 130 add_file(AuthenticationConnection *ac, const char *filename) 131 { 132 struct stat st; 133 Key *public; 134 Key *private; 135 char *saved_comment, *comment, *askpass = NULL; 136 char buf[1024], msg[1024]; 137 int success; 138 int interactive = isatty(STDIN_FILENO); 139 int type = KEY_RSA; 140 141 if (stat(filename, &st) < 0) { 142 perror(filename); 143 exit(1); 144 } 145 /* 146 * try to load the public key. right now this only works for RSA, 147 * since DSA keys are fully encrypted 148 */ 149 public = key_new(KEY_RSA); 150 if (!load_public_key(filename, public, &saved_comment)) { 151 /* ok, so we will asume this is a DSA key */ 152 type = KEY_DSA; 153 saved_comment = xstrdup(filename); 154 } 155 key_free(public); 156 157 if (!interactive && getenv("DISPLAY")) { 158 if (getenv(SSH_ASKPASS_ENV)) 159 askpass = getenv(SSH_ASKPASS_ENV); 160 else 161 askpass = SSH_ASKPASS_DEFAULT; 162 } 163 164 /* At first, try empty passphrase */ 165 private = key_new(type); 166 success = load_private_key(filename, "", private, &comment); 167 if (!success) { 168 printf("Need passphrase for %.200s\n", filename); 169 if (!interactive && askpass == NULL) { 170 xfree(saved_comment); 171 return; 172 } 173 snprintf(msg, sizeof msg, "Enter passphrase for %.200s", saved_comment); 174 for (;;) { 175 char *pass; 176 if (interactive) { 177 snprintf(buf, sizeof buf, "%s: ", msg); 178 pass = read_passphrase(buf, 1); 179 } else { 180 pass = ssh_askpass(askpass, msg); 181 } 182 if (strcmp(pass, "") == 0) { 183 xfree(pass); 184 xfree(saved_comment); 185 return; 186 } 187 success = load_private_key(filename, pass, private, &comment); 188 memset(pass, 0, strlen(pass)); 189 xfree(pass); 190 if (success) 191 break; 192 strlcpy(msg, "Bad passphrase, try again", sizeof msg); 193 } 194 } 195 xfree(comment); 196 if (ssh_add_identity(ac, private, saved_comment)) 197 fprintf(stderr, "Identity added: %s (%s)\n", filename, saved_comment); 198 else 199 fprintf(stderr, "Could not add identity: %s\n", filename); 200 key_free(private); 201 xfree(saved_comment); 202 } 203 204 void 205 list_identities(AuthenticationConnection *ac, int fp) 206 { 207 Key *key; 208 char *comment; 209 int had_identities = 0; 210 int version; 211 212 for (version = 1; version <= 2; version++) { 213 for (key = ssh_get_first_identity(ac, &comment, version); 214 key != NULL; 215 key = ssh_get_next_identity(ac, &comment, version)) { 216 had_identities = 1; 217 if (fp) { 218 printf("%d %s %s\n", 219 key_size(key), key_fingerprint(key), comment); 220 } else { 221 if (!key_write(key, stdout)) 222 fprintf(stderr, "key_write failed"); 223 fprintf(stdout, " %s\n", comment); 224 } 225 key_free(key); 226 xfree(comment); 227 } 228 } 229 if (!had_identities) 230 printf("The agent has no identities.\n"); 231 } 232 233 int 234 main(int argc, char **argv) 235 { 236 AuthenticationConnection *ac = NULL; 237 struct passwd *pw; 238 char buf[1024]; 239 int no_files = 1; 240 int i; 241 int deleting = 0; 242 243 /* check if RSA support exists */ 244 if (rsa_alive() == 0) { 245 extern char *__progname; 246 247 fprintf(stderr, 248 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", 249 __progname); 250 exit(1); 251 } 252 SSLeay_add_all_algorithms(); 253 254 /* At first, get a connection to the authentication agent. */ 255 ac = ssh_get_authentication_connection(); 256 if (ac == NULL) { 257 fprintf(stderr, "Could not open a connection to your authentication agent.\n"); 258 exit(1); 259 } 260 for (i = 1; i < argc; i++) { 261 if ((strcmp(argv[i], "-l") == 0) || 262 (strcmp(argv[i], "-L") == 0)) { 263 list_identities(ac, argv[i][1] == 'l' ? 1 : 0); 264 /* Don't default-add/delete if -l. */ 265 no_files = 0; 266 continue; 267 } 268 if (strcmp(argv[i], "-d") == 0) { 269 deleting = 1; 270 continue; 271 } 272 if (strcmp(argv[i], "-D") == 0) { 273 delete_all(ac); 274 no_files = 0; 275 continue; 276 } 277 no_files = 0; 278 if (deleting) 279 delete_file(ac, argv[i]); 280 else 281 add_file(ac, argv[i]); 282 } 283 if (no_files) { 284 pw = getpwuid(getuid()); 285 if (!pw) { 286 fprintf(stderr, "No user found with uid %u\n", 287 (u_int)getuid()); 288 ssh_close_authentication_connection(ac); 289 exit(1); 290 } 291 snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); 292 if (deleting) 293 delete_file(ac, buf); 294 else 295 add_file(ac, buf); 296 } 297 ssh_close_authentication_connection(ac); 298 exit(0); 299 } 300