Skip to content

Commit 6eac006

Browse files
committed
ebox unlock is slow with many configs
Using e.g. pivy-box key unlock on an ebox with many primary configs can be very slow, as we may enumerate all PIV tokens on the system for every single non-present device in the list (which may take 1-2 sec sometimes). With this change, we try agent keys first and then cache enumeration results so that things take far less time. On an ebox with 8 primary configs, where the config on the system is the last one, this takes unlock from ~9sec to 0.25sec with an agent or ~1sec without.
1 parent 545f1a4 commit 6eac006

File tree

5 files changed

+100
-15
lines changed

5 files changed

+100
-15
lines changed

ebox-cmd.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@
6060

6161
#include "words.h"
6262

63-
int ebox_authfd;
63+
int ebox_authfd = -1;
6464
SCARDCONTEXT ebox_ctx;
6565
boolean_t ebox_ctx_init = B_FALSE;
6666
char *ebox_pin;
6767
uint ebox_min_retries = 1;
6868
boolean_t ebox_batch = B_FALSE;
69+
struct piv_token *ebox_enum_tokens = NULL;
6970

7071
#if defined(__sun)
7172
static GetLine *sungl = NULL;
@@ -232,6 +233,12 @@ local_unlock_agent(struct piv_ecdh_box *box)
232233
struct sshbuf *datab = NULL;
233234
boolean_t found = B_FALSE;
234235

236+
if (ebox_authfd == -1 &&
237+
(rc = ssh_get_authentication_socket(&ebox_authfd)) == -1) {
238+
err = ssherrf("ssh_get_authentication_socket", rc);
239+
goto out;
240+
}
241+
235242
pubkey = piv_box_pubkey(box);
236243

237244
rc = ssh_fetch_identitylist(ebox_authfd, &idl);
@@ -368,7 +375,8 @@ local_unlock(struct piv_ecdh_box *box, struct sshkey *cak, const char *name)
368375
struct piv_slot *slot, *cakslot;
369376
struct piv_token *tokens = NULL, *token;
370377

371-
if (ssh_get_authentication_socket(&ebox_authfd) != -1) {
378+
if (ebox_authfd != -1 ||
379+
ssh_get_authentication_socket(&ebox_authfd) != -1) {
372380
agerr = local_unlock_agent(box);
373381
if (agerr == ERRF_OK)
374382
return (ERRF_OK);
@@ -392,14 +400,27 @@ local_unlock(struct piv_ecdh_box *box, struct sshkey *cak, const char *name)
392400
}
393401
}
394402

