|
Lines 116-121
struct idtable {
Link Here
|
| 116 |
/* private key table */ |
116 |
/* private key table */ |
| 117 |
struct idtable *idtab; |
117 |
struct idtable *idtab; |
| 118 |
|
118 |
|
|
|
119 |
/* certificates waiting for private keys */ |
| 120 |
struct idtable *pending_certs; |
| 121 |
|
| 119 |
int max_fd = 0; |
122 |
int max_fd = 0; |
| 120 |
|
123 |
|
| 121 |
/* pid of shell == parent of agent */ |
124 |
/* pid of shell == parent of agent */ |
|
Lines 159-169
close_socket(SocketEntry *e)
Link Here
|
| 159 |
} |
162 |
} |
| 160 |
|
163 |
|
| 161 |
static void |
164 |
static void |
| 162 |
idtab_init(void) |
165 |
idtable_init(struct idtable **tab) |
| 163 |
{ |
166 |
{ |
| 164 |
idtab = xcalloc(1, sizeof(*idtab)); |
167 |
*tab = xcalloc(1, sizeof(**tab)); |
| 165 |
TAILQ_INIT(&idtab->idlist); |
168 |
TAILQ_INIT(&(*tab)->idlist); |
| 166 |
idtab->nentries = 0; |
169 |
(*tab)->nentries = 0; |
| 167 |
} |
170 |
} |
| 168 |
|
171 |
|
| 169 |
static void |
172 |
static void |
|
Lines 175-191
free_identity(Identity *id)
Link Here
|
| 175 |
free(id); |
178 |
free(id); |
| 176 |
} |
179 |
} |
| 177 |
|
180 |
|
|
|
181 |
static Identity * |
| 182 |
idtable_lookup(struct idtable *tab, struct sshkey *key, int public) |
| 183 |
{ |
| 184 |
Identity *id; |
| 185 |
|
| 186 |
TAILQ_FOREACH(id, &tab->idlist, next) { |
| 187 |
if (public) { |
| 188 |
if (sshkey_equal_public(key, id->key)) |
| 189 |
return (id); |
| 190 |
} else { |
| 191 |
if (sshkey_equal(key, id->key)) |
| 192 |
return (id); |
| 193 |
} |
| 194 |
} |
| 195 |
return (NULL); |
| 196 |
} |
| 197 |
|
| 178 |
/* return matching private key for given public key */ |
198 |
/* return matching private key for given public key */ |
| 179 |
static Identity * |
199 |
static Identity * |
| 180 |
lookup_identity(struct sshkey *key) |
200 |
lookup_identity(struct sshkey *key) |
| 181 |
{ |
201 |
{ |
| 182 |
Identity *id; |
202 |
return idtable_lookup(idtab, key, 0); |
|
|
203 |
} |
| 183 |
|
204 |
|
| 184 |
TAILQ_FOREACH(id, &idtab->idlist, next) { |
205 |
static Identity * |
| 185 |
if (sshkey_equal(key, id->key)) |
206 |
lookup_identity_plain(struct sshkey *key) |
| 186 |
return (id); |
207 |
{ |
| 187 |
} |
208 |
return idtable_lookup(idtab, key, 1); |
| 188 |
return (NULL); |
209 |
} |
|
|
210 |
|
| 211 |
/* return matching certificate key for given key */ |
| 212 |
static Identity * |
| 213 |
lookup_cert(struct sshkey *key) |
| 214 |
{ |
| 215 |
return idtable_lookup(pending_certs, key, 1); |
| 189 |
} |
216 |
} |
| 190 |
|
217 |
|
| 191 |
/* Check confirmation of keysign request */ |
218 |
/* Check confirmation of keysign request */ |
|
Lines 309-314
process_sign_request2(SocketEntry *e)
Link Here
|
| 309 |
free(signature); |
336 |
free(signature); |
| 310 |
} |
337 |
} |
| 311 |
|
338 |
|
|
|
339 |
/* Remove an entry from an idtable; NB. frees 'id' in the process */ |
| 340 |
static void |
| 341 |
idtable_remove(struct idtable *tab, Identity *id) |
| 342 |
{ |
| 343 |
if (tab->nentries < 1) { |
| 344 |
fatal("%s: internal error: nentries %d", |
| 345 |
__func__, tab->nentries); |
| 346 |
} |
| 347 |
TAILQ_REMOVE(&tab->idlist, id, next); |
| 348 |
free_identity(id); |
| 349 |
tab->nentries--; |
| 350 |
} |
| 351 |
|
| 312 |
/* shared */ |
352 |
/* shared */ |
| 313 |
static void |
353 |
static void |
| 314 |
process_remove_identity(SocketEntry *e) |
354 |
process_remove_identity(SocketEntry *e) |
|
Lines 326-357
process_remove_identity(SocketEntry *e)
Link Here
|
| 326 |
goto done; |
366 |
goto done; |
| 327 |
} |
367 |
} |
| 328 |
/* We have this key, free it. */ |
368 |
/* We have this key, free it. */ |
| 329 |
if (idtab->nentries < 1) |
369 |
idtable_remove(idtab, id); |
| 330 |
fatal("%s: internal error: nentries %d", |
370 |
|
| 331 |
__func__, idtab->nentries); |
|
|
| 332 |
TAILQ_REMOVE(&idtab->idlist, id, next); |
| 333 |
free_identity(id); |
| 334 |
idtab->nentries--; |
| 335 |
sshkey_free(key); |
| 336 |
success = 1; |
371 |
success = 1; |
| 337 |
done: |
372 |
done: |
|
|
373 |
/* Clobber any pending certificates that happen to match too */ |
| 374 |
if ((id = lookup_cert(key)) != NULL) |
| 375 |
idtable_remove(pending_certs, id); |
| 376 |
|
| 377 |
sshkey_free(key); |
| 338 |
send_status(e, success); |
378 |
send_status(e, success); |
| 339 |
} |
379 |
} |
| 340 |
|
380 |
|
| 341 |
static void |
381 |
static void |
| 342 |
process_remove_all_identities(SocketEntry *e) |
382 |
idtable_clear(struct idtable *tab) |
| 343 |
{ |
383 |
{ |
| 344 |
Identity *id; |
384 |
Identity *id; |
| 345 |
|
385 |
|
| 346 |
/* Loop over all identities and clear the keys. */ |
386 |
/* Loop over all identities and clear the keys. */ |
| 347 |
for (id = TAILQ_FIRST(&idtab->idlist); id; |
387 |
for (id = TAILQ_FIRST(&tab->idlist); id != NULL; |
| 348 |
id = TAILQ_FIRST(&idtab->idlist)) { |
388 |
id = TAILQ_FIRST(&tab->idlist)) { |
| 349 |
TAILQ_REMOVE(&idtab->idlist, id, next); |
389 |
TAILQ_REMOVE(&tab->idlist, id, next); |
| 350 |
free_identity(id); |
390 |
free_identity(id); |
| 351 |
} |
391 |
} |
|
|
392 |
} |
| 393 |
|
| 394 |
static void |
| 395 |
process_remove_all_identities(SocketEntry *e) |
| 396 |
{ |
| 397 |
idtable_clear(idtab); |
| 398 |
idtable_clear(pending_certs); |
| 352 |
|
399 |
|
| 353 |
/* Mark that there are no identities. */ |
400 |
/* Mark that there are no identities. */ |
| 354 |
idtab->nentries = 0; |
401 |
idtab->nentries = 0; |
|
|
402 |
pending_certs->nentries = 0; |
| 355 |
|
403 |
|
| 356 |
/* Send success. */ |
404 |
/* Send success. */ |
| 357 |
send_status(e, 1); |
405 |
send_status(e, 1); |
|
Lines 383-388
reaper(void)
Link Here
|
| 383 |
return (deadline - now); |
431 |
return (deadline - now); |
| 384 |
} |
432 |
} |
| 385 |
|
433 |
|
|
|
434 |
static int |
| 435 |
promote_cert(Identity *private_id, Identity *cert) |
| 436 |
{ |
| 437 |
Identity *id; |
| 438 |
struct sshkey *grafted = NULL; |
| 439 |
int r = SSH_ERR_INTERNAL_ERROR; |
| 440 |
|
| 441 |
if ((r = sshkey_copy_private(private_id->key, &grafted)) != 0) { |
| 442 |
error("%s: sshkey_copy_private: %s", __func__, ssh_err(r)); |
| 443 |
goto out; |
| 444 |
} |
| 445 |
if ((r = sshkey_to_certified(grafted)) != 0) { |
| 446 |
error("%s: sshkey_to_certified: %s", __func__, ssh_err(r)); |
| 447 |
goto out; |
| 448 |
} |
| 449 |
if ((r = sshkey_cert_copy(cert->key, grafted)) != 0) { |
| 450 |
error("%s: sshkey_cert_copy: %s", __func__, ssh_err(r)); |
| 451 |
goto out; |
| 452 |
} |
| 453 |
|
| 454 |
/* Check whether the grafted cert is already recorded */ |
| 455 |
if ((id = lookup_identity(grafted)) == NULL) { |
| 456 |
debug("%s: added new %s private cert, now have %d private keys", |
| 457 |
__func__, sshkey_type(grafted), idtab->nentries); |
| 458 |
id = xcalloc(1, sizeof(*id)); |
| 459 |
TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
| 460 |
idtab->nentries++; |
| 461 |
id->key = grafted; |
| 462 |
grafted = NULL; /* transfer */ |
| 463 |
} else { |
| 464 |
debug("%s: existing %s private cert", |
| 465 |
__func__, sshkey_type(grafted)); |
| 466 |
/* Update the identity, as constraints may have changed */ |
| 467 |
free(id->comment); |
| 468 |
free(id->provider); |
| 469 |
} |
| 470 |
id->comment = private_id->comment != NULL ? |
| 471 |
xstrdup(private_id->comment) : NULL; |
| 472 |
id->provider = private_id->provider != NULL? |
| 473 |
xstrdup(private_id->provider) : NULL; |
| 474 |
id->death = private_id->death; |
| 475 |
id->confirm = private_id->confirm; |
| 476 |
|
| 477 |
/* success */ |
| 478 |
r = 0; |
| 479 |
out: |
| 480 |
sshkey_free(grafted); |
| 481 |
return r; |
| 482 |
} |
| 483 |
|
| 484 |
/* Check whether an incoming private key against the pending cert list */ |
| 485 |
static int |
| 486 |
check_pending_by_key(Identity *private_id) |
| 487 |
{ |
| 488 |
Identity *cert; |
| 489 |
int r; |
| 490 |
|
| 491 |
debug3("%s: entering for %s, npending = %d", __func__, |
| 492 |
sshkey_type(private_id->key), pending_certs->nentries); |
| 493 |
/* A private key could conceivable match multiple certificates */ |
| 494 |
while ((cert = lookup_cert(private_id->key)) != NULL) { |
| 495 |
debug3("%s: found matching cert %s", |
| 496 |
__func__, sshkey_type(cert->key)); |
| 497 |
if ((r = promote_cert(private_id, cert)) != 0) |
| 498 |
return r; |
| 499 |
/* Remove the cert from the pending list */ |
| 500 |
idtable_remove(pending_certs, cert); |
| 501 |
debug("%s: remove pending cert, now have %d pending", |
| 502 |
__func__, pending_certs->nentries); |
| 503 |
} |
| 504 |
return 0; |
| 505 |
} |
| 506 |
|
| 507 |
/* Check whether an incoming cert against the pending cert list */ |
| 508 |
static int |
| 509 |
check_pending_by_cert(Identity *cert, int *matched) |
| 510 |
{ |
| 511 |
Identity *private_id; |
| 512 |
int r; |
| 513 |
|
| 514 |
*matched = 0; |
| 515 |
debug3("%s: entering for %s", __func__, sshkey_type(cert->key)); |
| 516 |
/* A certificate should match at most one private key */ |
| 517 |
if ((private_id = lookup_identity_plain(cert->key)) != NULL) { |
| 518 |
debug3("%s: found matching key %s", |
| 519 |
__func__, sshkey_type(private_id->key)); |
| 520 |
if ((r = promote_cert(private_id, cert)) != 0) |
| 521 |
return r; |
| 522 |
*matched = 1; |
| 523 |
} |
| 524 |
return 0; |
| 525 |
} |
| 526 |
|
| 386 |
static void |
527 |
static void |
| 387 |
process_add_identity(SocketEntry *e) |
528 |
process_add_identity(SocketEntry *e) |
| 388 |
{ |
529 |
{ |
|
Lines 402-407
process_add_identity(SocketEntry *e)
Link Here
|
| 402 |
goto err; |
543 |
goto err; |
| 403 |
} |
544 |
} |
| 404 |
|
545 |
|
|
|
546 |
debug3("%s: have %s key constraint len %zu", __func__, |
| 547 |
sshkey_type(k), sshbuf_len(e->request)); |
| 548 |
|
| 405 |
while (sshbuf_len(e->request)) { |
549 |
while (sshbuf_len(e->request)) { |
| 406 |
if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { |
550 |
if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { |
| 407 |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
551 |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
|
Lines 441-447
process_add_identity(SocketEntry *e)
Link Here
|
| 441 |
} |
585 |
} |
| 442 |
} |
586 |
} |
| 443 |
|
587 |
|
| 444 |
success = 1; |
|
|
| 445 |
if (lifetime && !death) |
588 |
if (lifetime && !death) |
| 446 |
death = monotime() + lifetime; |
589 |
death = monotime() + lifetime; |
| 447 |
if ((id = lookup_identity(k)) == NULL) { |
590 |
if ((id = lookup_identity(k)) == NULL) { |
|
Lines 449-467
process_add_identity(SocketEntry *e)
Link Here
|
| 449 |
TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
592 |
TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
| 450 |
/* Increment the number of identities. */ |
593 |
/* Increment the number of identities. */ |
| 451 |
idtab->nentries++; |
594 |
idtab->nentries++; |
|
|
595 |
debug("%s: new key, now have %d", __func__, idtab->nentries); |
| 452 |
} else { |
596 |
} else { |
| 453 |
/* key state might have been updated */ |
597 |
/* key state might have been updated */ |
| 454 |
sshkey_free(id->key); |
598 |
sshkey_free(id->key); |
| 455 |
free(id->comment); |
599 |
free(id->comment); |
|
|
600 |
debug("%s: existing key", __func__); |
| 456 |
} |
601 |
} |
| 457 |
id->key = k; |
602 |
id->key = k; |
| 458 |
id->comment = comment; |
603 |
id->comment = comment; |
| 459 |
id->death = death; |
604 |
id->death = death; |
| 460 |
id->confirm = confirm; |
605 |
id->confirm = confirm; |
|
|
606 |
|
| 607 |
/* Can this key matriculate a pending_cert? */ |
| 608 |
if (!sshkey_is_cert(id->key) && check_pending_by_key(id) != 0) |
| 609 |
goto send; |
| 610 |
|
| 611 |
success = 1; |
| 461 |
send: |
612 |
send: |
| 462 |
send_status(e, success); |
613 |
send_status(e, success); |
| 463 |
} |
614 |
} |
| 464 |
|
615 |
|
|
|
616 |
static void |
| 617 |
process_add_certificates(SocketEntry *e) |
| 618 |
{ |
| 619 |
Identity *id; |
| 620 |
int matched, success = 0; |
| 621 |
struct sshkey *k = NULL; |
| 622 |
int r = SSH_ERR_INTERNAL_ERROR; |
| 623 |
|
| 624 |
debug3("%s: entering len = %zu", __func__, sshbuf_len(e->request)); |
| 625 |
|
| 626 |
while (sshbuf_len(e->request)) { |
| 627 |
sshkey_free(k); |
| 628 |
k = NULL; |
| 629 |
if ((r = sshkey_froms(e->request, &k)) != 0) { |
| 630 |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
| 631 |
goto send; |
| 632 |
} |
| 633 |
debug2("%s: key type %s", __func__, sshkey_type(k)); |
| 634 |
if (!sshkey_is_cert(k)) { |
| 635 |
error("%s: key is not a certificate", __func__); |
| 636 |
goto send; |
| 637 |
} |
| 638 |
if ((id = lookup_identity(k)) != NULL) { |
| 639 |
debug("%s: cert already has key", __func__); |
| 640 |
} else if ((id = lookup_cert(k)) != NULL) { |
| 641 |
debug("%s: cert already enqueued", __func__); |
| 642 |
if ((r = check_pending_by_cert(id, &matched)) != 0) |
| 643 |
goto send; |
| 644 |
if (matched) { |
| 645 |
debug("%s: cert matches, remove from pending", |
| 646 |
__func__); |
| 647 |
idtable_remove(pending_certs, id); |
| 648 |
} |
| 649 |
} else { |
| 650 |
id = xcalloc(1, sizeof(Identity)); |
| 651 |
id->key = k; |
| 652 |
k = NULL; /* transfer */ |
| 653 |
if ((r = check_pending_by_cert(id, &matched)) != 0) |
| 654 |
goto send; |
| 655 |
if (matched) |
| 656 |
free_identity(id); |
| 657 |
else { |
| 658 |
TAILQ_INSERT_TAIL(&pending_certs->idlist, |
| 659 |
id, next); |
| 660 |
pending_certs->nentries++; |
| 661 |
debug("%s: add cert, nentries %d", |
| 662 |
__func__, pending_certs->nentries); |
| 663 |
} |
| 664 |
} |
| 665 |
} |
| 666 |
success = 1; |
| 667 |
send: |
| 668 |
sshkey_free(k); |
| 669 |
send_status(e, success); |
| 670 |
} |
| 671 |
|
| 672 |
|
| 465 |
/* XXX todo: encrypt sensitive data with passphrase */ |
673 |
/* XXX todo: encrypt sensitive data with passphrase */ |
| 466 |
static void |
674 |
static void |
| 467 |
process_lock_agent(SocketEntry *e, int lock) |
675 |
process_lock_agent(SocketEntry *e, int lock) |
|
Lines 539-545
process_add_smartcard_key(SocketEntry *e)
Link Here
|
| 539 |
u_int seconds; |
747 |
u_int seconds; |
| 540 |
time_t death = 0; |
748 |
time_t death = 0; |
| 541 |
u_char type; |
749 |
u_char type; |
| 542 |
struct sshkey **keys = NULL, *k; |
750 |
struct sshkey **keys = NULL; |
| 543 |
Identity *id; |
751 |
Identity *id; |
| 544 |
|
752 |
|
| 545 |
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || |
753 |
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || |
|
Lines 586-610
process_add_smartcard_key(SocketEntry *e)
Link Here
|
| 586 |
|
794 |
|
| 587 |
count = pkcs11_add_provider(canonical_provider, pin, &keys); |
795 |
count = pkcs11_add_provider(canonical_provider, pin, &keys); |
| 588 |
for (i = 0; i < count; i++) { |
796 |
for (i = 0; i < count; i++) { |
| 589 |
k = keys[i]; |
797 |
if ((id = lookup_identity(keys[i])) == NULL) { |
| 590 |
if (lookup_identity(k) == NULL) { |
|
|
| 591 |
id = xcalloc(1, sizeof(Identity)); |
798 |
id = xcalloc(1, sizeof(Identity)); |
| 592 |
id->key = k; |
|
|
| 593 |
id->provider = xstrdup(canonical_provider); |
799 |
id->provider = xstrdup(canonical_provider); |
| 594 |
id->comment = xstrdup(canonical_provider); /* XXX */ |
800 |
id->comment = xstrdup(canonical_provider); /* XXX */ |
| 595 |
id->death = death; |
801 |
id->death = death; |
| 596 |
id->confirm = confirm; |
802 |
id->confirm = confirm; |
|
|
803 |
id->key = keys[i]; |
| 804 |
keys[i] = NULL; /* transfer */ |
| 597 |
TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
805 |
TAILQ_INSERT_TAIL(&idtab->idlist, id, next); |
| 598 |
idtab->nentries++; |
806 |
idtab->nentries++; |
| 599 |
success = 1; |
|
|
| 600 |
} else { |
| 601 |
sshkey_free(k); |
| 602 |
} |
807 |
} |
| 603 |
keys[i] = NULL; |
808 |
/* Can this key matriculate a pending_cert? */ |
|
|
809 |
if (!sshkey_is_cert(id->key) && check_pending_by_key(id) != 0) { |
| 810 |
success = 0; |
| 811 |
goto send; |
| 812 |
} |
| 813 |
success = 1; |
| 604 |
} |
814 |
} |
| 605 |
send: |
815 |
send: |
| 606 |
free(pin); |
816 |
free(pin); |
| 607 |
free(provider); |
817 |
free(provider); |
|
|
818 |
for (i = 0; i < count; i++) |
| 819 |
sshkey_free(keys[i]); |
| 608 |
free(keys); |
820 |
free(keys); |
| 609 |
send_status(e, success); |
821 |
send_status(e, success); |
| 610 |
} |
822 |
} |
|
Lines 743-748
process_message(u_int socknum)
Link Here
|
| 743 |
process_remove_smartcard_key(e); |
955 |
process_remove_smartcard_key(e); |
| 744 |
break; |
956 |
break; |
| 745 |
#endif /* ENABLE_PKCS11 */ |
957 |
#endif /* ENABLE_PKCS11 */ |
|
|
958 |
case SSH2_AGENTC_ADD_CERTIFICATES: |
| 959 |
process_add_certificates(e); |
| 960 |
break; |
| 746 |
default: |
961 |
default: |
| 747 |
/* Unknown message. Respond with failure. */ |
962 |
/* Unknown message. Respond with failure. */ |
| 748 |
error("Unknown message %d", type); |
963 |
error("Unknown message %d", type); |
|
Lines 1287-1293
skip:
Link Here
|
| 1287 |
new_socket(AUTH_SOCKET, sock); |
1502 |
new_socket(AUTH_SOCKET, sock); |
| 1288 |
if (ac > 0) |
1503 |
if (ac > 0) |
| 1289 |
parent_alive_interval = 10; |
1504 |
parent_alive_interval = 10; |
| 1290 |
idtab_init(); |
1505 |
idtable_init(&idtab); |
|
|
1506 |
idtable_init(&pending_certs); |
| 1291 |
signal(SIGPIPE, SIG_IGN); |
1507 |
signal(SIGPIPE, SIG_IGN); |
| 1292 |
signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); |
1508 |
signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); |
| 1293 |
signal(SIGHUP, cleanup_handler); |
1509 |
signal(SIGHUP, cleanup_handler); |