1*1323ec57SEd Maste /* $OpenBSD: ssh-sk-client.c,v 1.12 2022/01/14 03:34:00 djm Exp $ */
219261079SEd Maste /*
319261079SEd Maste * Copyright (c) 2019 Google LLC
419261079SEd Maste *
519261079SEd Maste * Permission to use, copy, modify, and distribute this software for any
619261079SEd Maste * purpose with or without fee is hereby granted, provided that the above
719261079SEd Maste * copyright notice and this permission notice appear in all copies.
819261079SEd Maste *
919261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1019261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1119261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1219261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1319261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1419261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1519261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1619261079SEd Maste */
1719261079SEd Maste
1819261079SEd Maste #include "includes.h"
1919261079SEd Maste
2019261079SEd Maste #include <sys/types.h>
2119261079SEd Maste #include <sys/socket.h>
2219261079SEd Maste #include <sys/wait.h>
2319261079SEd Maste
2419261079SEd Maste #include <fcntl.h>
2519261079SEd Maste #include <limits.h>
2619261079SEd Maste #include <errno.h>
2719261079SEd Maste #include <signal.h>
2819261079SEd Maste #include <stdarg.h>
2919261079SEd Maste #include <stdio.h>
3019261079SEd Maste #include <stdlib.h>
3119261079SEd Maste #include <string.h>
3219261079SEd Maste #include <unistd.h>
3319261079SEd Maste
3419261079SEd Maste #include "log.h"
3519261079SEd Maste #include "ssherr.h"
3619261079SEd Maste #include "sshbuf.h"
3719261079SEd Maste #include "sshkey.h"
3819261079SEd Maste #include "msg.h"
3919261079SEd Maste #include "digest.h"
4019261079SEd Maste #include "pathnames.h"
4119261079SEd Maste #include "ssh-sk.h"
4219261079SEd Maste #include "misc.h"
4319261079SEd Maste
4419261079SEd Maste /* #define DEBUG_SK 1 */
4519261079SEd Maste
4619261079SEd Maste static int
start_helper(int * fdp,pid_t * pidp,void (** osigchldp)(int))4719261079SEd Maste start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
4819261079SEd Maste {
4919261079SEd Maste void (*osigchld)(int);
5019261079SEd Maste int oerrno, pair[2];
5119261079SEd Maste pid_t pid;
5219261079SEd Maste char *helper, *verbosity = NULL;
5319261079SEd Maste
5419261079SEd Maste *fdp = -1;
5519261079SEd Maste *pidp = 0;
5619261079SEd Maste *osigchldp = SIG_DFL;
5719261079SEd Maste
5819261079SEd Maste helper = getenv("SSH_SK_HELPER");
5919261079SEd Maste if (helper == NULL || strlen(helper) == 0)
6019261079SEd Maste helper = _PATH_SSH_SK_HELPER;
6119261079SEd Maste if (access(helper, X_OK) != 0) {
6219261079SEd Maste oerrno = errno;
6319261079SEd Maste error_f("helper \"%s\" unusable: %s", helper, strerror(errno));
6419261079SEd Maste errno = oerrno;
6519261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
6619261079SEd Maste }
6719261079SEd Maste #ifdef DEBUG_SK
6819261079SEd Maste verbosity = "-vvv";
6919261079SEd Maste #endif
7019261079SEd Maste
7119261079SEd Maste /* Start helper */
7219261079SEd Maste if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
7319261079SEd Maste error("socketpair: %s", strerror(errno));
7419261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
7519261079SEd Maste }
7619261079SEd Maste osigchld = ssh_signal(SIGCHLD, SIG_DFL);
7719261079SEd Maste if ((pid = fork()) == -1) {
7819261079SEd Maste oerrno = errno;
7919261079SEd Maste error("fork: %s", strerror(errno));
8019261079SEd Maste close(pair[0]);
8119261079SEd Maste close(pair[1]);
8219261079SEd Maste ssh_signal(SIGCHLD, osigchld);
8319261079SEd Maste errno = oerrno;
8419261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
8519261079SEd Maste }
8619261079SEd Maste if (pid == 0) {
8719261079SEd Maste if ((dup2(pair[1], STDIN_FILENO) == -1) ||
8819261079SEd Maste (dup2(pair[1], STDOUT_FILENO) == -1)) {
8919261079SEd Maste error_f("dup2: %s", strerror(errno));
9019261079SEd Maste _exit(1);
9119261079SEd Maste }
9219261079SEd Maste close(pair[0]);
9319261079SEd Maste close(pair[1]);
9419261079SEd Maste closefrom(STDERR_FILENO + 1);
9519261079SEd Maste debug_f("starting %s %s", helper,
9619261079SEd Maste verbosity == NULL ? "" : verbosity);
9719261079SEd Maste execlp(helper, helper, verbosity, (char *)NULL);
9819261079SEd Maste error_f("execlp: %s", strerror(errno));
9919261079SEd Maste _exit(1);
10019261079SEd Maste }
10119261079SEd Maste close(pair[1]);
10219261079SEd Maste
10319261079SEd Maste /* success */
10419261079SEd Maste debug3_f("started pid=%ld", (long)pid);
10519261079SEd Maste *fdp = pair[0];
10619261079SEd Maste *pidp = pid;
10719261079SEd Maste *osigchldp = osigchld;
10819261079SEd Maste return 0;
10919261079SEd Maste }
11019261079SEd Maste
11119261079SEd Maste static int
reap_helper(pid_t pid)11219261079SEd Maste reap_helper(pid_t pid)
11319261079SEd Maste {
11419261079SEd Maste int status, oerrno;
11519261079SEd Maste
11619261079SEd Maste debug3_f("pid=%ld", (long)pid);
11719261079SEd Maste
11819261079SEd Maste errno = 0;
11919261079SEd Maste while (waitpid(pid, &status, 0) == -1) {
12019261079SEd Maste if (errno == EINTR) {
12119261079SEd Maste errno = 0;
12219261079SEd Maste continue;
12319261079SEd Maste }
12419261079SEd Maste oerrno = errno;
12519261079SEd Maste error_f("waitpid: %s", strerror(errno));
12619261079SEd Maste errno = oerrno;
12719261079SEd Maste return SSH_ERR_SYSTEM_ERROR;
12819261079SEd Maste }
12919261079SEd Maste if (!WIFEXITED(status)) {
13019261079SEd Maste error_f("helper exited abnormally");
13119261079SEd Maste return SSH_ERR_AGENT_FAILURE;
13219261079SEd Maste } else if (WEXITSTATUS(status) != 0) {
13319261079SEd Maste error_f("helper exited with non-zero exit status");
13419261079SEd Maste return SSH_ERR_AGENT_FAILURE;
13519261079SEd Maste }
13619261079SEd Maste return 0;
13719261079SEd Maste }
13819261079SEd Maste
13919261079SEd Maste static int
client_converse(struct sshbuf * msg,struct sshbuf ** respp,u_int type)14019261079SEd Maste client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type)
14119261079SEd Maste {
14219261079SEd Maste int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR;
14319261079SEd Maste u_int rtype, rerr;
14419261079SEd Maste pid_t pid;
14519261079SEd Maste u_char version;
14619261079SEd Maste void (*osigchld)(int);
14719261079SEd Maste struct sshbuf *req = NULL, *resp = NULL;
14819261079SEd Maste *respp = NULL;
14919261079SEd Maste
15019261079SEd Maste if ((r = start_helper(&fd, &pid, &osigchld)) != 0)
15119261079SEd Maste return r;
15219261079SEd Maste
15319261079SEd Maste if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) {
15419261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
15519261079SEd Maste goto out;
15619261079SEd Maste }
15719261079SEd Maste /* Request preamble: type, log_on_stderr, log_level */
15819261079SEd Maste ll = log_level_get();
15919261079SEd Maste if ((r = sshbuf_put_u32(req, type)) != 0 ||
16019261079SEd Maste (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 ||
16119261079SEd Maste (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 ||
16219261079SEd Maste (r = sshbuf_putb(req, msg)) != 0) {
16319261079SEd Maste error_fr(r, "compose");
16419261079SEd Maste goto out;
16519261079SEd Maste }
16619261079SEd Maste if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) {
16719261079SEd Maste error_fr(r, "send");
16819261079SEd Maste goto out;
16919261079SEd Maste }
17019261079SEd Maste if ((r = ssh_msg_recv(fd, resp)) != 0) {
17119261079SEd Maste error_fr(r, "receive");
17219261079SEd Maste goto out;
17319261079SEd Maste }
17419261079SEd Maste if ((r = sshbuf_get_u8(resp, &version)) != 0) {
17519261079SEd Maste error_fr(r, "parse version");
17619261079SEd Maste goto out;
17719261079SEd Maste }
17819261079SEd Maste if (version != SSH_SK_HELPER_VERSION) {
17919261079SEd Maste error_f("unsupported version: got %u, expected %u",
18019261079SEd Maste version, SSH_SK_HELPER_VERSION);
18119261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
18219261079SEd Maste goto out;
18319261079SEd Maste }
18419261079SEd Maste if ((r = sshbuf_get_u32(resp, &rtype)) != 0) {
18519261079SEd Maste error_fr(r, "parse message type");
18619261079SEd Maste goto out;
18719261079SEd Maste }
18819261079SEd Maste if (rtype == SSH_SK_HELPER_ERROR) {
18919261079SEd Maste if ((r = sshbuf_get_u32(resp, &rerr)) != 0) {
19019261079SEd Maste error_fr(r, "parse");
19119261079SEd Maste goto out;
19219261079SEd Maste }
19319261079SEd Maste debug_f("helper returned error -%u", rerr);
19419261079SEd Maste /* OpenSSH error values are negative; encoded as -err on wire */
19519261079SEd Maste if (rerr == 0 || rerr >= INT_MAX)
19619261079SEd Maste r = SSH_ERR_INTERNAL_ERROR;
19719261079SEd Maste else
19819261079SEd Maste r = -(int)rerr;
19919261079SEd Maste goto out;
20019261079SEd Maste } else if (rtype != type) {
20119261079SEd Maste error_f("helper returned incorrect message type %u, "
20219261079SEd Maste "expecting %u", rtype, type);
20319261079SEd Maste r = SSH_ERR_INTERNAL_ERROR;
20419261079SEd Maste goto out;
20519261079SEd Maste }
20619261079SEd Maste /* success */
20719261079SEd Maste r = 0;
20819261079SEd Maste out:
20919261079SEd Maste oerrno = errno;
21019261079SEd Maste close(fd);
21119261079SEd Maste if ((r2 = reap_helper(pid)) != 0) {
21219261079SEd Maste if (r == 0) {
21319261079SEd Maste r = r2;
21419261079SEd Maste oerrno = errno;
21519261079SEd Maste }
21619261079SEd Maste }
21719261079SEd Maste if (r == 0) {
21819261079SEd Maste *respp = resp;
21919261079SEd Maste resp = NULL;
22019261079SEd Maste }
22119261079SEd Maste sshbuf_free(req);
22219261079SEd Maste sshbuf_free(resp);
22319261079SEd Maste ssh_signal(SIGCHLD, osigchld);
22419261079SEd Maste errno = oerrno;
22519261079SEd Maste return r;
22619261079SEd Maste
22719261079SEd Maste }
22819261079SEd Maste
22919261079SEd Maste 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)23019261079SEd Maste sshsk_sign(const char *provider, struct sshkey *key,
23119261079SEd Maste u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
23219261079SEd Maste u_int compat, const char *pin)
23319261079SEd Maste {
23419261079SEd Maste int oerrno, r = SSH_ERR_INTERNAL_ERROR;
23519261079SEd Maste struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
23619261079SEd Maste
23719261079SEd Maste *sigp = NULL;
23819261079SEd Maste *lenp = 0;
23919261079SEd Maste
24019261079SEd Maste #ifndef ENABLE_SK
24119261079SEd Maste return SSH_ERR_KEY_TYPE_UNKNOWN;
24219261079SEd Maste #endif
24319261079SEd Maste
24419261079SEd Maste if ((kbuf = sshbuf_new()) == NULL ||
24519261079SEd Maste (req = sshbuf_new()) == NULL) {
24619261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
24719261079SEd Maste goto out;
24819261079SEd Maste }
24919261079SEd Maste
25019261079SEd Maste if ((r = sshkey_private_serialize(key, kbuf)) != 0) {
25119261079SEd Maste error_fr(r, "encode key");
25219261079SEd Maste goto out;
25319261079SEd Maste }
25419261079SEd Maste if ((r = sshbuf_put_stringb(req, kbuf)) != 0 ||
25519261079SEd Maste (r = sshbuf_put_cstring(req, provider)) != 0 ||
25619261079SEd Maste (r = sshbuf_put_string(req, data, datalen)) != 0 ||
25719261079SEd Maste (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */
25819261079SEd Maste (r = sshbuf_put_u32(req, compat)) != 0 ||
25919261079SEd Maste (r = sshbuf_put_cstring(req, pin)) != 0) {
26019261079SEd Maste error_fr(r, "compose");
26119261079SEd Maste goto out;
26219261079SEd Maste }
26319261079SEd Maste
26419261079SEd Maste if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0)
26519261079SEd Maste goto out;
26619261079SEd Maste
26719261079SEd Maste if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
26819261079SEd Maste error_fr(r, "parse signature");
26919261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
27019261079SEd Maste goto out;
27119261079SEd Maste }
27219261079SEd Maste if (sshbuf_len(resp) != 0) {
27319261079SEd Maste error_f("trailing data in response");
27419261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
27519261079SEd Maste goto out;
27619261079SEd Maste }
27719261079SEd Maste /* success */
27819261079SEd Maste r = 0;
27919261079SEd Maste out:
28019261079SEd Maste oerrno = errno;
28119261079SEd Maste if (r != 0) {
28219261079SEd Maste freezero(*sigp, *lenp);
28319261079SEd Maste *sigp = NULL;
28419261079SEd Maste *lenp = 0;
28519261079SEd Maste }
28619261079SEd Maste sshbuf_free(kbuf);
28719261079SEd Maste sshbuf_free(req);
28819261079SEd Maste sshbuf_free(resp);
28919261079SEd Maste errno = oerrno;
29019261079SEd Maste return r;
29119261079SEd Maste }
29219261079SEd Maste
29319261079SEd Maste 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)29419261079SEd Maste sshsk_enroll(int type, const char *provider_path, const char *device,
29519261079SEd Maste const char *application, const char *userid, uint8_t flags,
29619261079SEd Maste const char *pin, struct sshbuf *challenge_buf,
29719261079SEd Maste struct sshkey **keyp, struct sshbuf *attest)
29819261079SEd Maste {
29919261079SEd Maste int oerrno, r = SSH_ERR_INTERNAL_ERROR;
30019261079SEd Maste struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL;
30119261079SEd Maste struct sshkey *key = NULL;
30219261079SEd Maste
30319261079SEd Maste *keyp = NULL;
30419261079SEd Maste if (attest != NULL)
30519261079SEd Maste sshbuf_reset(attest);
30619261079SEd Maste
30719261079SEd Maste #ifndef ENABLE_SK
30819261079SEd Maste return SSH_ERR_KEY_TYPE_UNKNOWN;
30919261079SEd Maste #endif
31019261079SEd Maste
31119261079SEd Maste if (type < 0)
31219261079SEd Maste return SSH_ERR_INVALID_ARGUMENT;
31319261079SEd Maste
31419261079SEd Maste if ((abuf = sshbuf_new()) == NULL ||
31519261079SEd Maste (kbuf = sshbuf_new()) == NULL ||
31619261079SEd Maste (req = sshbuf_new()) == NULL) {
31719261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
31819261079SEd Maste goto out;
31919261079SEd Maste }
32019261079SEd Maste
32119261079SEd Maste if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 ||
32219261079SEd Maste (r = sshbuf_put_cstring(req, provider_path)) != 0 ||
32319261079SEd Maste (r = sshbuf_put_cstring(req, device)) != 0 ||
32419261079SEd Maste (r = sshbuf_put_cstring(req, application)) != 0 ||
32519261079SEd Maste (r = sshbuf_put_cstring(req, userid)) != 0 ||
32619261079SEd Maste (r = sshbuf_put_u8(req, flags)) != 0 ||
32719261079SEd Maste (r = sshbuf_put_cstring(req, pin)) != 0 ||
32819261079SEd Maste (r = sshbuf_put_stringb(req, challenge_buf)) != 0) {
32919261079SEd Maste error_fr(r, "compose");
33019261079SEd Maste goto out;
33119261079SEd Maste }
33219261079SEd Maste
33319261079SEd Maste if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0)
33419261079SEd Maste goto out;
33519261079SEd Maste
33619261079SEd Maste if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
33719261079SEd Maste (r = sshbuf_get_stringb(resp, abuf)) != 0) {
33819261079SEd Maste error_fr(r, "parse");
33919261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
34019261079SEd Maste goto out;
34119261079SEd Maste }
34219261079SEd Maste if (sshbuf_len(resp) != 0) {
34319261079SEd Maste error_f("trailing data in response");
34419261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
34519261079SEd Maste goto out;
34619261079SEd Maste }
34719261079SEd Maste if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
34819261079SEd Maste error_fr(r, "encode");
34919261079SEd Maste goto out;
35019261079SEd Maste }
35119261079SEd Maste if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) {
35219261079SEd Maste error_fr(r, "encode attestation information");
35319261079SEd Maste goto out;
35419261079SEd Maste }
35519261079SEd Maste
35619261079SEd Maste /* success */
35719261079SEd Maste r = 0;
35819261079SEd Maste *keyp = key;
35919261079SEd Maste key = NULL;
36019261079SEd Maste out:
36119261079SEd Maste oerrno = errno;
36219261079SEd Maste sshkey_free(key);
36319261079SEd Maste sshbuf_free(kbuf);
36419261079SEd Maste sshbuf_free(abuf);
36519261079SEd Maste sshbuf_free(req);
36619261079SEd Maste sshbuf_free(resp);
36719261079SEd Maste errno = oerrno;
36819261079SEd Maste return r;
36919261079SEd Maste }
37019261079SEd Maste
371*1323ec57SEd Maste static void
sshsk_free_resident_key(struct sshsk_resident_key * srk)372*1323ec57SEd Maste sshsk_free_resident_key(struct sshsk_resident_key *srk)
373*1323ec57SEd Maste {
374*1323ec57SEd Maste if (srk == NULL)
375*1323ec57SEd Maste return;
376*1323ec57SEd Maste sshkey_free(srk->key);
377*1323ec57SEd Maste freezero(srk->user_id, srk->user_id_len);
378*1323ec57SEd Maste free(srk);
379*1323ec57SEd Maste }
380*1323ec57SEd Maste
381*1323ec57SEd Maste
382*1323ec57SEd Maste void
sshsk_free_resident_keys(struct sshsk_resident_key ** srks,size_t nsrks)383*1323ec57SEd Maste sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
384*1323ec57SEd Maste {
385*1323ec57SEd Maste size_t i;
386*1323ec57SEd Maste
387*1323ec57SEd Maste if (srks == NULL || nsrks == 0)
388*1323ec57SEd Maste return;
389*1323ec57SEd Maste
390*1323ec57SEd Maste for (i = 0; i < nsrks; i++)
391*1323ec57SEd Maste sshsk_free_resident_key(srks[i]);
392*1323ec57SEd Maste free(srks);
393*1323ec57SEd Maste }
394*1323ec57SEd Maste
39519261079SEd Maste 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)39619261079SEd Maste sshsk_load_resident(const char *provider_path, const char *device,
397*1323ec57SEd Maste const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
398*1323ec57SEd Maste size_t *nsrksp)
39919261079SEd Maste {
40019261079SEd Maste int oerrno, r = SSH_ERR_INTERNAL_ERROR;
40119261079SEd Maste struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
402*1323ec57SEd Maste struct sshkey *key = NULL;
403*1323ec57SEd Maste struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
404*1323ec57SEd Maste u_char *userid = NULL;
405*1323ec57SEd Maste size_t userid_len = 0, nsrks = 0;
40619261079SEd Maste
407*1323ec57SEd Maste *srksp = NULL;
408*1323ec57SEd Maste *nsrksp = 0;
40919261079SEd Maste
410*1323ec57SEd Maste if ((kbuf = sshbuf_new()) == NULL ||
41119261079SEd Maste (req = sshbuf_new()) == NULL) {
41219261079SEd Maste r = SSH_ERR_ALLOC_FAIL;
41319261079SEd Maste goto out;
41419261079SEd Maste }
41519261079SEd Maste
41619261079SEd Maste if ((r = sshbuf_put_cstring(req, provider_path)) != 0 ||
41719261079SEd Maste (r = sshbuf_put_cstring(req, device)) != 0 ||
418*1323ec57SEd Maste (r = sshbuf_put_cstring(req, pin)) != 0 ||
419*1323ec57SEd Maste (r = sshbuf_put_u32(req, flags)) != 0) {
42019261079SEd Maste error_fr(r, "compose");
42119261079SEd Maste goto out;
42219261079SEd Maste }
42319261079SEd Maste
42419261079SEd Maste if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0)
42519261079SEd Maste goto out;
42619261079SEd Maste
42719261079SEd Maste while (sshbuf_len(resp) != 0) {
428*1323ec57SEd Maste /* key, comment, user_id */
42919261079SEd Maste if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
430*1323ec57SEd Maste (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0 ||
431*1323ec57SEd Maste (r = sshbuf_get_string(resp, &userid, &userid_len)) != 0) {
432*1323ec57SEd Maste error_fr(r, "parse");
43319261079SEd Maste r = SSH_ERR_INVALID_FORMAT;
43419261079SEd Maste goto out;
43519261079SEd Maste }
43619261079SEd Maste if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
43719261079SEd Maste error_fr(r, "decode key");
43819261079SEd Maste goto out;
43919261079SEd Maste }
440*1323ec57SEd Maste if ((srk = calloc(1, sizeof(*srk))) == NULL) {
441*1323ec57SEd Maste error_f("calloc failed");
442*1323ec57SEd Maste goto out;
443*1323ec57SEd Maste }
444*1323ec57SEd Maste srk->key = key;
445*1323ec57SEd Maste key = NULL;
446*1323ec57SEd Maste srk->user_id = userid;
447*1323ec57SEd Maste srk->user_id_len = userid_len;
448*1323ec57SEd Maste userid = NULL;
449*1323ec57SEd Maste userid_len = 0;
450*1323ec57SEd Maste if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
451*1323ec57SEd Maste sizeof(*srks))) == NULL) {
45219261079SEd Maste error_f("recallocarray keys failed");
45319261079SEd Maste goto out;
45419261079SEd Maste }
455*1323ec57SEd Maste debug_f("srks[%zu]: %s %s uidlen %zu", nsrks,
456*1323ec57SEd Maste sshkey_type(srk->key), srk->key->sk_application,
457*1323ec57SEd Maste srk->user_id_len);
458*1323ec57SEd Maste srks = tmp;
459*1323ec57SEd Maste srks[nsrks++] = srk;
460*1323ec57SEd Maste srk = NULL;
46119261079SEd Maste }
46219261079SEd Maste
46319261079SEd Maste /* success */
46419261079SEd Maste r = 0;
465*1323ec57SEd Maste *srksp = srks;
466*1323ec57SEd Maste *nsrksp = nsrks;
467*1323ec57SEd Maste srks = NULL;
468*1323ec57SEd Maste nsrks = 0;
46919261079SEd Maste out:
47019261079SEd Maste oerrno = errno;
471*1323ec57SEd Maste sshsk_free_resident_key(srk);
472*1323ec57SEd Maste sshsk_free_resident_keys(srks, nsrks);
473*1323ec57SEd Maste freezero(userid, userid_len);
47419261079SEd Maste sshkey_free(key);
47519261079SEd Maste sshbuf_free(kbuf);
47619261079SEd Maste sshbuf_free(req);
47719261079SEd Maste sshbuf_free(resp);
47819261079SEd Maste errno = oerrno;
47919261079SEd Maste return r;
48019261079SEd Maste }
481