xref: /freebsd-src/crypto/openssh/ssh-sk-client.c (revision 1323ec571215a77ddd21294f0871979d5ad6b992)
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