|
Lines 75-80
static char *default_files[] = {
Link Here
|
| 75 |
|
75 |
|
| 76 |
static int fingerprint_hash = SSH_FP_HASH_DEFAULT; |
76 |
static int fingerprint_hash = SSH_FP_HASH_DEFAULT; |
| 77 |
|
77 |
|
|
|
78 |
#define CERT_EXPIRY_GRACE 60 /* grace period for expired certs */ |
| 79 |
|
| 78 |
/* Default lifetime (0 == forever) */ |
80 |
/* Default lifetime (0 == forever) */ |
| 79 |
static int lifetime = 0; |
81 |
static int lifetime = 0; |
| 80 |
|
82 |
|
|
Lines 94-118
clear_pass(void)
Link Here
|
| 94 |
} |
96 |
} |
| 95 |
|
97 |
|
| 96 |
static int |
98 |
static int |
| 97 |
delete_file(int agent_fd, const char *filename, int key_only, int qflag) |
99 |
delete_file(int agent_fd, const char *filename, int key_only, |
|
|
100 |
int cert_only, int qflag) |
| 98 |
{ |
101 |
{ |
| 99 |
struct sshkey *public, *cert = NULL; |
102 |
struct sshkey *public, *cert = NULL; |
| 100 |
char *certpath = NULL, *comment = NULL; |
103 |
char *certpath = NULL, *comment = NULL; |
| 101 |
int r, ret = -1; |
104 |
int r, ret = -1; |
| 102 |
|
105 |
|
| 103 |
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { |
106 |
if (!cert_only) { |
| 104 |
printf("Bad key file %s: %s\n", filename, ssh_err(r)); |
107 |
if ((r = sshkey_load_public(filename, &public, |
| 105 |
return -1; |
108 |
&comment)) != 0) { |
| 106 |
} |
109 |
printf("Bad key file %s: %s\n", filename, ssh_err(r)); |
| 107 |
if ((r = ssh_remove_identity(agent_fd, public)) == 0) { |
110 |
return -1; |
| 108 |
if (!qflag) { |
|
|
| 109 |
fprintf(stderr, "Identity removed: %s (%s)\n", |
| 110 |
filename, comment); |
| 111 |
} |
111 |
} |
| 112 |
ret = 0; |
112 |
if ((r = ssh_remove_identity(agent_fd, public)) == 0) { |
| 113 |
} else |
113 |
if (!qflag) { |
| 114 |
fprintf(stderr, "Could not remove identity \"%s\": %s\n", |
114 |
fprintf(stderr, "Identity removed: %s (%s)\n", |
| 115 |
filename, ssh_err(r)); |
115 |
filename, comment); |
|
|
116 |
} |
| 117 |
ret = 0; |
| 118 |
} else |
| 119 |
fprintf(stderr, "Could not remove identity " |
| 120 |
"\"%s\": %s\n", filename, ssh_err(r)); |
| 121 |
} |
| 116 |
|
122 |
|
| 117 |
if (key_only) |
123 |
if (key_only) |
| 118 |
goto out; |
124 |
goto out; |
|
Lines 175-188
delete_all(int agent_fd)
Link Here
|
| 175 |
return ret; |
181 |
return ret; |
| 176 |
} |
182 |
} |
| 177 |
|
183 |
|
|
|
184 |
/* returns non-zero if certificate has expired */ |
| 178 |
static int |
185 |
static int |
| 179 |
add_file(int agent_fd, const char *filename, int key_only, int qflag) |
186 |
cert_expired(const char *name, time_t now, const struct sshkey_cert *cert) |
| 180 |
{ |
187 |
{ |
| 181 |
struct sshkey *private, *cert; |
188 |
char msg[256]; |
|
|
189 |
|
| 190 |
sshkey_format_cert_validity(cert, msg, sizeof(msg)); |
| 191 |
if ((now < 0 || cert->valid_before < (u_int64_t)now)) { |
| 192 |
error("Certificate %s has expired, was valid %s", name, msg); |
| 193 |
return 1; |
| 194 |
} |
| 195 |
return 0; |
| 196 |
} |
| 197 |
|
| 198 |
static int |
| 199 |
add_file(int agent_fd, const char *filename, |
| 200 |
int key_only, int cert_only, int force, int qflag) |
| 201 |
{ |
| 202 |
struct sshkey *private = NULL, *cert = NULL; |
| 182 |
char *comment = NULL; |
203 |
char *comment = NULL; |
| 183 |
char msg[1024], *certpath = NULL; |
204 |
char msg[1024], *certpath = NULL; |
| 184 |
int r, fd, ret = -1; |
205 |
int r, fd, ret = -1, cert_fail = 0; |
| 185 |
struct sshbuf *keyblob; |
206 |
struct sshbuf *keyblob; |
|
|
207 |
time_t now; |
| 208 |
u_int64_t cert_lifetime; |
| 186 |
|
209 |
|
| 187 |
if (strcmp(filename, "-") == 0) { |
210 |
if (strcmp(filename, "-") == 0) { |
| 188 |
fd = STDIN_FILENO; |
211 |
fd = STDIN_FILENO; |
|
Lines 213-218
add_file(int agent_fd, const char *filename, int key_only, int qflag)
Link Here
|
| 213 |
} |
236 |
} |
| 214 |
close(fd); |
237 |
close(fd); |
| 215 |
|
238 |
|
|
|
239 |
/* Load and check the certificate before unwrapping private key */ |
| 240 |
if (!key_only) { |
| 241 |
xasprintf(&certpath, "%s-cert.pub", filename); |
| 242 |
if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { |
| 243 |
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) |
| 244 |
error("Failed to load certificate \"%s\": %s", |
| 245 |
certpath, ssh_err(r)); |
| 246 |
cert_fail = 1; |
| 247 |
} else { |
| 248 |
sshkey_format_cert_validity(cert->cert, |
| 249 |
msg, sizeof(msg)); |
| 250 |
debug("Certificate %s valid %s", certpath, msg); |
| 251 |
/* Skip expired certificates */ |
| 252 |
if (!force && |
| 253 |
cert_expired(certpath, time(NULL), cert->cert)) { |
| 254 |
cert_fail = 1; |
| 255 |
} |
| 256 |
} |
| 257 |
} |
| 258 |
if (cert_fail && cert_only) |
| 259 |
goto out; |
| 260 |
|
| 216 |
/* At first, try empty passphrase */ |
261 |
/* At first, try empty passphrase */ |
| 217 |
if ((r = sshkey_parse_private_fileblob(keyblob, "", &private, |
262 |
if ((r = sshkey_parse_private_fileblob(keyblob, "", &private, |
| 218 |
&comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { |
263 |
&comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { |
|
Lines 260-301
add_file(int agent_fd, const char *filename, int key_only, int qflag)
Link Here
|
| 260 |
comment = xstrdup(filename); |
305 |
comment = xstrdup(filename); |
| 261 |
sshbuf_free(keyblob); |
306 |
sshbuf_free(keyblob); |
| 262 |
|
307 |
|
| 263 |
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
308 |
if (!cert_only) { |
| 264 |
lifetime, confirm)) == 0) { |
309 |
if ((r = ssh_add_identity_constrained(agent_fd, |
| 265 |
fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); |
310 |
private, comment, lifetime, confirm)) == 0) { |
| 266 |
ret = 0; |
311 |
fprintf(stderr, "Identity added: %s (%s)\n", |
| 267 |
if (lifetime != 0) |
312 |
filename, comment); |
| 268 |
fprintf(stderr, |
313 |
ret = 0; |
| 269 |
"Lifetime set to %d seconds\n", lifetime); |
314 |
if (lifetime != 0) |
| 270 |
if (confirm != 0) |
315 |
fprintf(stderr, |
| 271 |
fprintf(stderr, |
316 |
"Lifetime set to %d seconds\n", lifetime); |
| 272 |
"The user must confirm each use of the key\n"); |
317 |
if (confirm != 0) |
| 273 |
} else { |
318 |
fprintf(stderr, "The user must confirm each " |
| 274 |
fprintf(stderr, "Could not add identity \"%s\": %s\n", |
319 |
"use of the key\n"); |
| 275 |
filename, ssh_err(r)); |
320 |
} else { |
|
|
321 |
fprintf(stderr, "Could not add identity \"%s\": %s\n", |
| 322 |
filename, ssh_err(r)); |
| 323 |
} |
| 276 |
} |
324 |
} |
| 277 |
|
325 |
|
| 278 |
/* Skip trying to load the cert if requested */ |
326 |
/* Skip trying to load the cert if requested */ |
| 279 |
if (key_only) |
327 |
if (key_only || cert_fail) |
| 280 |
goto out; |
328 |
goto out; |
| 281 |
|
329 |
|
| 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)) { |
330 |
if (!sshkey_equal_public(cert, private)) { |
| 292 |
error("Certificate %s does not match private key %s", |
331 |
error("Certificate %s does not match private key %s", |
| 293 |
certpath, filename); |
332 |
certpath, filename); |
| 294 |
sshkey_free(cert); |
333 |
sshkey_free(cert); |
| 295 |
goto out; |
334 |
goto out; |
| 296 |
} |
335 |
} |
| 297 |
|
336 |
|
| 298 |
/* Graft with private bits */ |
337 |
/* Redo expiry check in case user lingered at passphrase prompt */ |
|
|
338 |
now = time(NULL); |
| 339 |
if (!force && cert_expired(certpath, now, cert->cert)) |
| 340 |
goto out; |
| 341 |
|
| 342 |
/* Clamp agent lifetimes to cert lifetime plus some grace */ |
| 343 |
cert_lifetime = cert->cert->valid_before - (u_int64_t)now; |
| 344 |
if (cert->cert->valid_before == ~(u_int64_t)0) |
| 345 |
debug("Certificate %s has no expiry time", certpath); |
| 346 |
else |
| 347 |
debug("Certificate %s has %llu seconds remaining", |
| 348 |
certpath, cert_lifetime); |
| 349 |
if (cert_lifetime < (u_int64_t)0 - CERT_EXPIRY_GRACE) |
| 350 |
cert_lifetime += CERT_EXPIRY_GRACE; |
| 351 |
/* |
| 352 |
* Don't fiddle lifetimes if the cert didn't have an expiry date, |
| 353 |
* the lifetime would be greater than that which could be expressed |
| 354 |
* on the wire, less than the lifetimes set by the user, or the -f |
| 355 |
* (force) flag was set. |
| 356 |
*/ |
| 357 |
if (force || cert->cert->valid_before == ~(u_int64_t)0 || |
| 358 |
cert_lifetime > UINT_MAX || |
| 359 |
(lifetime != 0 && (u_int64_t)lifetime < cert_lifetime)) |
| 360 |
cert_lifetime = lifetime; |
| 361 |
|
| 362 |
/* Graft certificate to private bits */ |
| 299 |
if ((r = sshkey_to_certified(private)) != 0) { |
363 |
if ((r = sshkey_to_certified(private)) != 0) { |
| 300 |
error("%s: sshkey_to_certified: %s", __func__, ssh_err(r)); |
364 |
error("%s: sshkey_to_certified: %s", __func__, ssh_err(r)); |
| 301 |
sshkey_free(cert); |
365 |
sshkey_free(cert); |
|
Lines 309-323
add_file(int agent_fd, const char *filename, int key_only, int qflag)
Link Here
|
| 309 |
sshkey_free(cert); |
373 |
sshkey_free(cert); |
| 310 |
|
374 |
|
| 311 |
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
375 |
if ((r = ssh_add_identity_constrained(agent_fd, private, comment, |
| 312 |
lifetime, confirm)) != 0) { |
376 |
cert_lifetime, confirm)) != 0) { |
| 313 |
error("Certificate %s (%s) add failed: %s", certpath, |
377 |
error("Certificate %s (%s) add failed: %s", certpath, |
| 314 |
private->cert->key_id, ssh_err(r)); |
378 |
private->cert->key_id, ssh_err(r)); |
| 315 |
goto out; |
379 |
goto out; |
| 316 |
} |
380 |
} |
| 317 |
fprintf(stderr, "Certificate added: %s (%s)\n", certpath, |
381 |
fprintf(stderr, "Certificate added: %s (%s)\n", certpath, |
| 318 |
private->cert->key_id); |
382 |
private->cert->key_id); |
| 319 |
if (lifetime != 0) |
383 |
if (cert_lifetime != 0) |
| 320 |
fprintf(stderr, "Lifetime set to %d seconds\n", lifetime); |
384 |
fprintf(stderr, "Lifetime set to %llu seconds\n", |
|
|
385 |
cert_lifetime); |
| 321 |
if (confirm != 0) |
386 |
if (confirm != 0) |
| 322 |
fprintf(stderr, "The user must confirm each use of the key\n"); |
387 |
fprintf(stderr, "The user must confirm each use of the key\n"); |
| 323 |
out: |
388 |
out: |
|
Lines 424-436
lock_agent(int agent_fd, int lock)
Link Here
|
| 424 |
} |
489 |
} |
| 425 |
|
490 |
|
| 426 |
static int |
491 |
static int |
| 427 |
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) |
492 |
do_file(int agent_fd, int deleting, int key_only, int cert_only, |
|
|
493 |
int force, char *file, int qflag) |
| 428 |
{ |
494 |
{ |
| 429 |
if (deleting) { |
495 |
if (deleting) { |
| 430 |
if (delete_file(agent_fd, file, key_only, qflag) == -1) |
496 |
if (delete_file(agent_fd, file, key_only, |
|
|
497 |
cert_only, qflag) == -1) |
| 431 |
return -1; |
498 |
return -1; |
| 432 |
} else { |
499 |
} else { |
| 433 |
if (add_file(agent_fd, file, key_only, qflag) == -1) |
500 |
if (add_file(agent_fd, file, key_only, cert_only, |
|
|
501 |
force, qflag) == -1) |
| 434 |
return -1; |
502 |
return -1; |
| 435 |
} |
503 |
} |
| 436 |
return 0; |
504 |
return 0; |
|
Lines 463-470
main(int argc, char **argv)
Link Here
|
| 463 |
extern int optind; |
531 |
extern int optind; |
| 464 |
int agent_fd; |
532 |
int agent_fd; |
| 465 |
char *pkcs11provider = NULL; |
533 |
char *pkcs11provider = NULL; |
| 466 |
int r, i, ch, deleting = 0, ret = 0, key_only = 0; |
534 |
int r, i, ch, ret = 0; |
|
|
535 |
int deleting = 0, key_only = 0, cert_only = 0, force = 0; |
| 467 |
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0; |
536 |
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0; |
|
|
537 |
int log_level = SYSLOG_LEVEL_INFO; |
| 468 |
|
538 |
|
| 469 |
ssh_malloc_init(); /* must be called before any mallocs */ |
539 |
ssh_malloc_init(); /* must be called before any mallocs */ |
| 470 |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
540 |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
|
Lines 474-479
main(int argc, char **argv)
Link Here
|
| 474 |
|
544 |
|
| 475 |
setvbuf(stdout, NULL, _IOLBF, 0); |
545 |
setvbuf(stdout, NULL, _IOLBF, 0); |
| 476 |
|
546 |
|
|
|
547 |
log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); |
| 548 |
|
| 477 |
/* First, get a connection to the authentication agent. */ |
549 |
/* First, get a connection to the authentication agent. */ |
| 478 |
switch (r = ssh_get_authentication_socket(&agent_fd)) { |
550 |
switch (r = ssh_get_authentication_socket(&agent_fd)) { |
| 479 |
case 0: |
551 |
case 0: |
|
Lines 487-499
main(int argc, char **argv)
Link Here
|
| 487 |
exit(2); |
559 |
exit(2); |
| 488 |
} |
560 |
} |
| 489 |
|
561 |
|
| 490 |
while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) { |
562 |
while ((ch = getopt(argc, argv, "CcDdfkLlqvXxE:e:s:t:")) != -1) { |
| 491 |
switch (ch) { |
563 |
switch (ch) { |
|
|
564 |
case 'C': |
| 565 |
cert_only = 1; |
| 566 |
break; |
| 492 |
case 'E': |
567 |
case 'E': |
| 493 |
fingerprint_hash = ssh_digest_alg_by_name(optarg); |
568 |
fingerprint_hash = ssh_digest_alg_by_name(optarg); |
| 494 |
if (fingerprint_hash == -1) |
569 |
if (fingerprint_hash == -1) |
| 495 |
fatal("Invalid hash algorithm \"%s\"", optarg); |
570 |
fatal("Invalid hash algorithm \"%s\"", optarg); |
| 496 |
break; |
571 |
break; |
|
|
572 |
case 'f': |
| 573 |
force = 1; |
| 574 |
break; |
| 497 |
case 'k': |
575 |
case 'k': |
| 498 |
key_only = 1; |
576 |
key_only = 1; |
| 499 |
break; |
577 |
break; |
|
Lines 535-540
main(int argc, char **argv)
Link Here
|
| 535 |
case 'q': |
613 |
case 'q': |
| 536 |
qflag = 1; |
614 |
qflag = 1; |
| 537 |
break; |
615 |
break; |
|
|
616 |
case 'v': |
| 617 |
if (log_level == SYSLOG_LEVEL_INFO) |
| 618 |
log_level = SYSLOG_LEVEL_DEBUG1; |
| 619 |
else { |
| 620 |
if (log_level >= SYSLOG_LEVEL_DEBUG1 && |
| 621 |
log_level < SYSLOG_LEVEL_DEBUG3) |
| 622 |
log_level++; |
| 623 |
} |
| 624 |
break; |
| 538 |
default: |
625 |
default: |
| 539 |
usage(); |
626 |
usage(); |
| 540 |
ret = 1; |
627 |
ret = 1; |
|
Lines 542-548
main(int argc, char **argv)
Link Here
|
| 542 |
} |
629 |
} |
| 543 |
} |
630 |
} |
| 544 |
|
631 |
|
| 545 |
if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) |
632 |
/* reinit */ |
|
|
633 |
log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); |
| 634 |
|
| 635 |
if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1 || |
| 636 |
(key_only && cert_only)) |
| 546 |
fatal("Invalid combination of actions"); |
637 |
fatal("Invalid combination of actions"); |
| 547 |
else if (xflag) { |
638 |
else if (xflag) { |
| 548 |
if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) |
639 |
if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) |
|
Lines 583-590
main(int argc, char **argv)
Link Here
|
| 583 |
default_files[i]); |
674 |
default_files[i]); |
| 584 |
if (stat(buf, &st) < 0) |
675 |
if (stat(buf, &st) < 0) |
| 585 |
continue; |
676 |
continue; |
| 586 |
if (do_file(agent_fd, deleting, key_only, buf, |
677 |
if (do_file(agent_fd, deleting, key_only, cert_only, |
| 587 |
qflag) == -1) |
678 |
force, buf, qflag) == -1) |
| 588 |
ret = 1; |
679 |
ret = 1; |
| 589 |
else |
680 |
else |
| 590 |
count++; |
681 |
count++; |
|
Lines 593-600
main(int argc, char **argv)
Link Here
|
| 593 |
ret = 1; |
684 |
ret = 1; |
| 594 |
} else { |
685 |
} else { |
| 595 |
for (i = 0; i < argc; i++) { |
686 |
for (i = 0; i < argc; i++) { |
| 596 |
if (do_file(agent_fd, deleting, key_only, |
687 |
if (do_file(agent_fd, deleting, key_only, cert_only, |
| 597 |
argv[i], qflag) == -1) |
688 |
force, argv[i], qflag) == -1) |
| 598 |
ret = 1; |
689 |
ret = 1; |
| 599 |
} |
690 |
} |
| 600 |
} |
691 |
} |