xref: /openssh-portable/ssh-agent.c (revision 31d8d231)
1*31d8d231Sdjm@openbsd.org /* $OpenBSD: ssh-agent.c,v 1.278 2021/04/03 06:18:41 djm Exp $ */
2d4a8b7e3SDamien Miller /*
395def098SDamien Miller  * Author: Tatu Ylonen <ylo@cs.hut.fi>
495def098SDamien Miller  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
595def098SDamien Miller  *                    All rights reserved
695def098SDamien Miller  * The authentication agent program.
7ad833b3eSDamien Miller  *
8e4340be5SDamien Miller  * As far as I am concerned, the code I have written for this software
9e4340be5SDamien Miller  * can be used freely for any purpose.  Any derived versions of this
10e4340be5SDamien Miller  * software must be clearly marked as such, and if the derived work is
11e4340be5SDamien Miller  * incompatible with the protocol description in the RFC file, it must be
12e4340be5SDamien Miller  * called by a name other than "ssh" or "Secure Shell".
13e4340be5SDamien Miller  *
1444697233SBen Lindstrom  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
15e4340be5SDamien Miller  *
16e4340be5SDamien Miller  * Redistribution and use in source and binary forms, with or without
17e4340be5SDamien Miller  * modification, are permitted provided that the following conditions
18e4340be5SDamien Miller  * are met:
19e4340be5SDamien Miller  * 1. Redistributions of source code must retain the above copyright
20e4340be5SDamien Miller  *    notice, this list of conditions and the following disclaimer.
21e4340be5SDamien Miller  * 2. Redistributions in binary form must reproduce the above copyright
22e4340be5SDamien Miller  *    notice, this list of conditions and the following disclaimer in the
23e4340be5SDamien Miller  *    documentation and/or other materials provided with the distribution.
24e4340be5SDamien Miller  *
25e4340be5SDamien Miller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26e4340be5SDamien Miller  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27e4340be5SDamien Miller  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28e4340be5SDamien Miller  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29e4340be5SDamien Miller  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30e4340be5SDamien Miller  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31e4340be5SDamien Miller  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32e4340be5SDamien Miller  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33e4340be5SDamien Miller  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34e4340be5SDamien Miller  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35d4a8b7e3SDamien Miller  */
36d4a8b7e3SDamien Miller 
37d4a8b7e3SDamien Miller #include "includes.h"
38ec52d7c0SDamien Miller 
39574c41fdSDamien Miller #include <sys/types.h>
408dbffe79SDamien Miller #include <sys/param.h>
418dbffe79SDamien Miller #include <sys/resource.h>
4242fb0689SDamien Miller #include <sys/stat.h>
43e3b60b52SDamien Miller #include <sys/socket.h>
4407da39f7Sdjm@openbsd.org #include <sys/wait.h>
459aec9194SDamien Miller #ifdef HAVE_SYS_TIME_H
469aec9194SDamien Miller # include <sys/time.h>
479aec9194SDamien Miller #endif
48574c41fdSDamien Miller #ifdef HAVE_SYS_UN_H
49574c41fdSDamien Miller # include <sys/un.h>
50574c41fdSDamien Miller #endif
51cd4223c2SDamien Miller #include "openbsd-compat/sys-queue.h"
52e3b60b52SDamien Miller 
531f0311c7SDamien Miller #ifdef WITH_OPENSSL
54e3476ed0SDamien Miller #include <openssl/evp.h>
55bfaaf960SDarren Tucker #include "openbsd-compat/openssl-compat.h"
561f0311c7SDamien Miller #endif
57e3476ed0SDamien Miller 
5839972493SDarren Tucker #include <errno.h>
5957cf6385SDamien Miller #include <fcntl.h>
602ae4f337Sderaadt@openbsd.org #include <limits.h>
6103e2003aSDamien Miller #ifdef HAVE_PATHS_H
6203e2003aSDamien Miller # include <paths.h>
6303e2003aSDamien Miller #endif
64fd0e8fa5Sdjm@openbsd.org #ifdef HAVE_POLL_H
65fd0e8fa5Sdjm@openbsd.org # include <poll.h>
66fd0e8fa5Sdjm@openbsd.org #endif
676ff3caddSDamien Miller #include <signal.h>
68ded319ccSDamien Miller #include <stdarg.h>
69a7a73ee3SDamien Miller #include <stdio.h>
70e7a1e5cfSDamien Miller #include <stdlib.h>
715598b4f1SDamien Miller #include <time.h>
72e3476ed0SDamien Miller #include <string.h>
73e6b3b610SDamien Miller #include <unistd.h>
74e97201feSDamien Miller #ifdef HAVE_UTIL_H
759173d0fbSdtucker@openbsd.org # include <util.h>
76e97201feSDamien Miller #endif
776ff3caddSDamien Miller 
78d7834353SDamien Miller #include "xmalloc.h"
79d4a8b7e3SDamien Miller #include "ssh.h"
800c111eb8Sdjm@openbsd.org #include "ssh2.h"
81139ca818Smarkus@openbsd.org #include "sshbuf.h"
82139ca818Smarkus@openbsd.org #include "sshkey.h"
83994cf142SDamien Miller #include "authfd.h"
8462cee007SDamien Miller #include "compat.h"
85226cfa03SBen Lindstrom #include "log.h"
866c71179fSDamien Miller #include "misc.h"
874a1c7aa6SDamien Miller #include "digest.h"
88139ca818Smarkus@openbsd.org #include "ssherr.h"
89786d5994Sdjm@openbsd.org #include "match.h"
9007da39f7Sdjm@openbsd.org #include "msg.h"
9145ffa369Sdjm@openbsd.org #include "ssherr.h"
9207da39f7Sdjm@openbsd.org #include "pathnames.h"
937ea845e4SDamien Miller #include "ssh-pkcs11.h"
94b52ec0baSdjm@openbsd.org #include "sk-api.h"
953f471630SBen Lindstrom 
966d755706Sdjm@openbsd.org #ifndef DEFAULT_ALLOWED_PROVIDERS
976d755706Sdjm@openbsd.org # define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*"
98786d5994Sdjm@openbsd.org #endif
99786d5994Sdjm@openbsd.org 
100fd0e8fa5Sdjm@openbsd.org /* Maximum accepted message length */
101fd0e8fa5Sdjm@openbsd.org #define AGENT_MAX_LEN	(256*1024)
102d691588bSdjm@openbsd.org /* Maximum bytes to read from client socket */
103d691588bSdjm@openbsd.org #define AGENT_RBUF_LEN	(4096)
104fd0e8fa5Sdjm@openbsd.org 
10565366a8cSBen Lindstrom typedef enum {
1061a4b9275Sdjm@openbsd.org 	AUTH_UNUSED = 0,
1071a4b9275Sdjm@openbsd.org 	AUTH_SOCKET = 1,
1081a4b9275Sdjm@openbsd.org 	AUTH_CONNECTION = 2,
10965366a8cSBen Lindstrom } sock_type;
11065366a8cSBen Lindstrom 
1118afaa7d7Sdjm@openbsd.org typedef struct socket_entry {
112d4a8b7e3SDamien Miller 	int fd;
11365366a8cSBen Lindstrom 	sock_type type;
114139ca818Smarkus@openbsd.org 	struct sshbuf *input;
115139ca818Smarkus@openbsd.org 	struct sshbuf *output;
116139ca818Smarkus@openbsd.org 	struct sshbuf *request;
117d4a8b7e3SDamien Miller } SocketEntry;
118d4a8b7e3SDamien Miller 
11946c16220SBen Lindstrom u_int sockets_alloc = 0;
120d4a8b7e3SDamien Miller SocketEntry *sockets = NULL;
121d4a8b7e3SDamien Miller 
1221a534ae9SDamien Miller typedef struct identity {
1231a534ae9SDamien Miller 	TAILQ_ENTRY(identity) next;
124139ca818Smarkus@openbsd.org 	struct sshkey *key;
125d4a8b7e3SDamien Miller 	char *comment;
1267ea845e4SDamien Miller 	char *provider;
12755119253SDarren Tucker 	time_t death;
1286c71179fSDamien Miller 	u_int confirm;
12907da39f7Sdjm@openbsd.org 	char *sk_provider;
130d4a8b7e3SDamien Miller } Identity;
131d4a8b7e3SDamien Miller 
132f4a6a88dSdjm@openbsd.org struct idtable {
133ad833b3eSDamien Miller 	int nentries;
1341a534ae9SDamien Miller 	TAILQ_HEAD(idqueue, identity) idlist;
135f4a6a88dSdjm@openbsd.org };
136ad833b3eSDamien Miller 
137f4a6a88dSdjm@openbsd.org /* private key table */
138f4a6a88dSdjm@openbsd.org struct idtable *idtab;
139d4a8b7e3SDamien Miller 
140d4a8b7e3SDamien Miller int max_fd = 0;
141d4a8b7e3SDamien Miller 
142d4a8b7e3SDamien Miller /* pid of shell == parent of agent */
143166fca88SDamien Miller pid_t parent_pid = -1;
144073f795bSDarren Tucker time_t parent_alive_interval = 0;
145d4a8b7e3SDamien Miller 
146b1e967c8SDamien Miller /* pid of process for which cleanup_socket is applicable */
147b1e967c8SDamien Miller pid_t cleanup_pid = 0;
148b1e967c8SDamien Miller 
149d4a8b7e3SDamien Miller /* pathname and directory for AUTH_SOCKET */
1502ae4f337Sderaadt@openbsd.org char socket_name[PATH_MAX];
1512ae4f337Sderaadt@openbsd.org char socket_dir[PATH_MAX];
152d4a8b7e3SDamien Miller 
153fc270bafSdjm@openbsd.org /* Pattern-list of allowed PKCS#11/Security key paths */
154fc270bafSdjm@openbsd.org static char *allowed_providers;
155786d5994Sdjm@openbsd.org 
1562f71704bSBen Lindstrom /* locking */
1579173d0fbSdtucker@openbsd.org #define LOCK_SIZE	32
1589173d0fbSdtucker@openbsd.org #define LOCK_SALT_SIZE	16
1599173d0fbSdtucker@openbsd.org #define LOCK_ROUNDS	1
1602f71704bSBen Lindstrom int locked = 0;
1611a31d02bSdjm@openbsd.org u_char lock_pwhash[LOCK_SIZE];
1621a31d02bSdjm@openbsd.org u_char lock_salt[LOCK_SALT_SIZE];
1632f71704bSBen Lindstrom 
16495def098SDamien Miller extern char *__progname;
16595def098SDamien Miller 
16655119253SDarren Tucker /* Default lifetime in seconds (0 == forever) */
1676d30673fSdtucker@openbsd.org static int lifetime = 0;
16853d81483SDamien Miller 
16956d1c83cSdjm@openbsd.org static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
17056d1c83cSdjm@openbsd.org 
1710c111eb8Sdjm@openbsd.org /* Refuse signing of non-SSH messages for web-origin FIDO keys */
1720c111eb8Sdjm@openbsd.org static int restrict_websafe = 1;
1730c111eb8Sdjm@openbsd.org 
174bba81213SBen Lindstrom static void
close_socket(SocketEntry * e)17558f3486cSDamien Miller close_socket(SocketEntry *e)
17658f3486cSDamien Miller {
17758f3486cSDamien Miller 	close(e->fd);
178139ca818Smarkus@openbsd.org 	sshbuf_free(e->input);
179139ca818Smarkus@openbsd.org 	sshbuf_free(e->output);
180139ca818Smarkus@openbsd.org 	sshbuf_free(e->request);
1811fe16fd6Sdjm@openbsd.org 	memset(e, '\0', sizeof(*e));
1821fe16fd6Sdjm@openbsd.org 	e->fd = -1;
1831fe16fd6Sdjm@openbsd.org 	e->type = AUTH_UNUSED;
18458f3486cSDamien Miller }
18558f3486cSDamien Miller 
18658f3486cSDamien Miller static void
idtab_init(void)187ad833b3eSDamien Miller idtab_init(void)
188d4a8b7e3SDamien Miller {
189f4a6a88dSdjm@openbsd.org 	idtab = xcalloc(1, sizeof(*idtab));
190f4a6a88dSdjm@openbsd.org 	TAILQ_INIT(&idtab->idlist);
191f4a6a88dSdjm@openbsd.org 	idtab->nentries = 0;
192ad833b3eSDamien Miller }
193ad833b3eSDamien Miller 
19461d328acSBen Lindstrom static void
free_identity(Identity * id)19561d328acSBen Lindstrom free_identity(Identity *id)
19661d328acSBen Lindstrom {
197139ca818Smarkus@openbsd.org 	sshkey_free(id->key);
198a627d42eSDarren Tucker 	free(id->provider);
199a627d42eSDarren Tucker 	free(id->comment);
20007da39f7Sdjm@openbsd.org 	free(id->sk_provider);
201a627d42eSDarren Tucker 	free(id);
20261d328acSBen Lindstrom }
20361d328acSBen Lindstrom 
204ad833b3eSDamien Miller /* return matching private key for given public key */
2051a534ae9SDamien Miller static Identity *
lookup_identity(struct sshkey * key)206f4a6a88dSdjm@openbsd.org lookup_identity(struct sshkey *key)
207ad833b3eSDamien Miller {
2081a534ae9SDamien Miller 	Identity *id;
2091a534ae9SDamien Miller 
210f4a6a88dSdjm@openbsd.org 	TAILQ_FOREACH(id, &idtab->idlist, next) {
211139ca818Smarkus@openbsd.org 		if (sshkey_equal(key, id->key))
2121a534ae9SDamien Miller 			return (id);
213ad833b3eSDamien Miller 	}
2141a534ae9SDamien Miller 	return (NULL);
215ad833b3eSDamien Miller }
2161a534ae9SDamien Miller 
2176c71179fSDamien Miller /* Check confirmation of keysign request */
2186c71179fSDamien Miller static int
confirm_key(Identity * id,const char * extra)219e0e8bee8Sdjm@openbsd.org confirm_key(Identity *id, const char *extra)
2206c71179fSDamien Miller {
221ce327b62SDarren Tucker 	char *p;
2226c71179fSDamien Miller 	int ret = -1;
2236c71179fSDamien Miller 
224139ca818Smarkus@openbsd.org 	p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT);
2259ce86c92Sdjm@openbsd.org 	if (p != NULL &&
226e0e8bee8Sdjm@openbsd.org 	    ask_permission("Allow use of key %s?\nKey fingerprint %s.%s%s",
227e0e8bee8Sdjm@openbsd.org 	    id->comment, p,
228e0e8bee8Sdjm@openbsd.org 	    extra == NULL ? "" : "\n", extra == NULL ? "" : extra))
2296c71179fSDamien Miller 		ret = 0;
230a627d42eSDarren Tucker 	free(p);
231ce327b62SDarren Tucker 
2326c71179fSDamien Miller 	return (ret);
2336c71179fSDamien Miller }
2346c71179fSDamien Miller 
235139ca818Smarkus@openbsd.org static void
send_status(SocketEntry * e,int success)236139ca818Smarkus@openbsd.org send_status(SocketEntry *e, int success)
237139ca818Smarkus@openbsd.org {
238139ca818Smarkus@openbsd.org 	int r;
239139ca818Smarkus@openbsd.org 
240139ca818Smarkus@openbsd.org 	if ((r = sshbuf_put_u32(e->output, 1)) != 0 ||
241139ca818Smarkus@openbsd.org 	    (r = sshbuf_put_u8(e->output, success ?
242139ca818Smarkus@openbsd.org 	    SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0)
243816036f1Sdjm@openbsd.org 		fatal_fr(r, "compose");
244139ca818Smarkus@openbsd.org }
245139ca818Smarkus@openbsd.org 
246ad833b3eSDamien Miller /* send list of supported public keys to 'client' */
247bba81213SBen Lindstrom static void
process_request_identities(SocketEntry * e)248f4a6a88dSdjm@openbsd.org process_request_identities(SocketEntry *e)
249ad833b3eSDamien Miller {
2501a534ae9SDamien Miller 	Identity *id;
251139ca818Smarkus@openbsd.org 	struct sshbuf *msg;
252139ca818Smarkus@openbsd.org 	int r;
253d4a8b7e3SDamien Miller 
2541fe16fd6Sdjm@openbsd.org 	debug2_f("entering");
2551fe16fd6Sdjm@openbsd.org 
256139ca818Smarkus@openbsd.org 	if ((msg = sshbuf_new()) == NULL)
257816036f1Sdjm@openbsd.org 		fatal_f("sshbuf_new failed");
258f4a6a88dSdjm@openbsd.org 	if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
259f4a6a88dSdjm@openbsd.org 	    (r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
260816036f1Sdjm@openbsd.org 		fatal_fr(r, "compose");
261f4a6a88dSdjm@openbsd.org 	TAILQ_FOREACH(id, &idtab->idlist, next) {
262*31d8d231Sdjm@openbsd.org 		if ((r = sshkey_puts_opts(id->key, msg,
263*31d8d231Sdjm@openbsd.org 		    SSHKEY_SERIALIZE_INFO)) != 0 ||
264f4a6a88dSdjm@openbsd.org 		    (r = sshbuf_put_cstring(msg, id->comment)) != 0) {
265816036f1Sdjm@openbsd.org 			error_fr(r, "compose key/comment");
266139ca818Smarkus@openbsd.org 			continue;
267139ca818Smarkus@openbsd.org 		}
268d4a8b7e3SDamien Miller 	}
269139ca818Smarkus@openbsd.org 	if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
270816036f1Sdjm@openbsd.org 		fatal_fr(r, "enqueue");
271139ca818Smarkus@openbsd.org 	sshbuf_free(msg);
272d4a8b7e3SDamien Miller }
273d4a8b7e3SDamien Miller 
274ad833b3eSDamien Miller 
27576c9fbbeSmarkus@openbsd.org static char *
agent_decode_alg(struct sshkey * key,u_int flags)27676c9fbbeSmarkus@openbsd.org agent_decode_alg(struct sshkey *key, u_int flags)
27776c9fbbeSmarkus@openbsd.org {
27876c9fbbeSmarkus@openbsd.org 	if (key->type == KEY_RSA) {
27976c9fbbeSmarkus@openbsd.org 		if (flags & SSH_AGENT_RSA_SHA2_256)
28076c9fbbeSmarkus@openbsd.org 			return "rsa-sha2-256";
28176c9fbbeSmarkus@openbsd.org 		else if (flags & SSH_AGENT_RSA_SHA2_512)
28276c9fbbeSmarkus@openbsd.org 			return "rsa-sha2-512";
2832317ce4bSdjm@openbsd.org 	} else if (key->type == KEY_RSA_CERT) {
2842317ce4bSdjm@openbsd.org 		if (flags & SSH_AGENT_RSA_SHA2_256)
2852317ce4bSdjm@openbsd.org 			return "rsa-sha2-256-cert-v01@openssh.com";
2862317ce4bSdjm@openbsd.org 		else if (flags & SSH_AGENT_RSA_SHA2_512)
2872317ce4bSdjm@openbsd.org 			return "rsa-sha2-512-cert-v01@openssh.com";
28876c9fbbeSmarkus@openbsd.org 	}
28976c9fbbeSmarkus@openbsd.org 	return NULL;
29076c9fbbeSmarkus@openbsd.org }
29176c9fbbeSmarkus@openbsd.org 
2920c111eb8Sdjm@openbsd.org /*
293e0e8bee8Sdjm@openbsd.org  * Attempt to parse the contents of a buffer as a SSH publickey userauth
294e0e8bee8Sdjm@openbsd.org  * request, checking its contents for consistency and matching the embedded
295e0e8bee8Sdjm@openbsd.org  * key against the one that is being used for signing.
296e0e8bee8Sdjm@openbsd.org  * Note: does not modify msg buffer.
297e0e8bee8Sdjm@openbsd.org  * Optionally extract the username and session ID from the request.
298e0e8bee8Sdjm@openbsd.org  */
299e0e8bee8Sdjm@openbsd.org static int
parse_userauth_request(struct sshbuf * msg,const struct sshkey * expected_key,char ** userp,struct sshbuf ** sess_idp)300e0e8bee8Sdjm@openbsd.org parse_userauth_request(struct sshbuf *msg, const struct sshkey *expected_key,
301e0e8bee8Sdjm@openbsd.org     char **userp, struct sshbuf **sess_idp)
302e0e8bee8Sdjm@openbsd.org {
303e0e8bee8Sdjm@openbsd.org 	struct sshbuf *b = NULL, *sess_id = NULL;
304e0e8bee8Sdjm@openbsd.org 	char *user = NULL, *service = NULL, *method = NULL, *pkalg = NULL;
305e0e8bee8Sdjm@openbsd.org 	int r;
306e0e8bee8Sdjm@openbsd.org 	u_char t, sig_follows;
307e0e8bee8Sdjm@openbsd.org 	struct sshkey *mkey = NULL;
308e0e8bee8Sdjm@openbsd.org 
309e0e8bee8Sdjm@openbsd.org 	if (userp != NULL)
310e0e8bee8Sdjm@openbsd.org 		*userp = NULL;
311e0e8bee8Sdjm@openbsd.org 	if (sess_idp != NULL)
312e0e8bee8Sdjm@openbsd.org 		*sess_idp = NULL;
313e0e8bee8Sdjm@openbsd.org 	if ((b = sshbuf_fromb(msg)) == NULL)
314e0e8bee8Sdjm@openbsd.org 		fatal_f("sshbuf_fromb");
315e0e8bee8Sdjm@openbsd.org 
316e0e8bee8Sdjm@openbsd.org 	/* SSH userauth request */
317e0e8bee8Sdjm@openbsd.org 	if ((r = sshbuf_froms(b, &sess_id)) != 0)
318e0e8bee8Sdjm@openbsd.org 		goto out;
319e0e8bee8Sdjm@openbsd.org 	if (sshbuf_len(sess_id) == 0) {
320e0e8bee8Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
321e0e8bee8Sdjm@openbsd.org 		goto out;
322e0e8bee8Sdjm@openbsd.org 	}
323e0e8bee8Sdjm@openbsd.org 	if ((r = sshbuf_get_u8(b, &t)) != 0 || /* SSH2_MSG_USERAUTH_REQUEST */
324e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, &user, NULL)) != 0 || /* server user */
325e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, &service, NULL)) != 0 || /* service */
326e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, &method, NULL)) != 0 || /* method */
327e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_u8(b, &sig_follows)) != 0 || /* sig-follows */
328e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0 || /* alg */
329e0e8bee8Sdjm@openbsd.org 	    (r = sshkey_froms(b, &mkey)) != 0) /* key */
330e0e8bee8Sdjm@openbsd.org 		goto out;
331e0e8bee8Sdjm@openbsd.org 	if (t != SSH2_MSG_USERAUTH_REQUEST ||
332e0e8bee8Sdjm@openbsd.org 	    sig_follows != 1 ||
333e0e8bee8Sdjm@openbsd.org 	    strcmp(service, "ssh-connection") != 0 ||
334e0e8bee8Sdjm@openbsd.org 	    !sshkey_equal(expected_key, mkey) ||
335e0e8bee8Sdjm@openbsd.org 	    sshkey_type_from_name(pkalg) != expected_key->type) {
336e0e8bee8Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
337e0e8bee8Sdjm@openbsd.org 		goto out;
338e0e8bee8Sdjm@openbsd.org 	}
339e0e8bee8Sdjm@openbsd.org 	if (strcmp(method, "publickey") != 0) {
340e0e8bee8Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
341e0e8bee8Sdjm@openbsd.org 		goto out;
342e0e8bee8Sdjm@openbsd.org 	}
343e0e8bee8Sdjm@openbsd.org 	if (sshbuf_len(b) != 0) {
344e0e8bee8Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
345e0e8bee8Sdjm@openbsd.org 		goto out;
346e0e8bee8Sdjm@openbsd.org 	}
347e0e8bee8Sdjm@openbsd.org 	/* success */
348e0e8bee8Sdjm@openbsd.org 	r = 0;
349e0e8bee8Sdjm@openbsd.org 	debug3_f("well formed userauth");
350e0e8bee8Sdjm@openbsd.org 	if (userp != NULL) {
351e0e8bee8Sdjm@openbsd.org 		*userp = user;
352e0e8bee8Sdjm@openbsd.org 		user = NULL;
353e0e8bee8Sdjm@openbsd.org 	}
354e0e8bee8Sdjm@openbsd.org 	if (sess_idp != NULL) {
355e0e8bee8Sdjm@openbsd.org 		*sess_idp = sess_id;
356e0e8bee8Sdjm@openbsd.org 		sess_id = NULL;
357e0e8bee8Sdjm@openbsd.org 	}
358e0e8bee8Sdjm@openbsd.org  out:
359e0e8bee8Sdjm@openbsd.org 	sshbuf_free(b);
360e0e8bee8Sdjm@openbsd.org 	sshbuf_free(sess_id);
361e0e8bee8Sdjm@openbsd.org 	free(user);
362e0e8bee8Sdjm@openbsd.org 	free(service);
363e0e8bee8Sdjm@openbsd.org 	free(method);
364e0e8bee8Sdjm@openbsd.org 	free(pkalg);
365e0e8bee8Sdjm@openbsd.org 	sshkey_free(mkey);
366e0e8bee8Sdjm@openbsd.org 	return r;
367e0e8bee8Sdjm@openbsd.org }
368e0e8bee8Sdjm@openbsd.org 
369e0e8bee8Sdjm@openbsd.org /*
370e0e8bee8Sdjm@openbsd.org  * Attempt to parse the contents of a buffer as a SSHSIG signature request.
371e0e8bee8Sdjm@openbsd.org  * Note: does not modify buffer.
372e0e8bee8Sdjm@openbsd.org  */
373e0e8bee8Sdjm@openbsd.org static int
parse_sshsig_request(struct sshbuf * msg)374e0e8bee8Sdjm@openbsd.org parse_sshsig_request(struct sshbuf *msg)
375e0e8bee8Sdjm@openbsd.org {
376e0e8bee8Sdjm@openbsd.org 	int r;
377e0e8bee8Sdjm@openbsd.org 	struct sshbuf *b;
378e0e8bee8Sdjm@openbsd.org 
379e0e8bee8Sdjm@openbsd.org 	if ((b = sshbuf_fromb(msg)) == NULL)
380e0e8bee8Sdjm@openbsd.org 		fatal_f("sshbuf_fromb");
381e0e8bee8Sdjm@openbsd.org 
382e0e8bee8Sdjm@openbsd.org 	if ((r = sshbuf_cmp(b, 0, "SSHSIG", 6)) != 0 ||
383e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_consume(b, 6)) != 0 ||
384e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, NULL, NULL)) != 0 || /* namespace */
385e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || /* reserved */
386e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(b, NULL, NULL)) != 0 || /* hashalg */
387e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_string_direct(b, NULL, NULL)) != 0) /* H(msg) */
388e0e8bee8Sdjm@openbsd.org 		goto out;
389e0e8bee8Sdjm@openbsd.org 	if (sshbuf_len(b) != 0) {
390e0e8bee8Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
391e0e8bee8Sdjm@openbsd.org 		goto out;
392e0e8bee8Sdjm@openbsd.org 	}
393e0e8bee8Sdjm@openbsd.org 	/* success */
394e0e8bee8Sdjm@openbsd.org 	r = 0;
395e0e8bee8Sdjm@openbsd.org  out:
396e0e8bee8Sdjm@openbsd.org 	sshbuf_free(b);
397e0e8bee8Sdjm@openbsd.org 	return r;
398e0e8bee8Sdjm@openbsd.org }
399e0e8bee8Sdjm@openbsd.org 
400e0e8bee8Sdjm@openbsd.org /*
4010c111eb8Sdjm@openbsd.org  * This function inspects a message to be signed by a FIDO key that has a
4020c111eb8Sdjm@openbsd.org  * web-like application string (i.e. one that does not begin with "ssh:".
4030c111eb8Sdjm@openbsd.org  * It checks that the message is one of those expected for SSH operations
4040c111eb8Sdjm@openbsd.org  * (pubkey userauth, sshsig, CA key signing) to exclude signing challenges
4050c111eb8Sdjm@openbsd.org  * for the web.
4060c111eb8Sdjm@openbsd.org  */
4070c111eb8Sdjm@openbsd.org static int
check_websafe_message_contents(struct sshkey * key,struct sshbuf * data)408e0e8bee8Sdjm@openbsd.org check_websafe_message_contents(struct sshkey *key, struct sshbuf *data)
4090c111eb8Sdjm@openbsd.org {
410e0e8bee8Sdjm@openbsd.org 	if (parse_userauth_request(data, key, NULL, NULL) == 0) {
411e0e8bee8Sdjm@openbsd.org 		debug_f("signed data matches public key userauth request");
4120c111eb8Sdjm@openbsd.org 		return 1;
413e0e8bee8Sdjm@openbsd.org 	}
414e0e8bee8Sdjm@openbsd.org 	if (parse_sshsig_request(data) == 0) {
415e0e8bee8Sdjm@openbsd.org 		debug_f("signed data matches SSHSIG signature request");
416e0e8bee8Sdjm@openbsd.org 		return 1;
4170c111eb8Sdjm@openbsd.org 	}
4180c111eb8Sdjm@openbsd.org 
419e0e8bee8Sdjm@openbsd.org 	/* XXX check CA signature operation */
4200c111eb8Sdjm@openbsd.org 
4210c111eb8Sdjm@openbsd.org 	error("web-origin key attempting to sign non-SSH message");
4220c111eb8Sdjm@openbsd.org 	return 0;
4230c111eb8Sdjm@openbsd.org }
4240c111eb8Sdjm@openbsd.org 
425ad833b3eSDamien Miller /* ssh2 only */
426bba81213SBen Lindstrom static void
process_sign_request2(SocketEntry * e)427ad833b3eSDamien Miller process_sign_request2(SocketEntry *e)
428ad833b3eSDamien Miller {
429f4a6a88dSdjm@openbsd.org 	u_char *signature = NULL;
430e26c9807Sdtucker@openbsd.org 	size_t slen = 0;
431139ca818Smarkus@openbsd.org 	u_int compat = 0, flags;
43205daa211Sdjm@openbsd.org 	int r, ok = -1;
433b52ec0baSdjm@openbsd.org 	char *fp = NULL;
434e0e8bee8Sdjm@openbsd.org 	struct sshbuf *msg = NULL, *data = NULL;
435f4a6a88dSdjm@openbsd.org 	struct sshkey *key = NULL;
4360088c57aSdjm@openbsd.org 	struct identity *id;
437b52ec0baSdjm@openbsd.org 	struct notifier_ctx *notifier = NULL;
438ad833b3eSDamien Miller 
439e0e8bee8Sdjm@openbsd.org 	debug_f("entering");
440e0e8bee8Sdjm@openbsd.org 
441d1532d90Sdtucker@openbsd.org 	if ((msg = sshbuf_new()) == NULL || (data = sshbuf_new()) == NULL)
442816036f1Sdjm@openbsd.org 		fatal_f("sshbuf_new failed");
443f4a6a88dSdjm@openbsd.org 	if ((r = sshkey_froms(e->request, &key)) != 0 ||
444e0e8bee8Sdjm@openbsd.org 	    (r = sshbuf_get_stringb(e->request, data)) != 0 ||
44593c68a8fSdjm@openbsd.org@openbsd.org 	    (r = sshbuf_get_u32(e->request, &flags)) != 0) {
446816036f1Sdjm@openbsd.org 		error_fr(r, "parse");
44793c68a8fSdjm@openbsd.org@openbsd.org 		goto send;
44893c68a8fSdjm@openbsd.org@openbsd.org 	}
44993c68a8fSdjm@openbsd.org@openbsd.org 
450f4a6a88dSdjm@openbsd.org 	if ((id = lookup_identity(key)) == NULL) {
451816036f1Sdjm@openbsd.org 		verbose_f("%s key not found", sshkey_type(key));
4520088c57aSdjm@openbsd.org 		goto send;
4530088c57aSdjm@openbsd.org 	}
454e0e8bee8Sdjm@openbsd.org 	if (id->confirm && confirm_key(id, NULL) != 0) {
455816036f1Sdjm@openbsd.org 		verbose_f("user refused key");
4560088c57aSdjm@openbsd.org 		goto send;
4570088c57aSdjm@openbsd.org 	}
4580c111eb8Sdjm@openbsd.org 	if (sshkey_is_sk(id->key)) {
4590c111eb8Sdjm@openbsd.org 		if (strncmp(id->key->sk_application, "ssh:", 4) != 0 &&
460e0e8bee8Sdjm@openbsd.org 		    !check_websafe_message_contents(key, data)) {
4610c111eb8Sdjm@openbsd.org 			/* error already logged */
4620c111eb8Sdjm@openbsd.org 			goto send;
4630c111eb8Sdjm@openbsd.org 		}
4640c111eb8Sdjm@openbsd.org 		if ((id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
465b52ec0baSdjm@openbsd.org 			if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
466b52ec0baSdjm@openbsd.org 			    SSH_FP_DEFAULT)) == NULL)
467816036f1Sdjm@openbsd.org 				fatal_f("fingerprint failed");
468b52ec0baSdjm@openbsd.org 			notifier = notify_start(0,
469b52ec0baSdjm@openbsd.org 			    "Confirm user presence for key %s %s",
470b52ec0baSdjm@openbsd.org 			    sshkey_type(id->key), fp);
47107da39f7Sdjm@openbsd.org 		}
4720c111eb8Sdjm@openbsd.org 	}
4739b8ad938Sdjm@openbsd.org 	/* XXX support PIN required FIDO keys */
4740088c57aSdjm@openbsd.org 	if ((r = sshkey_sign(id->key, &signature, &slen,
475e0e8bee8Sdjm@openbsd.org 	    sshbuf_ptr(data), sshbuf_len(data), agent_decode_alg(key, flags),
4769b8ad938Sdjm@openbsd.org 	    id->sk_provider, NULL, compat)) != 0) {
477816036f1Sdjm@openbsd.org 		error_fr(r, "sshkey_sign");
4780088c57aSdjm@openbsd.org 		goto send;
4790088c57aSdjm@openbsd.org 	}
4800088c57aSdjm@openbsd.org 	/* Success */
4810088c57aSdjm@openbsd.org 	ok = 0;
4820088c57aSdjm@openbsd.org  send:
483d5a0cd4fSdjm@openbsd.org 	notify_complete(notifier, "User presence confirmed");
484e0e8bee8Sdjm@openbsd.org 
485ad833b3eSDamien Miller 	if (ok == 0) {
486139ca818Smarkus@openbsd.org 		if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
487139ca818Smarkus@openbsd.org 		    (r = sshbuf_put_string(msg, signature, slen)) != 0)
488816036f1Sdjm@openbsd.org 			fatal_fr(r, "compose");
489139ca818Smarkus@openbsd.org 	} else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
490816036f1Sdjm@openbsd.org 		fatal_fr(r, "compose failure");
491139ca818Smarkus@openbsd.org 
492139ca818Smarkus@openbsd.org 	if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
493816036f1Sdjm@openbsd.org 		fatal_fr(r, "enqueue");
494139ca818Smarkus@openbsd.org 
495e0e8bee8Sdjm@openbsd.org 	sshbuf_free(data);
496139ca818Smarkus@openbsd.org 	sshbuf_free(msg);
497e0e8bee8Sdjm@openbsd.org 	sshkey_free(key);
498e0e8bee8Sdjm@openbsd.org 	free(fp);
499a627d42eSDarren Tucker 	free(signature);
500d4a8b7e3SDamien Miller }
501d4a8b7e3SDamien Miller 
502ad833b3eSDamien Miller /* shared */
503bba81213SBen Lindstrom static void
process_remove_identity(SocketEntry * e)504f4a6a88dSdjm@openbsd.org process_remove_identity(SocketEntry *e)
505d4a8b7e3SDamien Miller {
506139ca818Smarkus@openbsd.org 	int r, success = 0;
507139ca818Smarkus@openbsd.org 	struct sshkey *key = NULL;
508f4a6a88dSdjm@openbsd.org 	Identity *id;
509d4a8b7e3SDamien Miller 
5101fe16fd6Sdjm@openbsd.org 	debug2_f("entering");
511f4a6a88dSdjm@openbsd.org 	if ((r = sshkey_froms(e->request, &key)) != 0) {
512816036f1Sdjm@openbsd.org 		error_fr(r, "parse key");
513f4a6a88dSdjm@openbsd.org 		goto done;
514ad833b3eSDamien Miller 	}
515f4a6a88dSdjm@openbsd.org 	if ((id = lookup_identity(key)) == NULL) {
516816036f1Sdjm@openbsd.org 		debug_f("key not found");
517f4a6a88dSdjm@openbsd.org 		goto done;
518f4a6a88dSdjm@openbsd.org 	}
519f4a6a88dSdjm@openbsd.org 	/* We have this key, free it. */
520f4a6a88dSdjm@openbsd.org 	if (idtab->nentries < 1)
521816036f1Sdjm@openbsd.org 		fatal_f("internal error: nentries %d", idtab->nentries);
522f4a6a88dSdjm@openbsd.org 	TAILQ_REMOVE(&idtab->idlist, id, next);
5231a534ae9SDamien Miller 	free_identity(id);
524f4a6a88dSdjm@openbsd.org 	idtab->nentries--;
525f4a6a88dSdjm@openbsd.org 	success = 1;
526f4a6a88dSdjm@openbsd.org  done:
5273287790eSdjm@openbsd.org 	sshkey_free(key);
528139ca818Smarkus@openbsd.org 	send_status(e, success);
529d4a8b7e3SDamien Miller }
530d4a8b7e3SDamien Miller 
531bba81213SBen Lindstrom static void
process_remove_all_identities(SocketEntry * e)532f4a6a88dSdjm@openbsd.org process_remove_all_identities(SocketEntry *e)
533d4a8b7e3SDamien Miller {
5341a534ae9SDamien Miller 	Identity *id;
535d4a8b7e3SDamien Miller 
5361fe16fd6Sdjm@openbsd.org 	debug2_f("entering");
537d4a8b7e3SDamien Miller 	/* Loop over all identities and clear the keys. */
538f4a6a88dSdjm@openbsd.org 	for (id = TAILQ_FIRST(&idtab->idlist); id;
539f4a6a88dSdjm@openbsd.org 	    id = TAILQ_FIRST(&idtab->idlist)) {
540f4a6a88dSdjm@openbsd.org 		TAILQ_REMOVE(&idtab->idlist, id, next);
5411a534ae9SDamien Miller 		free_identity(id);
542d4a8b7e3SDamien Miller 	}
543d4a8b7e3SDamien Miller 
544d4a8b7e3SDamien Miller 	/* Mark that there are no identities. */
545f4a6a88dSdjm@openbsd.org 	idtab->nentries = 0;
546d4a8b7e3SDamien Miller 
547d4a8b7e3SDamien Miller 	/* Send success. */
548139ca818Smarkus@openbsd.org 	send_status(e, 1);
549d4a8b7e3SDamien Miller }
550d4a8b7e3SDamien Miller 
5512812dc92SDarren Tucker /* removes expired keys and returns number of seconds until the next expiry */
55255119253SDarren Tucker static time_t
reaper(void)55361d328acSBen Lindstrom reaper(void)
55461d328acSBen Lindstrom {
555b759c9c2SDarren Tucker 	time_t deadline = 0, now = monotime();
55661d328acSBen Lindstrom 	Identity *id, *nxt;
55761d328acSBen Lindstrom 
558f4a6a88dSdjm@openbsd.org 	for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
55961d328acSBen Lindstrom 		nxt = TAILQ_NEXT(id, next);
5602812dc92SDarren Tucker 		if (id->death == 0)
5612812dc92SDarren Tucker 			continue;
5622812dc92SDarren Tucker 		if (now >= id->death) {
563cf0d2db2SDarren Tucker 			debug("expiring key '%s'", id->comment);
564f4a6a88dSdjm@openbsd.org 			TAILQ_REMOVE(&idtab->idlist, id, next);
56561d328acSBen Lindstrom 			free_identity(id);
566f4a6a88dSdjm@openbsd.org 			idtab->nentries--;
5672812dc92SDarren Tucker 		} else
5682812dc92SDarren Tucker 			deadline = (deadline == 0) ? id->death :
5699136ec13Sderaadt@openbsd.org 			    MINIMUM(deadline, id->death);
57061d328acSBen Lindstrom 	}
5712812dc92SDarren Tucker 	if (deadline == 0 || deadline <= now)
5722812dc92SDarren Tucker 		return 0;
5732812dc92SDarren Tucker 	else
5742812dc92SDarren Tucker 		return (deadline - now);
57561d328acSBen Lindstrom }
57661d328acSBen Lindstrom 
57737c70ea8Sdjm@openbsd.org static int
parse_key_constraint_extension(struct sshbuf * m,char ** sk_providerp)578e04fd6ddSdjm@openbsd.org parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp)
579e04fd6ddSdjm@openbsd.org {
580e04fd6ddSdjm@openbsd.org 	char *ext_name = NULL;
581e04fd6ddSdjm@openbsd.org 	int r;
582e04fd6ddSdjm@openbsd.org 
583e04fd6ddSdjm@openbsd.org 	if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
584e04fd6ddSdjm@openbsd.org 		error_fr(r, "parse constraint extension");
585e04fd6ddSdjm@openbsd.org 		goto out;
586e04fd6ddSdjm@openbsd.org 	}
587e04fd6ddSdjm@openbsd.org 	debug_f("constraint ext %s", ext_name);
588e04fd6ddSdjm@openbsd.org 	if (strcmp(ext_name, "sk-provider@openssh.com") == 0) {
589e04fd6ddSdjm@openbsd.org 		if (sk_providerp == NULL) {
590e04fd6ddSdjm@openbsd.org 			error_f("%s not valid here", ext_name);
591e04fd6ddSdjm@openbsd.org 			r = SSH_ERR_INVALID_FORMAT;
592e04fd6ddSdjm@openbsd.org 			goto out;
593e04fd6ddSdjm@openbsd.org 		}
594e04fd6ddSdjm@openbsd.org 		if (*sk_providerp != NULL) {
595e04fd6ddSdjm@openbsd.org 			error_f("%s already set", ext_name);
596e04fd6ddSdjm@openbsd.org 			r = SSH_ERR_INVALID_FORMAT;
597e04fd6ddSdjm@openbsd.org 			goto out;
598