395-
err = piv_find(ebox_ctx, piv_box_guid(box), GUID_LEN, &tokens);
396-
if (errf_caused_by(err, "NotFoundError")) {
397-
errf_free(err);
398-
err = piv_enumerate(ebox_ctx, &tokens);
399-
if (err && agerr) {
400-
err = errf("AgentError", agerr, "ssh-agent unlock "
401-
"failed, and no PIV tokens were detected on "
402-
"the local system");
403+
/*
404+
* We might try to call local_unlock on a whole lot of configs in a
405+
* row (looking for one that works). If we resort to enumerating all
406+
* the tokens on the system at any point, cache them in
407+
* ebox_enum_tokens so that things are a bit faster.
408+
*/
409+
if (ebox_enum_tokens != NULL) {
410+
tokens = ebox_enum_tokens;
411+
err = NULL;
412+
} else {
413+
err = piv_find(ebox_ctx, piv_box_guid(box), GUID_LEN, &tokens);
414+
if (errf_caused_by(err, "NotFoundError")) {
415+
errf_free(err);
416+
err = piv_enumerate(ebox_ctx, &tokens);
417+
if (err && agerr) {
418+
err = errf("AgentError", agerr, "ssh-agent "
419+
"unlock failed, and no PIV tokens were "
420+
"detected on the local system");
421+
} else {
422+
ebox_enum_tokens = tokens;
423+
}
403424
}
404425
}
405426
errf_free(agerr);

piv.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ struct piv_token {
244244

245245
struct piv_slot *pt_slots;
246246
struct piv_slot *pt_last_slot;
247+
boolean_t pt_did_read_all;
247248

248249
/* YubicoPIV specific stuff */
249250
boolean_t pt_ykpiv; /* Supports YubicoPIV? */
@@ -3109,6 +3110,8 @@ piv_read_all_certs(struct piv_token *tk)
31093110
errf_free(err);
31103111
}
31113112

3113+
tk->pt_did_read_all = B_TRUE;
3114+
31123115
return (ERRF_OK);
31133116
}
31143117

@@ -4759,13 +4762,15 @@ piv_box_find_token(struct piv_token *tks, struct piv_ecdh_box *box,
47594762
* token available.
47604763
*/
47614764
for (pt = tks; pt != NULL; pt = pt->pt_next) {
4762-
if (piv_txn_begin(pt))
4763-
continue;
4764-
if (piv_select(pt) || piv_read_all_certs(pt)) {
4765+
if (!pt->pt_did_read_all) {
4766+
if (piv_txn_begin(pt))
4767+
continue;
4768+
if (piv_select(pt) || piv_read_all_certs(pt)) {
4769+
piv_txn_end(pt);
4770+
continue;
4771+
}
47654772
piv_txn_end(pt);
4766-
continue;
47674773
}
4768-
piv_txn_end(pt);
47694774

47704775
s = NULL;
47714776
while ((s = piv_slot_next(pt, s)) != NULL) {

pivy-box.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,25 @@ interactive_unlock_ebox(struct ebox *ebox)
871871
struct answer *a;
872872
char k = '0';
873873

874+
/* Try to use the pivy-agent to unlock first if we have one. */
875+
config = NULL;
876+
while ((config = ebox_next_config(ebox, config)) != NULL) {
877+
tconfig = ebox_config_tpl(config);
878+
if (ebox_tpl_config_type(tconfig) == EBOX_PRIMARY) {
879+
part = ebox_config_next_part(config, NULL);
880+
tpart = ebox_part_tpl(part);
881+
error = local_unlock_agent(ebox_part_box(part));
882+
if (error) {
883+
errf_free(error);
884+
continue;
885+
}
886+
error = ebox_unlock(ebox, config);
887+
if (error)
888+
return (error);
889+
goto done;
890+
}
891+
}
892+
874893
config = NULL;
875894
while ((config = ebox_next_config(ebox, config)) != NULL) {
876895
tconfig = ebox_config_tpl(config);

pivy-luks.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,26 @@ unlock_or_recover(struct ebox *ebox, const char *descr, boolean_t *recovered)
8888
struct answer *a;
8989
char k = '0';
9090

91+
/* Try to use the pivy-agent to unlock first if we have one. */
92+
config = NULL;
93+
while ((config = ebox_next_config(ebox, config)) != NULL) {
94+
tconfig = ebox_config_tpl(config);
95+
if (ebox_tpl_config_type(tconfig) == EBOX_PRIMARY) {
96+
part = ebox_config_next_part(config, NULL);
97+
tpart = ebox_part_tpl(part);
98+
error = local_unlock_agent(ebox_part_box(part));
99+
if (error) {
100+
errf_free(error);
101+
continue;
102+
}
103+
error = ebox_unlock(ebox, config);
104+
if (error)
105+
return (error);
106+
*recovered = B_FALSE;
107+
goto done;
108+
}
109+
}
110+
91111
config = NULL;
92112
while ((config = ebox_next_config(ebox, config)) != NULL) {
93113
tconfig = ebox_config_tpl(config);

pivy-zfs.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,26 @@ unlock_or_recover(struct ebox *ebox, const char *descr, boolean_t *recovered)
9090
struct answer *a;
9191
char k = '0';
9292

93+
/* Try to use the pivy-agent to unlock first if we have one. */
94+
config = NULL;
95+
while ((config = ebox_next_config(ebox, config)) != NULL) {
96+
tconfig = ebox_config_tpl(config);
97+
if (ebox_tpl_config_type(tconfig) == EBOX_PRIMARY) {
98+
part = ebox_config_next_part(config, NULL);
99+
tpart = ebox_part_tpl(part);
100+
error = local_unlock_agent(ebox_part_box(part));
101+
if (error) {
102+
errf_free(error);
103+
continue;
104+
}
105+
error = ebox_unlock(ebox, config);
106+
if (error)
107+
return (error);
108+
*recovered = B_FALSE;
109+
goto done;
110+
}
111+
}
112+
93113
config = NULL;
94114
while ((config = ebox_next_config(ebox, config)) != NULL) {
95115
tconfig = ebox_config_tpl(config);

0 commit comments

Comments
 (0)