]>
Commit | Line | Data |
---|---|---|
8efc0c15 | 1 | /* |
2 | ||
3 | ssh-add.c | |
4 | ||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | |
6 | ||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
8 | All rights reserved | |
9 | ||
10 | Created: Thu Apr 6 00:52:24 1995 ylo | |
11 | ||
12 | Adds an identity to the authentication server, or removes an identity. | |
13 | ||
14 | */ | |
15 | ||
16 | #include "includes.h" | |
17 | RCSID("$Id$"); | |
18 | ||
19 | #include "rsa.h" | |
20 | #include "ssh.h" | |
21 | #include "xmalloc.h" | |
22 | #include "authfd.h" | |
23 | ||
dad9a31e | 24 | #ifdef USE_EXTERNAL_ASKPASS |
25 | int askpass(const char *filename, RSA *key, const char *saved_comment, char **comment); | |
26 | #endif /* USE_EXTERNAL_ASKPASS */ | |
27 | ||
f601d847 | 28 | #ifdef HAVE___PROGNAME |
29 | extern char *__progname; | |
30 | #else /* HAVE___PROGNAME */ | |
31 | const char *__progname = "ssh-add"; | |
32 | #endif /* HAVE___PROGNAME */ | |
33 | ||
8efc0c15 | 34 | void |
5068f573 | 35 | delete_file(AuthenticationConnection *ac, const char *filename) |
8efc0c15 | 36 | { |
37 | RSA *key; | |
38 | char *comment; | |
8efc0c15 | 39 | |
40 | key = RSA_new(); | |
41 | if (!load_public_key(filename, key, &comment)) | |
42 | { | |
43 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | |
44 | return; | |
45 | } | |
46 | ||
8efc0c15 | 47 | if (ssh_remove_identity(ac, key)) |
48 | fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); | |
49 | else | |
50 | fprintf(stderr, "Could not remove identity: %s\n", filename); | |
51 | RSA_free(key); | |
52 | xfree(comment); | |
8efc0c15 | 53 | } |
54 | ||
55 | void | |
5068f573 | 56 | delete_all(AuthenticationConnection *ac) |
8efc0c15 | 57 | { |
8efc0c15 | 58 | /* Send a request to remove all identities. */ |
59 | if (ssh_remove_all_identities(ac)) | |
60 | fprintf(stderr, "All identities removed.\n"); | |
61 | else | |
62 | fprintf(stderr, "Failed to remove all identitities.\n"); | |
8efc0c15 | 63 | } |
64 | ||
65 | void | |
5068f573 | 66 | add_file(AuthenticationConnection *ac, const char *filename) |
8efc0c15 | 67 | { |
68 | RSA *key; | |
69 | RSA *public_key; | |
dad9a31e | 70 | char *saved_comment, *comment; |
71 | int success; | |
72 | ||
8efc0c15 | 73 | key = RSA_new(); |
74 | public_key = RSA_new(); | |
75 | if (!load_public_key(filename, public_key, &saved_comment)) | |
76 | { | |
77 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | |
78 | return; | |
79 | } | |
80 | RSA_free(public_key); | |
8efc0c15 | 81 | |
dad9a31e | 82 | /* At first, try empty passphrase */ |
83 | success = load_private_key(filename, "", key, &comment); | |
84 | if (!success) { | |
85 | printf("Need passphrase for %s (%s).\n", filename, saved_comment); | |
86 | if (!isatty(STDIN_FILENO)) { | |
87 | #ifdef USE_EXTERNAL_ASKPASS | |
88 | int prompts = 3; | |
89 | ||
90 | while (prompts && !success) | |
91 | { | |
92 | success = askpass(filename, key, saved_comment, &comment); | |
93 | prompts--; | |
94 | } | |
95 | if (!success) | |
96 | { | |
97 | xfree(saved_comment); | |
98 | return; | |
99 | } | |
100 | #else /* !USE_EXTERNAL_ASKPASS */ | |
045672f9 | 101 | xfree(saved_comment); |
102 | return; | |
dad9a31e | 103 | #endif /* USE_EXTERNAL_ASKPASS */ |
8efc0c15 | 104 | } |
8efc0c15 | 105 | |
dad9a31e | 106 | while (!success) { |
107 | char *pass = read_passphrase("Enter passphrase: ", 1); | |
108 | if (strcmp(pass, "") == 0){ | |
109 | xfree(pass); | |
110 | xfree(saved_comment); | |
111 | return; | |
112 | } | |
113 | success = load_private_key(filename, pass, key, &comment); | |
114 | memset(pass, 0, strlen(pass)); | |
115 | xfree(pass); | |
116 | if (success) | |
117 | break; | |
118 | ||
119 | printf("Bad passphrase.\n"); | |
120 | } | |
121 | } | |
8efc0c15 | 122 | xfree(saved_comment); |
123 | ||
8efc0c15 | 124 | if (ssh_add_identity(ac, key, comment)) |
125 | fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | |
126 | else | |
127 | fprintf(stderr, "Could not add identity: %s\n", filename); | |
128 | RSA_free(key); | |
129 | xfree(comment); | |
8efc0c15 | 130 | } |
131 | ||
132 | void | |
5068f573 | 133 | list_identities(AuthenticationConnection *ac) |
8efc0c15 | 134 | { |
8efc0c15 | 135 | BIGNUM *e, *n; |
4d195447 | 136 | int status; |
8efc0c15 | 137 | char *comment; |
138 | int had_identities; | |
139 | ||
8efc0c15 | 140 | e = BN_new(); |
141 | n = BN_new(); | |
142 | had_identities = 0; | |
4d195447 | 143 | for (status = ssh_get_first_identity(ac, e, n, &comment); |
8efc0c15 | 144 | status; |
4d195447 | 145 | status = ssh_get_next_identity(ac, e, n, &comment)) |
8efc0c15 | 146 | { |
4d195447 | 147 | char *ebuf, *nbuf; |
8efc0c15 | 148 | had_identities = 1; |
4d195447 | 149 | ebuf = BN_bn2dec(e); |
150 | if (ebuf == NULL) { | |
151 | error("list_identities: BN_bn2dec(e) failed."); | |
152 | }else{ | |
153 | nbuf = BN_bn2dec(n); | |
154 | if (nbuf == NULL) { | |
155 | error("list_identities: BN_bn2dec(n) failed."); | |
156 | }else{ | |
157 | unsigned int bits = BN_num_bits(n); | |
158 | printf("%d %s %s %s\n", bits, ebuf, nbuf, comment); | |
159 | free(nbuf); | |
160 | } | |
161 | free(ebuf); | |
5bae4ab8 | 162 | } |
8efc0c15 | 163 | xfree(comment); |
164 | } | |
165 | BN_clear_free(e); | |
166 | BN_clear_free(n); | |
167 | if (!had_identities) | |
168 | printf("The agent has no identities.\n"); | |
8efc0c15 | 169 | } |
170 | ||
171 | int | |
5068f573 | 172 | main(int argc, char **argv) |
8efc0c15 | 173 | { |
5068f573 | 174 | AuthenticationConnection *ac = NULL; |
8efc0c15 | 175 | struct passwd *pw; |
176 | char buf[1024]; | |
177 | int no_files = 1; | |
178 | int i; | |
179 | int deleting = 0; | |
180 | ||
181 | /* check if RSA support exists */ | |
182 | if (rsa_alive() == 0) { | |
8efc0c15 | 183 | |
184 | fprintf(stderr, | |
185 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | |
186 | __progname); | |
187 | exit(1); | |
188 | } | |
189 | ||
5068f573 | 190 | /* At first, get a connection to the authentication agent. */ |
191 | ac = ssh_get_authentication_connection(); | |
192 | if (ac == NULL) { | |
193 | fprintf(stderr, "Could not open a connection to your authentication agent.\n"); | |
194 | exit(1); | |
195 | } | |
196 | ||
197 | for (i = 1; i < argc; i++) | |
8efc0c15 | 198 | { |
5068f573 | 199 | if (strcmp(argv[i], "-l") == 0) |
8efc0c15 | 200 | { |
5068f573 | 201 | list_identities(ac); |
8efc0c15 | 202 | no_files = 0; /* Don't default-add/delete if -l. */ |
203 | continue; | |
204 | } | |
5068f573 | 205 | if (strcmp(argv[i], "-d") == 0) |
8efc0c15 | 206 | { |
207 | deleting = 1; | |
208 | continue; | |
209 | } | |
5068f573 | 210 | if (strcmp(argv[i], "-D") == 0) |
8efc0c15 | 211 | { |
5068f573 | 212 | delete_all(ac); |
8efc0c15 | 213 | no_files = 0; |
214 | continue; | |
215 | } | |
216 | no_files = 0; | |
217 | if (deleting) | |
5068f573 | 218 | delete_file(ac, argv[i]); |
8efc0c15 | 219 | else |
5068f573 | 220 | add_file(ac, argv[i]); |
8efc0c15 | 221 | } |
222 | if (no_files) | |
223 | { | |
224 | pw = getpwuid(getuid()); | |
225 | if (!pw) | |
226 | { | |
227 | fprintf(stderr, "No user found with uid %d\n", (int)getuid()); | |
5068f573 | 228 | ssh_close_authentication_connection(ac); |
8efc0c15 | 229 | exit(1); |
230 | } | |
231 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); | |
232 | if (deleting) | |
5068f573 | 233 | delete_file(ac, buf); |
8efc0c15 | 234 | else |
5068f573 | 235 | add_file(ac, buf); |
8efc0c15 | 236 | } |
5068f573 | 237 | ssh_close_authentication_connection(ac); |
8efc0c15 | 238 | exit(0); |
239 | } | |
dad9a31e | 240 | |
241 | #ifdef USE_EXTERNAL_ASKPASS | |
242 | int askpass(const char *filename, RSA *key, const char *saved_comment, char **comment) | |
243 | { | |
244 | int pipes[2]; | |
245 | char buf[1024]; | |
246 | int tmp; | |
247 | pid_t child; | |
248 | FILE *pipef; | |
249 | ||
250 | /* Check that we are X11-capable */ | |
251 | if (getenv("DISPLAY") == NULL) | |
252 | exit(1); | |
253 | ||
254 | if (pipe(pipes) == -1) { | |
255 | fprintf(stderr, "Creating pipes failed: %s\n", strerror(errno)); | |
256 | exit(1); | |
257 | } | |
258 | ||
259 | if (fflush(NULL) == EOF) { | |
260 | fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno)); | |
261 | exit(1); | |
262 | } | |
263 | ||
264 | child = fork(); | |
265 | if (child == -1) { | |
266 | fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); | |
267 | exit(1); | |
268 | } | |
269 | ||
270 | if (child == 0) { | |
271 | /* In child */ | |
272 | ||
273 | close(pipes[0]); | |
274 | if (dup2(pipes[1], 1) ==-1) { | |
275 | fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); | |
276 | exit(1); | |
277 | } | |
278 | ||
279 | tmp = snprintf(buf, sizeof(buf), "Need passphrase for %s (%s)", filename, saved_comment); | |
280 | /* skip the prompt if it won't fit */ | |
281 | if ((tmp < 0) || (tmp >= sizeof(buf))) | |
282 | tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", 0); | |
283 | else | |
284 | tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", buf, 0); | |
285 | ||
286 | /* Shouldn't get this far */ | |
287 | fprintf(stderr, "Executing ssh-askpass failed: %s\n", strerror(errno)); | |
288 | exit(1); | |
289 | } | |
290 | ||
291 | /* In parent */ | |
292 | close(pipes[1]); | |
293 | ||
294 | if ((pipef = fdopen(pipes[0], "r")) == NULL) { | |
295 | fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); | |
296 | exit(1); | |
297 | } | |
298 | ||
299 | /* Read passphrase back from child, abort if none presented */ | |
300 | if(fgets(buf, sizeof(buf), pipef) == NULL) | |
301 | exit(1); | |
302 | ||
303 | fclose(pipef); | |
304 | ||
305 | if (strchr(buf, '\n')) | |
306 | *strchr(buf, '\n') = 0; | |
307 | ||
308 | if (waitpid(child, NULL, 0) == -1) { | |
309 | fprintf(stderr, "Waiting for child failed: %s\n", | |
310 | strerror(errno)); | |
311 | exit(1); | |
312 | } | |
313 | ||
314 | /* Try password as it was presented */ | |
315 | tmp = load_private_key(filename, buf, key, comment); | |
316 | ||
317 | memset(buf, 0, sizeof(buf)); | |
318 | ||
319 | return(tmp); | |
320 | } | |
321 | #endif /* USE_EXTERNAL_ASKPASS */ |