1*ee116499SAntonio Huete Jimenez /* $OpenBSD: ssh-sk-client.c,v 1.12 2022/01/14 03:34:00 djm Exp $ */
20cbfa66cSDaniel Fojt /*
30cbfa66cSDaniel Fojt * Copyright (c) 2019 Google LLC
40cbfa66cSDaniel Fojt *
50cbfa66cSDaniel Fojt * Permission to use, copy, modify, and distribute this software for any
60cbfa66cSDaniel Fojt * purpose with or without fee is hereby granted, provided that the above
70cbfa66cSDaniel Fojt * copyright notice and this permission notice appear in all copies.
80cbfa66cSDaniel Fojt *
90cbfa66cSDaniel Fojt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
100cbfa66cSDaniel Fojt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
110cbfa66cSDaniel Fojt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
120cbfa66cSDaniel Fojt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
130cbfa66cSDaniel Fojt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
140cbfa66cSDaniel Fojt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
150cbfa66cSDaniel Fojt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
160cbfa66cSDaniel Fojt */
170cbfa66cSDaniel Fojt
180cbfa66cSDaniel Fojt #include "includes.h"
190cbfa66cSDaniel Fojt
200cbfa66cSDaniel Fojt #include <sys/types.h>
210cbfa66cSDaniel Fojt #include <sys/socket.h>
220cbfa66cSDaniel Fojt #include <sys/wait.h>
230cbfa66cSDaniel Fojt
240cbfa66cSDaniel Fojt #include <fcntl.h>
250cbfa66cSDaniel Fojt #include <limits.h>
260cbfa66cSDaniel Fojt #include <errno.h>
270cbfa66cSDaniel Fojt #include <signal.h>
280cbfa66cSDaniel Fojt #include <stdarg.h>
290cbfa66cSDaniel Fojt #include <stdio.h>
300cbfa66cSDaniel Fojt #include <stdlib.h>
310cbfa66cSDaniel Fojt #include <string.h>
320cbfa66cSDaniel Fojt #include <unistd.h>
330cbfa66cSDaniel Fojt
340cbfa66cSDaniel Fojt #include "log.h"
350cbfa66cSDaniel Fojt #include "ssherr.h"
360cbfa66cSDaniel Fojt #include "sshbuf.h"
370cbfa66cSDaniel Fojt #include "sshkey.h"
380cbfa66cSDaniel Fojt #include "msg.h"
390cbfa66cSDaniel Fojt #include "digest.h"
400cbfa66cSDaniel Fojt #include "pathnames.h"
410cbfa66cSDaniel Fojt #include "ssh-sk.h"
420cbfa66cSDaniel Fojt #include "misc.h"
430cbfa66cSDaniel Fojt
440cbfa66cSDaniel Fojt /* #define DEBUG_SK 1 */
450cbfa66cSDaniel Fojt
460cbfa66cSDaniel Fojt static int
start_helper(int * fdp,pid_t * pidp,void (** osigchldp)(int))470cbfa66cSDaniel Fojt start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
480cbfa66cSDaniel Fojt {
490cbfa66cSDaniel Fojt void (*osigchld)(int);
5050a69bb5SSascha Wildner int oerrno, pair[2];
510cbfa66cSDaniel Fojt pid_t pid;
520cbfa66cSDaniel Fojt char *helper, *verbosity = NULL;
530cbfa66cSDaniel Fojt
540cbfa66cSDaniel Fojt *fdp = -1;
550cbfa66cSDaniel Fojt *pidp = 0;
560cbfa66cSDaniel Fojt *osigchldp = SIG_DFL;
570cbfa66cSDaniel Fojt
580cbfa66cSDaniel Fojt helper = getenv("SSH_SK_HELPER");
590cbfa66cSDaniel Fojt if (helper == NULL || strlen(helper) == 0)
600cbfa66cSDaniel Fojt helper = _PATH_SSH_SK_HELPER;
610cbfa66cSDaniel Fojt if (access(helper, X_OK) != 0) {
620cbfa66cSDaniel Fojt oerrno = errno;
6350a69bb5SSascha Wildner error_f("helper \"%s\" unusable: %s", helper, strerror(errno));
640cbfa66cSDaniel Fojt errno = oerrno;
650cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR;
660cbfa66cSDaniel Fojt }
670cbfa66cSDaniel Fojt #ifdef DEBUG_SK
680cbfa66cSDaniel Fojt verbosity = "-vvv";
690cbfa66cSDaniel Fojt #endif
700cbfa66cSDaniel Fojt
710cbfa66cSDaniel Fojt /* Start helper */
720cbfa66cSDaniel Fojt if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
730cbfa66cSDaniel Fojt error("socketpair: %s", strerror(errno));
740cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR;
750cbfa66cSDaniel Fojt }
760cbfa66cSDaniel Fojt osigchld = ssh_signal(SIGCHLD, SIG_DFL);
770cbfa66cSDaniel Fojt if ((pid = fork()) == -1) {
780cbfa66cSDaniel Fojt oerrno = errno;
790cbfa66cSDaniel Fojt error("fork: %s", strerror(errno));
800cbfa66cSDaniel Fojt close(pair[0]);
810cbfa66cSDaniel Fojt close(pair[1]);
820cbfa66cSDaniel Fojt ssh_signal(SIGCHLD, osigchld);
830cbfa66cSDaniel Fojt errno = oerrno;
840cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR;
850cbfa66cSDaniel Fojt }
860cbfa66cSDaniel Fojt if (pid == 0) {
870cbfa66cSDaniel Fojt if ((dup2(pair[1], STDIN_FILENO) == -1) ||
880cbfa66cSDaniel Fojt (dup2(pair[1], STDOUT_FILENO) == -1)) {
8950a69bb5SSascha Wildner error_f("dup2: %s", strerror(errno));
900cbfa66cSDaniel Fojt _exit(1);
910cbfa66cSDaniel Fojt }
920cbfa66cSDaniel Fojt close(pair[0]);
930cbfa66cSDaniel Fojt close(pair[1]);
940cbfa66cSDaniel Fojt closefrom(STDERR_FILENO + 1);
9550a69bb5SSascha Wildner debug_f("starting %s %s", helper,
960cbfa66cSDaniel Fojt verbosity == NULL ? "" : verbosity);
970cbfa66cSDaniel Fojt execlp(helper, helper, verbosity, (char *)NULL);
9850a69bb5SSascha Wildner error_f("execlp: %s", strerror(errno));
990cbfa66cSDaniel Fojt _exit(1);
1000cbfa66cSDaniel Fojt }
1010cbfa66cSDaniel Fojt close(pair[1]);
1020cbfa66cSDaniel Fojt
1030cbfa66cSDaniel Fojt /* success */
10450a69bb5SSascha Wildner debug3_f("started pid=%ld", (long)pid);
1050cbfa66cSDaniel Fojt *fdp = pair[0];
1060cbfa66cSDaniel Fojt *pidp = pid;
1070cbfa66cSDaniel Fojt *osigchldp = osigchld;
1080cbfa66cSDaniel Fojt return 0;
1090cbfa66cSDaniel Fojt }
1100cbfa66cSDaniel Fojt
1110cbfa66cSDaniel Fojt static int
reap_helper(pid_t pid)1120cbfa66cSDaniel Fojt reap_helper(pid_t pid)
1130cbfa66cSDaniel Fojt {
1140cbfa66cSDaniel Fojt int status, oerrno;
1150cbfa66cSDaniel Fojt
11650a69bb5SSascha Wildner debug3_f("pid=%ld", (long)pid);
1170cbfa66cSDaniel Fojt
1180cbfa66cSDaniel Fojt errno = 0;
1190cbfa66cSDaniel Fojt while (waitpid(pid, &status, 0) == -1) {
1200cbfa66cSDaniel Fojt if (errno == EINTR) {
1210cbfa66cSDaniel Fojt errno = 0;
1220cbfa66cSDaniel Fojt continue;
1230cbfa66cSDaniel Fojt }
1240cbfa66cSDaniel Fojt oerrno = errno;
12550a69bb5SSascha Wildner error_f("waitpid: %s", strerror(errno));
1260cbfa66cSDaniel Fojt errno = oerrno;
1270cbfa66cSDaniel Fojt return SSH_ERR_SYSTEM_ERROR;
1280cbfa66cSDaniel Fojt }
1290cbfa66cSDaniel Fojt if (!WIFEXITED(status)) {
13050a69bb5SSascha Wildner error_f("helper exited abnormally");
1310cbfa66cSDaniel Fojt return SSH_ERR_AGENT_FAILURE;
1320cbfa66cSDaniel Fojt } else if (WEXITSTATUS(status) != 0) {
13350a69bb5SSascha Wildner error_f("helper exited with non-zero exit status");
1340cbfa66cSDaniel Fojt return SSH_ERR_AGENT_FAILURE;
1350cbfa66cSDaniel Fojt }
1360cbfa66cSDaniel Fojt return 0;
1370cbfa66cSDaniel Fojt }
1380cbfa66cSDaniel Fojt
1390cbfa66cSDaniel Fojt static int
client_converse(struct sshbuf * msg,struct sshbuf ** respp,u_int type)1400cbfa66cSDaniel Fojt client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type)
1410cbfa66cSDaniel Fojt {
1420cbfa66cSDaniel Fojt int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR;
1430cbfa66cSDaniel Fojt u_int rtype, rerr;
1440cbfa66cSDaniel Fojt pid_t pid;
1450cbfa66cSDaniel Fojt u_char version;
1460cbfa66cSDaniel Fojt void (*osigchld)(int);
1470cbfa66cSDaniel Fojt struct sshbuf *req = NULL, *resp = NULL;
1480cbfa66cSDaniel Fojt *respp = NULL;
1490cbfa66cSDaniel Fojt
1500cbfa66cSDaniel Fojt if ((r = start_helper(&fd, &pid, &osigchld)) != 0)
1510cbfa66cSDaniel Fojt return r;
1520cbfa66cSDaniel Fojt
1530cbfa66cSDaniel Fojt if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) {
1540cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL;
1550cbfa66cSDaniel Fojt goto out;
1560cbfa66cSDaniel Fojt }
1570cbfa66cSDaniel Fojt /* Request preamble: type, log_on_stderr, log_level */
1580cbfa66cSDaniel Fojt ll = log_level_get();
1590cbfa66cSDaniel Fojt if ((r = sshbuf_put_u32(req, type)) != 0 ||
1600cbfa66cSDaniel Fojt (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 ||
1610cbfa66cSDaniel Fojt (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 ||
1620cbfa66cSDaniel Fojt (r = sshbuf_putb(req, msg)) != 0) {
16350a69bb5SSascha Wildner error_fr(r, "compose");
1640cbfa66cSDaniel Fojt goto out;
1650cbfa66cSDaniel Fojt }
1660cbfa66cSDaniel Fojt if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) {
16750a69bb5SSascha Wildner error_fr(r, "send");
1680cbfa66cSDaniel Fojt goto out;
1690cbfa66cSDaniel Fojt }
1700cbfa66cSDaniel Fojt if ((r = ssh_msg_recv(fd, resp)) != 0) {
17150a69bb5SSascha Wildner error_fr(r, "receive");
1720cbfa66cSDaniel Fojt goto out;
1730cbfa66cSDaniel Fojt }
1740cbfa66cSDaniel Fojt if ((r = sshbuf_get_u8(resp, &version)) != 0) {
17550a69bb5SSascha Wildner error_fr(r, "parse version");
1760cbfa66cSDaniel Fojt goto out;
1770cbfa66cSDaniel Fojt }
1780cbfa66cSDaniel Fojt if (version != SSH_SK_HELPER_VERSION) {
17950a69bb5SSascha Wildner error_f("unsupported version: got %u, expected %u",
18050a69bb5SSascha Wildner version, SSH_SK_HELPER_VERSION);
1810cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT;
1820cbfa66cSDaniel Fojt goto out;
1830cbfa66cSDaniel Fojt }
1840cbfa66cSDaniel Fojt if ((r = sshbuf_get_u32(resp, &rtype)) != 0) {
18550a69bb5SSascha Wildner error_fr(r, "parse message type");
1860cbfa66cSDaniel Fojt goto out;
1870cbfa66cSDaniel Fojt }
1880cbfa66cSDaniel Fojt if (rtype == SSH_SK_HELPER_ERROR) {
1890cbfa66cSDaniel Fojt if ((r = sshbuf_get_u32(resp, &rerr)) != 0) {
19050a69bb5SSascha Wildner error_fr(r, "parse");
1910cbfa66cSDaniel Fojt goto out;
1920cbfa66cSDaniel Fojt }
19350a69bb5SSascha Wildner debug_f("helper returned error -%u", rerr);
1940cbfa66cSDaniel Fojt /* OpenSSH error values are negative; encoded as -err on wire */
1950cbfa66cSDaniel Fojt if (rerr == 0 || rerr >= INT_MAX)
1960cbfa66cSDaniel Fojt r = SSH_ERR_INTERNAL_ERROR;
1970cbfa66cSDaniel Fojt else
1980cbfa66cSDaniel Fojt r = -(int)rerr;
1990cbfa66cSDaniel Fojt goto out;
2000cbfa66cSDaniel Fojt } else if (rtype != type) {
20150a69bb5SSascha Wildner error_f("helper returned incorrect message type %u, "
20250a69bb5SSascha Wildner "expecting %u", rtype, type);
2030cbfa66cSDaniel Fojt r = SSH_ERR_INTERNAL_ERROR;
2040cbfa66cSDaniel Fojt goto out;
2050cbfa66cSDaniel Fojt }
2060cbfa66cSDaniel Fojt /* success */
2070cbfa66cSDaniel Fojt r = 0;
2080cbfa66cSDaniel Fojt out:
2090cbfa66cSDaniel Fojt oerrno = errno;
2100cbfa66cSDaniel Fojt close(fd);
2110cbfa66cSDaniel Fojt if ((r2 = reap_helper(pid)) != 0) {
2120cbfa66cSDaniel Fojt if (r == 0) {
2130cbfa66cSDaniel Fojt r = r2;
2140cbfa66cSDaniel Fojt oerrno = errno;
2150cbfa66cSDaniel Fojt }
2160cbfa66cSDaniel Fojt }
2170cbfa66cSDaniel Fojt if (r == 0) {
2180cbfa66cSDaniel Fojt *respp = resp;
2190cbfa66cSDaniel Fojt resp = NULL;
2200cbfa66cSDaniel Fojt }
2210cbfa66cSDaniel Fojt sshbuf_free(req);
2220cbfa66cSDaniel Fojt sshbuf_free(resp);
2230cbfa66cSDaniel Fojt ssh_signal(SIGCHLD, osigchld);
2240cbfa66cSDaniel Fojt errno = oerrno;
2250cbfa66cSDaniel Fojt return r;
2260cbfa66cSDaniel Fojt
2270cbfa66cSDaniel Fojt }
2280cbfa66cSDaniel Fojt
2290cbfa66cSDaniel Fojt 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)2300cbfa66cSDaniel Fojt sshsk_sign(const char *provider, struct sshkey *key,
2310cbfa66cSDaniel Fojt u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
2320cbfa66cSDaniel Fojt u_int compat, const char *pin)
2330cbfa66cSDaniel Fojt {
2340cbfa66cSDaniel Fojt int oerrno, r = SSH_ERR_INTERNAL_ERROR;
2350cbfa66cSDaniel Fojt struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
2360cbfa66cSDaniel Fojt
2370cbfa66cSDaniel Fojt *sigp = NULL;
2380cbfa66cSDaniel Fojt *lenp = 0;
2390cbfa66cSDaniel Fojt
2400cbfa66cSDaniel Fojt #ifndef ENABLE_SK
2410cbfa66cSDaniel Fojt return SSH_ERR_KEY_TYPE_UNKNOWN;
2420cbfa66cSDaniel Fojt #endif
2430cbfa66cSDaniel Fojt
2440cbfa66cSDaniel Fojt if ((kbuf = sshbuf_new()) == NULL ||
2450cbfa66cSDaniel Fojt (req = sshbuf_new()) == NULL) {
2460cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL;
2470cbfa66cSDaniel Fojt goto out;
2480cbfa66cSDaniel Fojt }
2490cbfa66cSDaniel Fojt
2500cbfa66cSDaniel Fojt if ((r = sshkey_private_serialize(key, kbuf)) != 0) {
25150a69bb5SSascha Wildner error_fr(r, "encode key");
2520cbfa66cSDaniel Fojt goto out;
2530cbfa66cSDaniel Fojt }
2540cbfa66cSDaniel Fojt if ((r = sshbuf_put_stringb(req, kbuf)) != 0 ||
2550cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, provider)) != 0 ||
2560cbfa66cSDaniel Fojt (r = sshbuf_put_string(req, data, datalen)) != 0 ||
2570cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */
2580cbfa66cSDaniel Fojt (r = sshbuf_put_u32(req, compat)) != 0 ||
2590cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, pin)) != 0) {
26050a69bb5SSascha Wildner error_fr(r, "compose");
2610cbfa66cSDaniel Fojt goto out;
2620cbfa66cSDaniel Fojt }
2630cbfa66cSDaniel Fojt
2640cbfa66cSDaniel Fojt if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0)
2650cbfa66cSDaniel Fojt goto out;
2660cbfa66cSDaniel Fojt
2670cbfa66cSDaniel Fojt if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
26850a69bb5SSascha Wildner error_fr(r, "parse signature");
2690cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT;
2700cbfa66cSDaniel Fojt goto out;
2710cbfa66cSDaniel Fojt }
2720cbfa66cSDaniel Fojt if (sshbuf_len(resp) != 0) {
27350a69bb5SSascha Wildner error_f("trailing data in response");
2740cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT;
2750cbfa66cSDaniel Fojt goto out;
2760cbfa66cSDaniel Fojt }
2770cbfa66cSDaniel Fojt /* success */
2780cbfa66cSDaniel Fojt r = 0;
2790cbfa66cSDaniel Fojt out:
2800cbfa66cSDaniel Fojt oerrno = errno;
2810cbfa66cSDaniel Fojt if (r != 0) {
2820cbfa66cSDaniel Fojt freezero(*sigp, *lenp);
2830cbfa66cSDaniel Fojt *sigp = NULL;
2840cbfa66cSDaniel Fojt *lenp = 0;
2850cbfa66cSDaniel Fojt }
2860cbfa66cSDaniel Fojt sshbuf_free(kbuf);
2870cbfa66cSDaniel Fojt sshbuf_free(req);
2880cbfa66cSDaniel Fojt sshbuf_free(resp);
2890cbfa66cSDaniel Fojt errno = oerrno;
2900cbfa66cSDaniel Fojt return r;
2910cbfa66cSDaniel Fojt }
2920cbfa66cSDaniel Fojt
2930cbfa66cSDaniel Fojt 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)2940cbfa66cSDaniel Fojt sshsk_enroll(int type, const char *provider_path, const char *device,
2950cbfa66cSDaniel Fojt const char *application, const char *userid, uint8_t flags,
2960cbfa66cSDaniel Fojt const char *pin, struct sshbuf *challenge_buf,
2970cbfa66cSDaniel Fojt struct sshkey **keyp, struct sshbuf *attest)
2980cbfa66cSDaniel Fojt {
2990cbfa66cSDaniel Fojt int oerrno, r = SSH_ERR_INTERNAL_ERROR;
3000cbfa66cSDaniel Fojt struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL;
3010cbfa66cSDaniel Fojt struct sshkey *key = NULL;
3020cbfa66cSDaniel Fojt
3030cbfa66cSDaniel Fojt *keyp = NULL;
3040cbfa66cSDaniel Fojt if (attest != NULL)
3050cbfa66cSDaniel Fojt sshbuf_reset(attest);
3060cbfa66cSDaniel Fojt
3070cbfa66cSDaniel Fojt #ifndef ENABLE_SK
3080cbfa66cSDaniel Fojt return SSH_ERR_KEY_TYPE_UNKNOWN;
3090cbfa66cSDaniel Fojt #endif
3100cbfa66cSDaniel Fojt
3110cbfa66cSDaniel Fojt if (type < 0)
3120cbfa66cSDaniel Fojt return SSH_ERR_INVALID_ARGUMENT;
3130cbfa66cSDaniel Fojt
3140cbfa66cSDaniel Fojt if ((abuf = sshbuf_new()) == NULL ||
3150cbfa66cSDaniel Fojt (kbuf = sshbuf_new()) == NULL ||
3160cbfa66cSDaniel Fojt (req = sshbuf_new()) == NULL) {
3170cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL;
3180cbfa66cSDaniel Fojt goto out;
3190cbfa66cSDaniel Fojt }
3200cbfa66cSDaniel Fojt
3210cbfa66cSDaniel Fojt if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 ||
3220cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, provider_path)) != 0 ||
3230cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, device)) != 0 ||
3240cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, application)) != 0 ||
3250cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, userid)) != 0 ||
3260cbfa66cSDaniel Fojt (r = sshbuf_put_u8(req, flags)) != 0 ||
3270cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, pin)) != 0 ||
3280cbfa66cSDaniel Fojt (r = sshbuf_put_stringb(req, challenge_buf)) != 0) {
32950a69bb5SSascha Wildner error_fr(r, "compose");
3300cbfa66cSDaniel Fojt goto out;
3310cbfa66cSDaniel Fojt }
3320cbfa66cSDaniel Fojt
3330cbfa66cSDaniel Fojt if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0)
3340cbfa66cSDaniel Fojt goto out;
3350cbfa66cSDaniel Fojt
3360cbfa66cSDaniel Fojt if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
3370cbfa66cSDaniel Fojt (r = sshbuf_get_stringb(resp, abuf)) != 0) {
33850a69bb5SSascha Wildner error_fr(r, "parse");
3390cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT;
3400cbfa66cSDaniel Fojt goto out;
3410cbfa66cSDaniel Fojt }
3420cbfa66cSDaniel Fojt if (sshbuf_len(resp) != 0) {
34350a69bb5SSascha Wildner error_f("trailing data in response");
3440cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT;
3450cbfa66cSDaniel Fojt goto out;
3460cbfa66cSDaniel Fojt }
3470cbfa66cSDaniel Fojt if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
34850a69bb5SSascha Wildner error_fr(r, "encode");
3490cbfa66cSDaniel Fojt goto out;
3500cbfa66cSDaniel Fojt }
3510cbfa66cSDaniel Fojt if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) {
35250a69bb5SSascha Wildner error_fr(r, "encode attestation information");
3530cbfa66cSDaniel Fojt goto out;
3540cbfa66cSDaniel Fojt }
3550cbfa66cSDaniel Fojt
3560cbfa66cSDaniel Fojt /* success */
3570cbfa66cSDaniel Fojt r = 0;
3580cbfa66cSDaniel Fojt *keyp = key;
3590cbfa66cSDaniel Fojt key = NULL;
3600cbfa66cSDaniel Fojt out:
3610cbfa66cSDaniel Fojt oerrno = errno;
3620cbfa66cSDaniel Fojt sshkey_free(key);
3630cbfa66cSDaniel Fojt sshbuf_free(kbuf);
3640cbfa66cSDaniel Fojt sshbuf_free(abuf);
3650cbfa66cSDaniel Fojt sshbuf_free(req);
3660cbfa66cSDaniel Fojt sshbuf_free(resp);
3670cbfa66cSDaniel Fojt errno = oerrno;
3680cbfa66cSDaniel Fojt return r;
3690cbfa66cSDaniel Fojt }
3700cbfa66cSDaniel Fojt
371*ee116499SAntonio Huete Jimenez static void
sshsk_free_resident_key(struct sshsk_resident_key * srk)372*ee116499SAntonio Huete Jimenez sshsk_free_resident_key(struct sshsk_resident_key *srk)
373*ee116499SAntonio Huete Jimenez {
374*ee116499SAntonio Huete Jimenez if (srk == NULL)
375*ee116499SAntonio Huete Jimenez return;
376*ee116499SAntonio Huete Jimenez sshkey_free(srk->key);
377*ee116499SAntonio Huete Jimenez freezero(srk->user_id, srk->user_id_len);
378*ee116499SAntonio Huete Jimenez free(srk);
379*ee116499SAntonio Huete Jimenez }
380*ee116499SAntonio Huete Jimenez
381*ee116499SAntonio Huete Jimenez
382*ee116499SAntonio Huete Jimenez void
sshsk_free_resident_keys(struct sshsk_resident_key ** srks,size_t nsrks)383*ee116499SAntonio Huete Jimenez sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks)
384*ee116499SAntonio Huete Jimenez {
385*ee116499SAntonio Huete Jimenez size_t i;
386*ee116499SAntonio Huete Jimenez
387*ee116499SAntonio Huete Jimenez if (srks == NULL || nsrks == 0)
388*ee116499SAntonio Huete Jimenez return;
389*ee116499SAntonio Huete Jimenez
390*ee116499SAntonio Huete Jimenez for (i = 0; i < nsrks; i++)
391*ee116499SAntonio Huete Jimenez sshsk_free_resident_key(srks[i]);
392*ee116499SAntonio Huete Jimenez free(srks);
393*ee116499SAntonio Huete Jimenez }
394*ee116499SAntonio Huete Jimenez
3950cbfa66cSDaniel Fojt 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)3960cbfa66cSDaniel Fojt sshsk_load_resident(const char *provider_path, const char *device,
397*ee116499SAntonio Huete Jimenez const char *pin, u_int flags, struct sshsk_resident_key ***srksp,
398*ee116499SAntonio Huete Jimenez size_t *nsrksp)
3990cbfa66cSDaniel Fojt {
4000cbfa66cSDaniel Fojt int oerrno, r = SSH_ERR_INTERNAL_ERROR;
4010cbfa66cSDaniel Fojt struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
402*ee116499SAntonio Huete Jimenez struct sshkey *key = NULL;
403*ee116499SAntonio Huete Jimenez struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp;
404*ee116499SAntonio Huete Jimenez u_char *userid = NULL;
405*ee116499SAntonio Huete Jimenez size_t userid_len = 0, nsrks = 0;
4060cbfa66cSDaniel Fojt
407*ee116499SAntonio Huete Jimenez *srksp = NULL;
408*ee116499SAntonio Huete Jimenez *nsrksp = 0;
4090cbfa66cSDaniel Fojt
410*ee116499SAntonio Huete Jimenez if ((kbuf = sshbuf_new()) == NULL ||
4110cbfa66cSDaniel Fojt (req = sshbuf_new()) == NULL) {
4120cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL;
4130cbfa66cSDaniel Fojt goto out;
4140cbfa66cSDaniel Fojt }
4150cbfa66cSDaniel Fojt
4160cbfa66cSDaniel Fojt if ((r = sshbuf_put_cstring(req, provider_path)) != 0 ||
4170cbfa66cSDaniel Fojt (r = sshbuf_put_cstring(req, device)) != 0 ||
418*ee116499SAntonio Huete Jimenez (r = sshbuf_put_cstring(req, pin)) != 0 ||
419*ee116499SAntonio Huete Jimenez (r = sshbuf_put_u32(req, flags)) != 0) {
42050a69bb5SSascha Wildner error_fr(r, "compose");
4210cbfa66cSDaniel Fojt goto out;
4220cbfa66cSDaniel Fojt }
4230cbfa66cSDaniel Fojt
4240cbfa66cSDaniel Fojt if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0)
4250cbfa66cSDaniel Fojt goto out;
4260cbfa66cSDaniel Fojt
4270cbfa66cSDaniel Fojt while (sshbuf_len(resp) != 0) {
428*ee116499SAntonio Huete Jimenez /* key, comment, user_id */
4290cbfa66cSDaniel Fojt if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
430*ee116499SAntonio Huete Jimenez (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0 ||
431*ee116499SAntonio Huete Jimenez (r = sshbuf_get_string(resp, &userid, &userid_len)) != 0) {
432*ee116499SAntonio Huete Jimenez error_fr(r, "parse");
4330cbfa66cSDaniel Fojt r = SSH_ERR_INVALID_FORMAT;
4340cbfa66cSDaniel Fojt goto out;
4350cbfa66cSDaniel Fojt }
4360cbfa66cSDaniel Fojt if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
43750a69bb5SSascha Wildner error_fr(r, "decode key");
4380cbfa66cSDaniel Fojt goto out;
4390cbfa66cSDaniel Fojt }
440*ee116499SAntonio Huete Jimenez if ((srk = calloc(1, sizeof(*srk))) == NULL) {
441*ee116499SAntonio Huete Jimenez error_f("calloc failed");
442*ee116499SAntonio Huete Jimenez goto out;
443*ee116499SAntonio Huete Jimenez }
444*ee116499SAntonio Huete Jimenez srk->key = key;
445*ee116499SAntonio Huete Jimenez key = NULL;
446*ee116499SAntonio Huete Jimenez srk->user_id = userid;
447*ee116499SAntonio Huete Jimenez srk->user_id_len = userid_len;
448*ee116499SAntonio Huete Jimenez userid = NULL;
449*ee116499SAntonio Huete Jimenez userid_len = 0;
450*ee116499SAntonio Huete Jimenez if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
451*ee116499SAntonio Huete Jimenez sizeof(*srks))) == NULL) {
45250a69bb5SSascha Wildner error_f("recallocarray keys failed");
4530cbfa66cSDaniel Fojt goto out;
4540cbfa66cSDaniel Fojt }
455*ee116499SAntonio Huete Jimenez debug_f("srks[%zu]: %s %s uidlen %zu", nsrks,
456*ee116499SAntonio Huete Jimenez sshkey_type(srk->key), srk->key->sk_application,
457*ee116499SAntonio Huete Jimenez srk->user_id_len);
458*ee116499SAntonio Huete Jimenez srks = tmp;
459*ee116499SAntonio Huete Jimenez srks[nsrks++] = srk;
460*ee116499SAntonio Huete Jimenez srk = NULL;
4610cbfa66cSDaniel Fojt }
4620cbfa66cSDaniel Fojt
4630cbfa66cSDaniel Fojt /* success */
4640cbfa66cSDaniel Fojt r = 0;
465*ee116499SAntonio Huete Jimenez *srksp = srks;
466*ee116499SAntonio Huete Jimenez *nsrksp = nsrks;
467*ee116499SAntonio Huete Jimenez srks = NULL;
468*ee116499SAntonio Huete Jimenez nsrks = 0;
4690cbfa66cSDaniel Fojt out:
4700cbfa66cSDaniel Fojt oerrno = errno;
471*ee116499SAntonio Huete Jimenez sshsk_free_resident_key(srk);
472*ee116499SAntonio Huete Jimenez sshsk_free_resident_keys(srks, nsrks);
473*ee116499SAntonio Huete Jimenez freezero(userid, userid_len);
4740cbfa66cSDaniel Fojt sshkey_free(key);
4750cbfa66cSDaniel Fojt sshbuf_free(kbuf);
4760cbfa66cSDaniel Fojt sshbuf_free(req);
4770cbfa66cSDaniel Fojt sshbuf_free(resp);
4780cbfa66cSDaniel Fojt errno = oerrno;
4790cbfa66cSDaniel Fojt return r;
4800cbfa66cSDaniel Fojt }
481