1 |
|
|
/* $OpenBSD: ssh-add.c,v 1.134 2017/08/29 09:42:29 dlg Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 |
|
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 |
|
|
* All rights reserved |
6 |
|
|
* Adds an identity to the authentication server, or removes an identity. |
7 |
|
|
* |
8 |
|
|
* As far as I am concerned, the code I have written for this software |
9 |
|
|
* can be used freely for any purpose. Any derived versions of this |
10 |
|
|
* software must be clearly marked as such, and if the derived work is |
11 |
|
|
* incompatible with the protocol description in the RFC file, it must be |
12 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
13 |
|
|
* |
14 |
|
|
* SSH2 implementation, |
15 |
|
|
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. |
16 |
|
|
* |
17 |
|
|
* Redistribution and use in source and binary forms, with or without |
18 |
|
|
* modification, are permitted provided that the following conditions |
19 |
|
|
* are met: |
20 |
|
|
* 1. Redistributions of source code must retain the above copyright |
21 |
|
|
* notice, this list of conditions and the following disclaimer. |
22 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
23 |
|
|
* notice, this list of conditions and the following disclaimer in the |
24 |
|
|
* documentation and/or other materials provided with the distribution. |
25 |
|
|
* |
26 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
27 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
28 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
29 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
30 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
31 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
32 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
33 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
34 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 |
|
|
*/ |
37 |
|
|
|
38 |
|
|
#include <sys/types.h> |
39 |
|
|
#include <sys/stat.h> |
40 |
|
|
|
41 |
|
|
#include <openssl/evp.h> |
42 |
|
|
|
43 |
|
|
#include <errno.h> |
44 |
|
|
#include <fcntl.h> |
45 |
|
|
#include <pwd.h> |
46 |
|
|
#include <stdio.h> |
47 |
|
|
#include <stdlib.h> |
48 |
|
|
#include <string.h> |
49 |
|
|
#include <unistd.h> |
50 |
|
|
#include <limits.h> |
51 |
|
|
|
52 |
|
|
#include "xmalloc.h" |
53 |
|
|
#include "ssh.h" |
54 |
|
|
#include "log.h" |
55 |
|
|
#include "sshkey.h" |
56 |
|
|
#include "sshbuf.h" |
57 |
|
|
#include "authfd.h" |
58 |
|
|
#include "authfile.h" |
59 |
|
|
#include "pathnames.h" |
60 |
|
|
#include "misc.h" |
61 |
|
|
#include "ssherr.h" |
62 |
|
|
#include "digest.h" |
63 |
|
|
|
64 |
|
|
/* argv0 */ |
65 |
|
|
extern char *__progname; |
66 |
|
|
|
67 |
|
|
/* Default files to add */ |
68 |
|
|
static char *default_files[] = { |
69 |
|
|
_PATH_SSH_CLIENT_ID_RSA, |
70 |
|
|
_PATH_SSH_CLIENT_ID_DSA, |
71 |
|
|
_PATH_SSH_CLIENT_ID_ECDSA, |
72 |
|
|
_PATH_SSH_CLIENT_ID_ED25519, |
73 |
|
|
NULL |
74 |
|
|
}; |
75 |
|
|
|
76 |
|
|
static int fingerprint_hash = SSH_FP_HASH_DEFAULT; |
77 |
|
|
|
78 |
|
|
/* Default lifetime (0 == forever) */ |
79 |
|
|
static int lifetime = 0; |
80 |
|
|
|
81 |
|
|
/* User has to confirm key use */ |
82 |
|
|
static int confirm = 0; |
83 |
|
|
|
84 |
|
|
/* we keep a cache of one passphrase */ |
85 |
|
|
static char *pass = NULL; |
86 |
|
|
static void |
87 |
|
|
clear_pass(void) |
88 |
|
|
{ |
89 |
|
|
if (pass) { |
90 |
|
|
explicit_bzero(pass, strlen(pass)); |
91 |
|
|
free(pass); |
92 |
|
|
pass = NULL; |
93 |
|
|
} |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
static int |
97 |
|
|
delete_file(int agent_fd, const char *filename, int key_only, int qflag) |
98 |
|
|
{ |
99 |
|
|
struct sshkey *public, *cert = NULL; |
100 |
|
|
char *certpath = NULL, *comment = NULL; |
101 |
|
|
int r, ret = -1; |
102 |
|
|
|
103 |
|
|
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { |
104 |
|
|
printf("Bad key file %s: %s\n", filename, ssh_err(r)); |
105 |
|
|
return -1; |
106 |
|
|
} |
107 |
|
|
if ((r = ssh_remove_identity(agent_fd, public)) == 0) { |
108 |
|
|
if (!qflag) { |
109 |
|
|
fprintf(stderr, "Identity removed: %s (%s)\n", |
110 |
|
|
filename, comment); |
111 |
|
|
} |
112 |
|
|
ret = 0; |
113 |
|
|
} else |
114 |
|
|
fprintf(stderr, "Could not remove identity \"%s\": %s\n", |
115 |
|
|
filename, ssh_err(r)); |
116 |
|
|
|
117 |
|
|
if (key_only) |
118 |
|
|
goto out; |
119 |
|
|
|
120 |
|
|
/* Now try to delete the corresponding certificate too */ |
121 |
|
|
free(comment); |
122 |
|
|
comment = NULL; |
123 |
|
|
xasprintf(&certpath, "%s-cert.pub", filename); |
124 |
|
|
if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) { |
125 |
|
|
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) |
126 |
|
|
error("Failed to load certificate \"%s\": %s", |
127 |
|
|
certpath, ssh_err(r)); |
128 |
|
|
goto out; |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
if (!sshkey_equal_public(cert, public)) |
132 |
|
|
fatal("Certificate %s does not match private key %s", |
133 |
|
|
certpath, filename); |
134 |
|
|
|
135 |
|
|
if ((r = ssh_remove_identity(agent_fd, cert)) == 0) { |
136 |
|
|
if (!qflag) { |
137 |
|
|
fprintf(stderr, "Identity removed: %s (%s)\n", |
138 |
|
|
certpath, comment); |
139 |
|
|
} |
140 |
|
|
ret = 0; |
141 |
|
|
} else |
142 |
|
|
fprintf(stderr, "Could not remove identity \"%s\": %s\n", |
143 |
|
|
certpath, ssh_err(r)); |
144 |
|
|
|
145 |
|
|
out: |
146 |
|
|
sshkey_free(cert); |
147 |
|
|
sshkey_free(public); |
148 |
|
|
free(certpath); |
149 |
|
|
free(comment); |
150 |
|
|
|
151 |
|
|
return ret; |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
/* Send a request to remove all identities. */ |
155 |
|
|
static int |
156 |
|
|
delete_all(int agent_fd) |
157 |
|
|
{ |
158 |
|
|
int ret = -1; |
159 |
|
|
|
160 |
|
|
/* |
161 |
|
|
* Since the agent might be forwarded, old or non-OpenSSH, when asked |
162 |
|
|
* to remove all keys, attempt to remove both protocol v.1 and v.2 |
163 |
|
|
* keys. |
164 |
|
|
*/ |
165 |
|
|
if (ssh_remove_all_identities(agent_fd, 2) == 0) |
166 |
|
|
ret = 0; |
167 |
|
|
/* ignore error-code for ssh1 */ |
168 |
|
|
ssh_remove_all_identities(agent_fd, 1); |
169 |
|
|
|
170 |
|
|
if (ret == 0) |
171 |
|
|
fprintf(stderr, "All identities removed.\n"); |
172 |
|
|
else |
173 |
|
|
fprintf(stderr, "Failed to remove all identities.\n"); |
174 |
|
|
|
175 |
|
|
return ret; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
static int |
179 |
|
|
add_file(int agent_fd, const char *filename, int key_only, int qflag) |
180 |
|
|
{ |
181 |
|
|
struct sshkey *private, *cert; |
182 |
|
|
char *comment = NULL; |
183 |
|
|
char msg[1024], *certpath = NULL; |
184 |
|
|
int r, fd, ret = -1; |
185 |
|
|
struct sshbuf *keyblob; |
186 |
|
|
|
187 |
|
|
if (strcmp(filename, "-") == 0) { |
188 |
|
|
fd = STDIN_FILENO; |
189 |
|
|
filename = "(stdin)"; |
190 |
|
|
} else if ((fd = open(filename, O_RDONLY)) < 0) { |
191 |
|
|
perror(filename); |
192 |
|
|
return -1; |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
/* |
196 |
|
|
* Since we'll try to load a keyfile multiple times, permission errors |
197 |
|
|
* will occur multiple times, so check perms first and bail if wrong. |
198 |
|
|
*/ |
199 |
|
|
if (fd != STDIN_FILENO) { |
200 |
|
|
if (sshkey_perm_ok(fd, filename) != 0) { |
201 |
|
|
close(fd); |
202 |
|
|
return -1; |
203 |
|
|
} |
204 |
|
|
} |
205 |
|
|
if ((keyblob = sshbuf_new()) == NULL) |
206 |
|
|
fatal("%s: sshbuf_new failed", __func__); |
207 |
|
|
if ((r = sshkey_load_file(fd, keyblob)) != 0) { |
208 |
|
|
fprintf(stderr, "Error loading key \"%s\": %s\n", |
209 |
|
|
filename, ssh_err(r)); |
210 |
|
|
sshbuf_free(keyblob); |
211 |
|
|
close(fd); |
212 |
|
|
return -1; |
213 |
|
|
} |
214 |
|
|
close(fd); |
215 |
|
|
|
216 |
|
|
/* At first, try empty passphrase */ |
217 |
|
|
if ((r = sshkey_parse_private_fileblob(keyblob, "", &private, |
218 |
|
|
&comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { |
219 |
|
|
fprintf(stderr, "Error loading key \"%s\": %s\n", |
220 |
|
|
filename, ssh_err(r)); |
221 |
|
|
goto fail_load; |
222 |
|
|
} |
223 |
|
|
/* try last */ |
224 |
|
|
if (private == NULL && pass != NULL) { |
225 |
|
|
if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private, |
226 |
|
|
&comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { |
227 |
|
|
fprintf(stderr, "Error loading key \"%s\": %s\n", |
228 |
|
|
filename, ssh_err(r)); |
229 |
|
|
goto fail_load; |
230 |
|
|
} |
231 |
|
|
} |
232 |
|
|
if (private == NULL) { |
233 |
|
|
/* clear passphrase since it did not work */ |
234 |
|
|
clear_pass(); |
235 |
|
|
snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ", |
236 |
|
|
filename, confirm ? " (will confirm each use)" : ""); |
237 |
|
|
for (;;) { |
238 |
|
|
pass = read_passphrase(msg, RP_ALLOW_STDIN); |
239 |
|
|
if (strcmp(pass, "") == 0) |
240 |
|
|
goto fail_load; |
241 |
|
|
if ((r = sshkey_parse_private_fileblob(keyblob, pass, |
242 |
|
|
&private, &comment)) == 0) |
243 |
|
|
break; |
244 |
|
|
else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { |
245 |
|
|
fprintf(stderr, |
246 |
|
|
"Error loading key \"%s\": %s\n", |
247 |
|
|
filename, ssh_err(r)); |
248 |
|
|
fail_load: |
249 |
|
|
clear_pass(); |
250 |
|
|
sshbuf_free(keyblob); |
251 |
|
|
return -1; |
252 |
|
|
} |
253 |
|
|
clear_pass(); |
254 |
|
|
snprintf(msg, sizeof msg, |
255 |
|
|
"Bad passphrase, try again for %s%s: ", filename, |
256 |
|
|
confirm ? " (will confirm each use)" : ""); |
257 |
|
|
} |
258 |
|
|
} |
259 |
|
|
if (comment == NULL || *comment == '\0') |
260 |
|
|
comment = xstrdup(filename); |
261 |
|
|
sshbuf_free(keyblob); |
262 |
|
|
|
263 |
|
|
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
264 |
|
|
lifetime, confirm)) == 0) { |
265 |
|
|
fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); |
266 |
|
|
ret = 0; |
267 |
|
|
if (lifetime != 0) |
268 |
|
|
fprintf(stderr, |
269 |
|
|
"Lifetime set to %d seconds\n", lifetime); |
270 |
|
|
if (confirm != 0) |
271 |
|
|
fprintf(stderr, |
272 |
|
|
"The user must confirm each use of the key\n"); |
273 |
|
|
} else { |
274 |
|
|
fprintf(stderr, "Could not add identity \"%s\": %s\n", |
275 |
|
|
filename, ssh_err(r)); |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
/* Skip trying to load the cert if requested */ |
279 |
|
|
if (key_only) |
280 |
|
|
goto out; |
281 |
|
|
|
282 |
|
|
/* Now try to add the certificate flavour too */ |
283 |
|
|
xasprintf(&certpath, "%s-cert.pub", filename); |
284 |
|
|
if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { |
285 |
|
|
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) |
286 |
|
|
error("Failed to load certificate \"%s\": %s", |
287 |
|
|
certpath, ssh_err(r)); |
288 |
|
|
goto out; |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
if (!sshkey_equal_public(cert, private)) { |
292 |
|
|
error("Certificate %s does not match private key %s", |
293 |
|
|
certpath, filename); |
294 |
|
|
sshkey_free(cert); |
295 |
|
|
goto out; |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
/* Graft with private bits */ |
299 |
|
|
if ((r = sshkey_to_certified(private)) != 0) { |
300 |
|
|
error("%s: sshkey_to_certified: %s", __func__, ssh_err(r)); |
301 |
|
|
sshkey_free(cert); |
302 |
|
|
goto out; |
303 |
|
|
} |
304 |
|
|
if ((r = sshkey_cert_copy(cert, private)) != 0) { |
305 |
|
|
error("%s: sshkey_cert_copy: %s", __func__, ssh_err(r)); |
306 |
|
|
sshkey_free(cert); |
307 |
|
|
goto out; |
308 |
|
|
} |
309 |
|
|
sshkey_free(cert); |
310 |
|
|
|
311 |
|
|
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
312 |
|
|
lifetime, confirm)) != 0) { |
313 |
|
|
error("Certificate %s (%s) add failed: %s", certpath, |
314 |
|
|
private->cert->key_id, ssh_err(r)); |
315 |
|
|
goto out; |
316 |
|
|
} |
317 |
|
|
fprintf(stderr, "Certificate added: %s (%s)\n", certpath, |
318 |
|
|
private->cert->key_id); |
319 |
|
|
if (lifetime != 0) |
320 |
|
|
fprintf(stderr, "Lifetime set to %d seconds\n", lifetime); |
321 |
|
|
if (confirm != 0) |
322 |
|
|
fprintf(stderr, "The user must confirm each use of the key\n"); |
323 |
|
|
out: |
324 |
|
|
free(certpath); |
325 |
|
|
free(comment); |
326 |
|
|
sshkey_free(private); |
327 |
|
|
|
328 |
|
|
return ret; |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
static int |
332 |
|
|
update_card(int agent_fd, int add, const char *id) |
333 |
|
|
{ |
334 |
|
|
char *pin = NULL; |
335 |
|
|
int r, ret = -1; |
336 |
|
|
|
337 |
|
|
if (add) { |
338 |
|
|
if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", |
339 |
|
|
RP_ALLOW_STDIN)) == NULL) |
340 |
|
|
return -1; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, |
344 |
|
|
lifetime, confirm)) == 0) { |
345 |
|
|
fprintf(stderr, "Card %s: %s\n", |
346 |
|
|
add ? "added" : "removed", id); |
347 |
|
|
ret = 0; |
348 |
|
|
} else { |
349 |
|
|
fprintf(stderr, "Could not %s card \"%s\": %s\n", |
350 |
|
|
add ? "add" : "remove", id, ssh_err(r)); |
351 |
|
|
ret = -1; |
352 |
|
|
} |
353 |
|
|
free(pin); |
354 |
|
|
return ret; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
static int |
358 |
|
|
list_identities(int agent_fd, int do_fp) |
359 |
|
|
{ |
360 |
|
|
char *fp; |
361 |
|
|
int r; |
362 |
|
|
struct ssh_identitylist *idlist; |
363 |
|
|
size_t i; |
364 |
|
|
|
365 |
|
|
if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { |
366 |
|
|
if (r != SSH_ERR_AGENT_NO_IDENTITIES) |
367 |
|
|
fprintf(stderr, "error fetching identities: %s\n", |
368 |
|
|
ssh_err(r)); |
369 |
|
|
else |
370 |
|
|
printf("The agent has no identities.\n"); |
371 |
|
|
return -1; |
372 |
|
|
} |
373 |
|
|
for (i = 0; i < idlist->nkeys; i++) { |
374 |
|
|
if (do_fp) { |
375 |
|
|
fp = sshkey_fingerprint(idlist->keys[i], |
376 |
|
|
fingerprint_hash, SSH_FP_DEFAULT); |
377 |
|
|
printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]), |
378 |
|
|
fp == NULL ? "(null)" : fp, idlist->comments[i], |
379 |
|
|
sshkey_type(idlist->keys[i])); |
380 |
|
|
free(fp); |
381 |
|
|
} else { |
382 |
|
|
if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) { |
383 |
|
|
fprintf(stderr, "sshkey_write: %s\n", |
384 |
|
|
ssh_err(r)); |
385 |
|
|
continue; |
386 |
|
|
} |
387 |
|
|
fprintf(stdout, " %s\n", idlist->comments[i]); |
388 |
|
|
} |
389 |
|
|
} |
390 |
|
|
ssh_free_identitylist(idlist); |
391 |
|
|
return 0; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
static int |
395 |
|
|
lock_agent(int agent_fd, int lock) |
396 |
|
|
{ |
397 |
|
|
char prompt[100], *p1, *p2; |
398 |
|
|
int r, passok = 1, ret = -1; |
399 |
|
|
|
400 |
|
|
strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); |
401 |
|
|
p1 = read_passphrase(prompt, RP_ALLOW_STDIN); |
402 |
|
|
if (lock) { |
403 |
|
|
strlcpy(prompt, "Again: ", sizeof prompt); |
404 |
|
|
p2 = read_passphrase(prompt, RP_ALLOW_STDIN); |
405 |
|
|
if (strcmp(p1, p2) != 0) { |
406 |
|
|
fprintf(stderr, "Passwords do not match.\n"); |
407 |
|
|
passok = 0; |
408 |
|
|
} |
409 |
|
|
explicit_bzero(p2, strlen(p2)); |
410 |
|
|
free(p2); |
411 |
|
|
} |
412 |
|
|
if (passok) { |
413 |
|
|
if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) { |
414 |
|
|
fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); |
415 |
|
|
ret = 0; |
416 |
|
|
} else { |
417 |
|
|
fprintf(stderr, "Failed to %slock agent: %s\n", |
418 |
|
|
lock ? "" : "un", ssh_err(r)); |
419 |
|
|
} |
420 |
|
|
} |
421 |
|
|
explicit_bzero(p1, strlen(p1)); |
422 |
|
|
free(p1); |
423 |
|
|
return (ret); |
424 |
|
|
} |
425 |
|
|
|
426 |
|
|
static int |
427 |
|
|
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) |
428 |
|
|
{ |
429 |
|
|
if (deleting) { |
430 |
|
|
if (delete_file(agent_fd, file, key_only, qflag) == -1) |
431 |
|
|
return -1; |
432 |
|
|
} else { |
433 |
|
|
if (add_file(agent_fd, file, key_only, qflag) == -1) |
434 |
|
|
return -1; |
435 |
|
|
} |
436 |
|
|
return 0; |
437 |
|
|
} |
438 |
|
|
|
439 |
|
|
static void |
440 |
|
|
usage(void) |
441 |
|
|
{ |
442 |
|
|
fprintf(stderr, "usage: %s [options] [file ...]\n", __progname); |
443 |
|
|
fprintf(stderr, "Options:\n"); |
444 |
|
|
fprintf(stderr, " -l List fingerprints of all identities.\n"); |
445 |
|
|
fprintf(stderr, " -E hash Specify hash algorithm used for fingerprints.\n"); |
446 |
|
|
fprintf(stderr, " -L List public key parameters of all identities.\n"); |
447 |
|
|
fprintf(stderr, " -k Load only keys and not certificates.\n"); |
448 |
|
|
fprintf(stderr, " -c Require confirmation to sign using identities\n"); |
449 |
|
|
fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); |
450 |
|
|
fprintf(stderr, " -d Delete identity.\n"); |
451 |
|
|
fprintf(stderr, " -D Delete all identities.\n"); |
452 |
|
|
fprintf(stderr, " -x Lock agent.\n"); |
453 |
|
|
fprintf(stderr, " -X Unlock agent.\n"); |
454 |
|
|
fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); |
455 |
|
|
fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); |
456 |
|
|
fprintf(stderr, " -q Be quiet after a successful operation.\n"); |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
int |
460 |
|
|
main(int argc, char **argv) |
461 |
|
|
{ |
462 |
|
|
extern char *optarg; |
463 |
|
|
extern int optind; |
464 |
|
|
int agent_fd; |
465 |
|
|
char *pkcs11provider = NULL; |
466 |
|
|
int r, i, ch, deleting = 0, ret = 0, key_only = 0; |
467 |
|
|
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0; |
468 |
|
|
|
469 |
|
|
ssh_malloc_init(); /* must be called before any mallocs */ |
470 |
|
|
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
471 |
|
|
sanitise_stdfd(); |
472 |
|
|
|
473 |
|
|
OpenSSL_add_all_algorithms(); |
474 |
|
|
|
475 |
|
|
setvbuf(stdout, NULL, _IOLBF, 0); |
476 |
|
|
|
477 |
|
|
/* First, get a connection to the authentication agent. */ |
478 |
|
|
switch (r = ssh_get_authentication_socket(&agent_fd)) { |
479 |
|
|
case 0: |
480 |
|
|
break; |
481 |
|
|
case SSH_ERR_AGENT_NOT_PRESENT: |
482 |
|
|
fprintf(stderr, "Could not open a connection to your " |
483 |
|
|
"authentication agent.\n"); |
484 |
|
|
exit(2); |
485 |
|
|
default: |
486 |
|
|
fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r)); |
487 |
|
|
exit(2); |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) { |
491 |
|
|
switch (ch) { |
492 |
|
|
case 'E': |
493 |
|
|
fingerprint_hash = ssh_digest_alg_by_name(optarg); |
494 |
|
|
if (fingerprint_hash == -1) |
495 |
|
|
fatal("Invalid hash algorithm \"%s\"", optarg); |
496 |
|
|
break; |
497 |
|
|
case 'k': |
498 |
|
|
key_only = 1; |
499 |
|
|
break; |
500 |
|
|
case 'l': |
501 |
|
|
case 'L': |
502 |
|
|
if (lflag != 0) |
503 |
|
|
fatal("-%c flag already specified", lflag); |
504 |
|
|
lflag = ch; |
505 |
|
|
break; |
506 |
|
|
case 'x': |
507 |
|
|
case 'X': |
508 |
|
|
if (xflag != 0) |
509 |
|
|
fatal("-%c flag already specified", xflag); |
510 |
|
|
xflag = ch; |
511 |
|
|
break; |
512 |
|
|
case 'c': |
513 |
|
|
confirm = 1; |
514 |
|
|
break; |
515 |
|
|
case 'd': |
516 |
|
|
deleting = 1; |
517 |
|
|
break; |
518 |
|
|
case 'D': |
519 |
|
|
Dflag = 1; |
520 |
|
|
break; |
521 |
|
|
case 's': |
522 |
|
|
pkcs11provider = optarg; |
523 |
|
|
break; |
524 |
|
|
case 'e': |
525 |
|
|
deleting = 1; |
526 |
|
|
pkcs11provider = optarg; |
527 |
|
|
break; |
528 |
|
|
case 't': |
529 |
|
|
if ((lifetime = convtime(optarg)) == -1) { |
530 |
|
|
fprintf(stderr, "Invalid lifetime\n"); |
531 |
|
|
ret = 1; |
532 |
|
|
goto done; |
533 |
|
|
} |
534 |
|
|
break; |
535 |
|
|
case 'q': |
536 |
|
|
qflag = 1; |
537 |
|
|
break; |
538 |
|
|
default: |
539 |
|
|
usage(); |
540 |
|
|
ret = 1; |
541 |
|
|
goto done; |
542 |
|
|
} |
543 |
|
|
} |
544 |
|
|
|
545 |
|
|
if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) |
546 |
|
|
fatal("Invalid combination of actions"); |
547 |
|
|
else if (xflag) { |
548 |
|
|
if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) |
549 |
|
|
ret = 1; |
550 |
|
|
goto done; |
551 |
|
|
} else if (lflag) { |
552 |
|
|
if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1) |
553 |
|
|
ret = 1; |
554 |
|
|
goto done; |
555 |
|
|
} else if (Dflag) { |
556 |
|
|
if (delete_all(agent_fd) == -1) |
557 |
|
|
ret = 1; |
558 |
|
|
goto done; |
559 |
|
|
} |
560 |
|
|
|
561 |
|
|
argc -= optind; |
562 |
|
|
argv += optind; |
563 |
|
|
if (pkcs11provider != NULL) { |
564 |
|
|
if (update_card(agent_fd, !deleting, pkcs11provider) == -1) |
565 |
|
|
ret = 1; |
566 |
|
|
goto done; |
567 |
|
|
} |
568 |
|
|
if (argc == 0) { |
569 |
|
|
char buf[PATH_MAX]; |
570 |
|
|
struct passwd *pw; |
571 |
|
|
struct stat st; |
572 |
|
|
int count = 0; |
573 |
|
|
|
574 |
|
|
if ((pw = getpwuid(getuid())) == NULL) { |
575 |
|
|
fprintf(stderr, "No user found with uid %u\n", |
576 |
|
|
(u_int)getuid()); |
577 |
|
|
ret = 1; |
578 |
|
|
goto done; |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
for (i = 0; default_files[i]; i++) { |
582 |
|
|
snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, |
583 |
|
|
default_files[i]); |
584 |
|
|
if (stat(buf, &st) < 0) |
585 |
|
|
continue; |
586 |
|
|
if (do_file(agent_fd, deleting, key_only, buf, |
587 |
|
|
qflag) == -1) |
588 |
|
|
ret = 1; |
589 |
|
|
else |
590 |
|
|
count++; |
591 |
|
|
} |
592 |
|
|
if (count == 0) |
593 |
|
|
ret = 1; |
594 |
|
|
} else { |
595 |
|
|
for (i = 0; i < argc; i++) { |
596 |
|
|
if (do_file(agent_fd, deleting, key_only, |
597 |
|
|
argv[i], qflag) == -1) |
598 |
|
|
ret = 1; |
599 |
|
|
} |
600 |
|
|
} |
601 |
|
|
clear_pass(); |
602 |
|
|
|
603 |
|
|
done: |
604 |
|
|
ssh_close_authentication_socket(agent_fd); |
605 |
|
|
return ret; |
606 |
|
|
} |