1*31c848ccSdjm /* $OpenBSD: ssh-sk-client.c,v 1.12 2022/01/14 03:34:00 djm Exp $ */
215704ed2Sdjm /*
315704ed2Sdjm * Copyright (c) 2019 Google LLC
415704ed2Sdjm *
515704ed2Sdjm * Permission to use, copy, modify, and distribute this software for any
615704ed2Sdjm * purpose with or without fee is hereby granted, provided that the above
715704ed2Sdjm * copyright notice and this permission notice appear in all copies.
815704ed2Sdjm *
915704ed2Sdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1015704ed2Sdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1115704ed2Sdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1215704ed2Sdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1315704ed2Sdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1415704ed2Sdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515704ed2Sdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1615704ed2Sdjm */
1715704ed2Sdjm
1815704ed2Sdjm #include <sys/types.h>
1915704ed2Sdjm #include <sys/socket.h>
2015704ed2Sdjm #include <sys/wait.h>
2115704ed2Sdjm
22fb247a26Sdjm #include <fcntl.h>
232db06755Sdjm #include <limits.h>
2415704ed2Sdjm #include <errno.h>
2515704ed2Sdjm #include <signal.h>
2615704ed2Sdjm #include <stdarg.h>
2715704ed2Sdjm #include <stdio.h>
2815704ed2Sdjm #include <stdlib.h>
2915704ed2Sdjm #include <string.h>
3015704ed2Sdjm #include <unistd.h>
3115704ed2Sdjm
3215704ed2Sdjm #include "log.h"
3315704ed2Sdjm #include "ssherr.h"
3415704ed2Sdjm #include "sshbuf.h"
3515704ed2Sdjm #include "sshkey.h"
3615704ed2Sdjm #include "msg.h"
3715704ed2Sdjm #include "digest.h"
3815704ed2Sdjm #include "pathnames.h"
3915704ed2Sdjm #include "ssh-sk.h"
40e9716d4dSdtucker #include "misc.h"
4115704ed2Sdjm
4215704ed2Sdjm /* #define DEBUG_SK 1 */
4315704ed2Sdjm
4415704ed2Sdjm static int
start_helper(int * fdp,pid_t * pidp,void (** osigchldp)(int))4515704ed2Sdjm start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
4615704ed2Sdjm {
4715704ed2Sdjm void (*osigchld)(int);
4848e6b99dSdjm int oerrno, pair[2];
4915704ed2Sdjm pid_t pid;
5015704ed2Sdjm char *helper, *verbosity = NULL;
5115704ed2Sdjm
5215704ed2Sdjm *fdp = -1;
5315704ed2Sdjm *pidp = 0;
5415704ed2Sdjm *osigchldp = SIG_DFL;
5515704ed2Sdjm
5615704ed2Sdjm helper = getenv("SSH_SK_HELPER");
5715704ed2Sdjm if (helper == NULL || strlen(helper) == 0)
5815704ed2Sdjm helper = _PATH_SSH_SK_HELPER;
59fb247a26Sdjm if (access(helper, X_OK) != 0) {
60fb247a26Sdjm oerrno = errno;
6148e6b99dSdjm error_f("helper \"%s\" unusable: %s", helper, strerror(errno));
62fb247a26Sdjm errno = oerrno;
63fb247a26Sdjm return SSH_ERR_SYSTEM_ERROR;
64fb247a26Sdjm }
6515704ed2Sdjm #ifdef DEBUG_SK
6615704ed2Sdjm verbosity = "-vvv";
6715704ed2Sdjm #endif
6815704ed2Sdjm
6915704ed2Sdjm /* Start helper */
7015704ed2Sdjm if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
7115704ed2Sdjm error("socketpair: %s", strerror(errno));
7215704ed2Sdjm return SSH_ERR_SYSTEM_ERROR;
7315704ed2Sdjm }
74e9716d4dSdtucker osigchld = ssh_signal(SIGCHLD, SIG_DFL);
7515704ed2Sdjm if ((pid = fork()) == -1) {
7615704ed2Sdjm oerrno = errno;
7715704ed2Sdjm error("fork: %s", strerror(errno));
7815704ed2Sdjm close(pair[0]);
7915704ed2Sdjm close(pair[1]);
80e9716d4dSdtucker ssh_signal(SIGCHLD, osigchld);
8115704ed2Sdjm errno = oerrno;
8215704ed2Sdjm return SSH_ERR_SYSTEM_ERROR;
8315704ed2Sdjm }
8415704ed2Sdjm if (pid == 0) {
8515704ed2Sdjm if ((dup2(pair[1], STDIN_FILENO) == -1) ||
8615704ed2Sdjm (dup2(pair[1], STDOUT_FILENO) == -1)) {
8748e6b99dSdjm error_f("dup2: %s", strerror(errno));
8815704ed2Sdjm _exit(1);
8915704ed2Sdjm }
9015704ed2Sdjm close(pair[0]);
9115704ed2Sdjm close(pair[1]);
9215704ed2Sdjm closefrom(STDERR_FILENO + 1);
9348e6b99dSdjm debug_f("starting %s %s", helper,
9415704ed2Sdjm verbosity == NULL ? "" : verbosity);
9515704ed2Sdjm execlp(helper, helper, verbosity, (char *)NULL);
9648e6b99dSdjm error_f("execlp: %s", strerror(errno));
9715704ed2Sdjm _exit(1);
9815704ed2Sdjm }
9915704ed2Sdjm close(pair[1]);
10015704ed2Sdjm
10115704ed2Sdjm /* success */
10248e6b99dSdjm debug3_f("started pid=%ld", (long)pid);
10315704ed2Sdjm *fdp = pair[0];
10415704ed2Sdjm *pidp = pid;
10515704ed2Sdjm *osigchldp = osigchld;
10615704ed2Sdjm return 0;
10715704ed2Sdjm }
10815704ed2Sdjm
10915704ed2Sdjm static int
reap_helper(pid_t pid)11015704ed2Sdjm reap_helper(pid_t pid)
11115704ed2Sdjm {
11215704ed2Sdjm int status, oerrno;
11315704ed2Sdjm
11448e6b99dSdjm debug3_f("pid=%ld", (long)pid);
11515704ed2Sdjm
11615704ed2Sdjm errno = 0;
11715704ed2Sdjm while (waitpid(pid, &status, 0) == -1) {
11815704ed2Sdjm if (errno == EINTR) {
11915704ed2Sdjm errno = 0;
12015704ed2Sdjm continue;
12115704ed2Sdjm }
12215704ed2Sdjm oerrno = errno;
12348e6b99dSdjm error_f("waitpid: %s", strerror(errno));
12415704ed2Sdjm errno = oerrno;
12515704ed2Sdjm return SSH_ERR_SYSTEM_ERROR;
12615704ed2Sdjm }
12715704ed2Sdjm if (!WIFEXITED(status)) {
12848e6b99dSdjm error_f("helper exited abnormally");
12915704ed2Sdjm return SSH_ERR_AGENT_FAILURE;
13015704ed2Sdjm } else if (WEXITSTATUS(status) != 0) {
13148e6b99dSdjm error_f("helper exited with non-zero exit status");
13215704ed2Sdjm return SSH_ERR_AGENT_FAILURE;
13315704ed2Sdjm }
13415704ed2Sdjm return 0;
13515704ed2Sdjm }
13615704ed2Sdjm
13715704ed2Sdjm static int
client_converse(struct sshbuf * msg,struct sshbuf ** respp,u_int type)138a769387cSdjm client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type)
13915704ed2Sdjm {
140a769387cSdjm int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR;
141a769387cSdjm u_int rtype, rerr;
14215704ed2Sdjm pid_t pid;
14315704ed2Sdjm u_char version;
14415704ed2Sdjm void (*osigchld)(int);
145a769387cSdjm struct sshbuf *req = NULL, *resp = NULL;
14615704ed2Sdjm *respp = NULL;
14715704ed2Sdjm
14815704ed2Sdjm if ((r = start_helper(&fd, &pid, &osigchld)) != 0)
14915704ed2Sdjm return r;
15015704ed2Sdjm
151a769387cSdjm if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) {
15215704ed2Sdjm r = SSH_ERR_ALLOC_FAIL;
15315704ed2Sdjm goto out;
15415704ed2Sdjm }
155a769387cSdjm /* Request preamble: type, log_on_stderr, log_level */
156a769387cSdjm ll = log_level_get();
157a769387cSdjm if ((r = sshbuf_put_u32(req, type)) != 0 ||
158a769387cSdjm (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 ||
159a769387cSdjm (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 ||
160a769387cSdjm (r = sshbuf_putb(req, msg)) != 0) {
16148e6b99dSdjm error_fr(r, "compose");
162a769387cSdjm goto out;
163a769387cSdjm }
16415704ed2Sdjm if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) {
16548e6b99dSdjm error_fr(r, "send");
16615704ed2Sdjm goto out;
16715704ed2Sdjm }
16815704ed2Sdjm if ((r = ssh_msg_recv(fd, resp)) != 0) {
16948e6b99dSdjm error_fr(r, "receive");
17015704ed2Sdjm goto out;
17115704ed2Sdjm }
17215704ed2Sdjm if ((r = sshbuf_get_u8(resp, &version)) != 0) {
17348e6b99dSdjm error_fr(r, "parse version");
17415704ed2Sdjm goto out;
17515704ed2Sdjm }
17615704ed2Sdjm if (version != SSH_SK_HELPER_VERSION) {
17748e6b99dSdjm error_f("unsupported version: got %u, expected %u",
17848e6b99dSdjm version, SSH_SK_HELPER_VERSION);
17915704ed2Sdjm r = SSH_ERR_INVALID_FORMAT;
18015704ed2Sdjm goto out;
18115704ed2Sdjm }
182a769387cSdjm if ((r = sshbuf_get_u32(resp, &rtype)) != 0) {
18348e6b99dSdjm error_fr(r, "parse message type");
1842db06755Sdjm goto out;
1852db06755Sdjm }
186a769387cSdjm if (rtype == SSH_SK_HELPER_ERROR) {
1872db06755Sdjm if ((r = sshbuf_get_u32(resp, &rerr)) != 0) {
18848e6b99dSdjm error_fr(r, "parse");
1892db06755Sdjm goto out;
1902db06755Sdjm }
19148e6b99dSdjm debug_f("helper returned error -%u", rerr);
1922db06755Sdjm /* OpenSSH error values are negative; encoded as -err on wire */
1932db06755Sdjm if (rerr == 0 || rerr >= INT_MAX)
1942db06755Sdjm r = SSH_ERR_INTERNAL_ERROR;
1952db06755Sdjm else
1962db06755Sdjm r = -(int)rerr;
1972db06755Sdjm goto out;
198a769387cSdjm } else if (rtype != type) {
19948e6b99dSdjm error_f("helper returned incorrect message type %u, "
20048e6b99dSdjm "expecting %u", rtype, type);
2012db06755Sdjm r = SSH_ERR_INTERNAL_ERROR;
2022db06755Sdjm goto out;
2032db06755Sdjm }
20415704ed2Sdjm /* success */
20515704ed2Sdjm r = 0;
20615704ed2Sdjm out:
20715704ed2Sdjm oerrno = errno;
20815704ed2Sdjm close(fd);
20915704ed2Sdjm if ((r2 = reap_helper(pid)) != 0) {
21015704ed2Sdjm if (r == 0) {
21115704ed2Sdjm r = r2;
21215704ed2Sdjm oerrno = errno;
21315704ed2Sdjm }
21415704ed2Sdjm }
21515704ed2Sdjm if (r == 0) {
21615704ed2Sdjm *respp = resp;
21715704ed2Sdjm resp = NULL;
21815704ed2Sdjm }
219a769387cSdjm sshbuf_free(req);
22015704ed2Sdjm sshbuf_free(resp);
221e9716d4dSdtucker ssh_signal(SIGCHLD, osigchld);
22215704ed2Sdjm errno = oerrno;
22315704ed2Sdjm return r;
22415704ed2Sdjm
22515704ed2Sdjm }
22615704ed2Sdjm
22715704ed2Sdjm int
sshsk_sign(const char * provider,struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t datalen,u_int compat,const char * pin)22815704ed2Sdjm sshsk_sign(const char *provider, struct sshkey *key,
22915704ed2Sdjm u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
2302db06755Sdjm u_int compat, const char *pin)
23115704ed2Sdjm {
23215704ed2Sdjm int oerrno, r = SSH_ERR_INTERNAL_ERROR;
23315704ed2Sdjm struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
23415704ed2Sdjm
23515704ed2Sdjm *sigp = NULL;
23615704ed2Sdjm *lenp = 0;
23715704ed2Sdjm
23815704ed2Sdjm if ((kbuf = sshbuf_new()) == NULL ||
23915704ed2Sdjm (req = sshbuf_new()) == NULL) {
24015704ed2Sdjm r = SSH_ERR_ALLOC_FAIL;
24115704ed2Sdjm goto out;
24215704ed2Sdjm }
24315704ed2Sdjm
24415704ed2Sdjm if ((r = sshkey_private_serialize(key, kbuf)) != 0) {
24548e6b99dSdjm error_fr(r, "encode key");
24615704ed2Sdjm goto out;
24715704ed2Sdjm }
248a769387cSdjm if ((r = sshbuf_put_stringb(req, kbuf)) != 0 ||
24915704ed2Sdjm (r = sshbuf_put_cstring(req, provider)) != 0 ||
25015704ed2Sdjm (r = sshbuf_put_string(req, data, datalen)) != 0 ||
25115704ed2Sdjm (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */
2522db06755Sdjm (r = sshbuf_put_u32(req, compat)) != 0 ||
2532db06755Sdjm (r = sshbuf_put_cstring(req, pin)) != 0) {
25448e6b99dSdjm error_fr(r, "compose");
25515704ed2Sdjm goto out;
25615704ed2Sdjm }
25715704ed2Sdjm
2582db06755Sdjm if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0)
25915704ed2Sdjm goto out;
26015704ed2Sdjm
26115704ed2Sdjm if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
26248e6b99dSdjm error_fr(r, "parse signature");
26315704ed2Sdjm r = SSH_ERR_INVALID_FORMAT;
26415704ed2Sdjm goto out;
26515704ed2Sdjm }
26615704ed2Sdjm if (sshbuf_len(resp) != 0) {
26748e6b99dSdjm error_f("trailing data in response");
26815704ed2Sdjm r = SSH_ERR_INVALID_FORMAT;
26915704ed2Sdjm goto out;
27015704ed2Sdjm }
27115704ed2Sdjm /* success */
27215704ed2Sdjm r = 0;
27315704ed2Sdjm out:
27415704ed2Sdjm oerrno = errno;
27515704ed2Sdjm if (r != 0) {
27615704ed2Sdjm freezero(*sigp, *lenp);
27715704ed2Sdjm *sigp = NULL;
27815704ed2Sdjm *lenp = 0;
27915704ed2Sdjm }
28015704ed2Sdjm sshbuf_free(kbuf);
28115704ed2Sdjm sshbuf_free(req);
28215704ed2Sdjm sshbuf_free(resp);
28315704ed2Sdjm errno = oerrno;
28415704ed2Sdjm return r;
28515704ed2Sdjm }
28615704ed2Sdjm
28715704ed2Sdjm int
sshsk_enroll(int type,const char * provider_path,const char * device,const char * application,const char * userid,uint8_t flags,const char * pin,struct sshbuf * challenge_buf,struct sshkey ** keyp,struct sshbuf * attest)288a0caf565Sdjm sshsk_enroll(int type, const char *provider_path, const char *device,
289a0caf565Sdjm const char *application, const char *userid, uint8_t flags,
290a0caf565Sdjm const char *pin, struct sshbuf *challenge_buf,
2912db06755Sdjm struct sshkey **keyp, struct sshbuf *attest)
29215704ed2Sdjm {
29315704ed2Sdjm int oerrno, r = SSH_ERR_INTERNAL_ERROR;
29415704ed2Sdjm struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL;
29515704ed2Sdjm struct sshkey *key = NULL;
29615704ed2Sdjm
29715704ed2Sdjm *keyp = NULL;
29815704ed2Sdjm if (attest != NULL)
29915704ed2Sdjm sshbuf_reset(attest);
30015704ed2Sdjm
30115704ed2Sdjm if (type < 0)
30215704ed2Sdjm return SSH_ERR_INVALID_ARGUMENT;
30315704ed2Sdjm
30415704ed2Sdjm if ((abuf = sshbuf_new()) == NULL ||
30515704ed2Sdjm (kbuf = sshbuf_new()) == NULL ||
30615704ed2Sdjm (req = sshbuf_new()) == NULL) {
30715704ed2Sdjm r = SSH_ERR_ALLOC_FAIL;
30815704ed2Sdjm goto out;
30915704ed2Sdjm }
31015704ed2Sdjm
311a769387cSdjm if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 ||
31215704ed2Sdjm (r = sshbuf_put_cstring(req, provider_path)) != 0 ||
313a0caf565Sdjm (r = sshbuf_put_cstring(req, device)) != 0 ||
31415704ed2Sdjm (r = sshbuf_put_cstring(req, application)) != 0 ||
315a0caf565Sdjm (r = sshbuf_put_cstring(req, userid)) != 0 ||
31615704ed2Sdjm (r = sshbuf_put_u8(req, flags)) != 0 ||
3172db06755Sdjm (r = sshbuf_put_cstring(req, pin)) != 0 ||
31815704ed2Sdjm (r = sshbuf_put_stringb(req, challenge_buf)) != 0) {
31948e6b99dSdjm error_fr(r, "compose");
32015704ed2Sdjm goto out;
32115704ed2Sdjm }
32215704ed2Sdjm
3232db06755Sdjm if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0)
32415704ed2Sdjm goto out;
32515704ed2Sdjm
32615704ed2Sdjm if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
32715704ed2Sdjm (r = sshbuf_get_stringb(resp, abuf)) != 0) {
32848e6b99dSdjm error_fr(r, "parse");
32915704ed2Sdjm r = SSH_ERR_INVALID_FORMAT;
33015704ed2Sdjm goto out;
33115704ed2Sdjm }
33215704ed2Sdjm if (sshbuf_len(resp) != 0) {
33348e6b99dSdjm error_f("trailing data in response");
33415704ed2Sdjm r = SSH_ERR_INVALID_FORMAT;
33515704ed2Sdjm goto out;
33615704ed2Sdjm }
33715704ed2Sdjm if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
33848e6b99dSdjm error_fr(r, "encode");
33915704ed2Sdjm goto out;
34015704ed2Sdjm }
34115704ed2Sdjm if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) {
34248e6b99dSdjm error_fr(r, "encode attestation information");
34315704ed2Sdjm goto out;
34415704ed2Sdjm }
34515704ed2Sdjm
34615704ed2Sdjm /* success */
34715704ed2Sdjm r = 0;
34815704ed2Sdjm *keyp = key;
34915704ed2Sdjm key = NULL;
35015704ed2Sdjm out:
35115704ed2Sdjm oerrno = errno;
35215704ed2Sdjm sshkey_free(key);
35315704ed2Sdjm sshbuf_free(kbuf);
35415704ed2Sdjm sshbuf_free(abuf);
35515704ed2Sdjm sshbuf_free(req);
35615704ed2Sdjm sshbuf_free(resp);
35715704ed2Sdjm errno = oerrno;
35815704ed2Sdjm return r;
35915704ed2Sdjm }
3609fe3789cSdjm
361991d5a20Sdjm static void
sshsk_free_resident_key(struct sshsk_resident_key * srk)362991d5a20Sdjm sshsk_free_resident_key(struct sshsk_resident_key *srk)
363991d5a20Sdjm {
364991d5a20Sdjm if (srk == NULL)
365991d5a20Sdjm return;
366991d5a20Sdjm sshkey_free(srk->key);
367991d5a20Sdjm freezero(srk->user_id, srk->user_id_len);
368991d5a20Sdjm free(srk);
369991d5a20Sdjm }
370991d5a20Sdjm
371991d5a20Sdjm
372991d5a20Sdjm void
sshsk_free_resident_keys(struct sshsk_resident_key ** srks,size_t nsrks)373991d5a20Sdjm sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
374991d5a20Sdjm {
375991d5a20Sdjm size_t i;
376991d5a20Sdjm
377991d5a20Sdjm if (srks == NULL || nsrks == 0)
378991d5a20Sdjm return;
379991d5a20Sdjm
380991d5a20Sdjm for (i = 0; i < nsrks; i++)
381991d5a20Sdjm sshsk_free_resident_key(srks[i]);
382991d5a20Sdjm free(srks);
383991d5a20Sdjm }
384991d5a20Sdjm
3859fe3789cSdjm int
sshsk_load_resident(const char * provider_path,const char * device,const char * pin,u_int flags,struct sshsk_resident_key *** srksp,size_t * nsrksp)386a0caf565Sdjm sshsk_load_resident(const char *provider_path, const char *device,
387991d5a20Sdjm const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
388991d5a20Sdjm size_t *nsrksp)
3899fe3789cSdjm {
3909fe3789cSdjm int oerrno, r = SSH_ERR_INTERNAL_ERROR;
3919fe3789cSdjm struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
392991d5a20Sdjm struct sshkey *key = NULL;
393991d5a20Sdjm struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
394991d5a20Sdjm u_char *userid = NULL;
395991d5a20Sdjm size_t userid_len = 0, nsrks = 0;
3969fe3789cSdjm
397991d5a20Sdjm *srksp = NULL;
398991d5a20Sdjm *nsrksp = 0;
3999fe3789cSdjm
400*31c848ccSdjm if ((kbuf = sshbuf_new()) == NULL ||
4019fe3789cSdjm (req = sshbuf_new()) == NULL) {
4029fe3789cSdjm r = SSH_ERR_ALLOC_FAIL;
4039fe3789cSdjm goto out;
4049fe3789cSdjm }
4059fe3789cSdjm
406a769387cSdjm if ((r = sshbuf_put_cstring(req, provider_path)) != 0 ||
407a0caf565Sdjm (r = sshbuf_put_cstring(req, device)) != 0 ||
408991d5a20Sdjm (r = sshbuf_put_cstring(req, pin)) != 0 ||
409991d5a20Sdjm (r = sshbuf_put_u32(req, flags)) != 0) {
41048e6b99dSdjm error_fr(r, "compose");
4119fe3789cSdjm goto out;
4129fe3789cSdjm }
4139fe3789cSdjm
4142db06755Sdjm if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0)
4159fe3789cSdjm goto out;
4169fe3789cSdjm
4179fe3789cSdjm while (sshbuf_len(resp) != 0) {
418991d5a20Sdjm /* key, comment, user_id */
4199fe3789cSdjm if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
420991d5a20Sdjm (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0 ||
421991d5a20Sdjm (r = sshbuf_get_string(resp, &userid, &userid_len)) != 0) {
422991d5a20Sdjm error_fr(r, "parse");
4239fe3789cSdjm r = SSH_ERR_INVALID_FORMAT;
4249fe3789cSdjm goto out;
4259fe3789cSdjm }
4269fe3789cSdjm if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
42748e6b99dSdjm error_fr(r, "decode key");
4289fe3789cSdjm goto out;
4299fe3789cSdjm }
430991d5a20Sdjm if ((srk = calloc(1, sizeof(*srk))) == NULL) {
431991d5a20Sdjm error_f("calloc failed");
432991d5a20Sdjm goto out;
433991d5a20Sdjm }
434991d5a20Sdjm srk->key = key;
435991d5a20Sdjm key = NULL;
436991d5a20Sdjm srk->user_id = userid;
437991d5a20Sdjm srk->user_id_len = userid_len;
438991d5a20Sdjm userid = NULL;
439991d5a20Sdjm userid_len = 0;
440991d5a20Sdjm if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
441991d5a20Sdjm sizeof(*srks))) == NULL) {
44248e6b99dSdjm error_f("recallocarray keys failed");
4439fe3789cSdjm goto out;
4449fe3789cSdjm }
445991d5a20Sdjm debug_f("srks[%zu]: %s %s uidlen %zu", nsrks,
446991d5a20Sdjm sshkey_type(srk->key), srk->key->sk_application,
447991d5a20Sdjm srk->user_id_len);
448991d5a20Sdjm srks = tmp;
449991d5a20Sdjm srks[nsrks++] = srk;
450991d5a20Sdjm srk = NULL;
4519fe3789cSdjm }
4529fe3789cSdjm
4539fe3789cSdjm /* success */
4549fe3789cSdjm r = 0;
455991d5a20Sdjm *srksp = srks;
456991d5a20Sdjm *nsrksp = nsrks;
457991d5a20Sdjm srks = NULL;
458991d5a20Sdjm nsrks = 0;
4599fe3789cSdjm out:
4609fe3789cSdjm oerrno = errno;
461991d5a20Sdjm sshsk_free_resident_key(srk);
462991d5a20Sdjm sshsk_free_resident_keys(srks, nsrks);
463991d5a20Sdjm freezero(userid, userid_len);
4649fe3789cSdjm sshkey_free(key);
4659fe3789cSdjm sshbuf_free(kbuf);
4669fe3789cSdjm sshbuf_free(req);
4679fe3789cSdjm sshbuf_free(resp);
4689fe3789cSdjm errno = oerrno;
4699fe3789cSdjm return r;
4709fe3789cSdjm }
471