xref: /openssh-portable/authfd.c (revision cb7b22ea)
1*cb7b22eaSdjm@openbsd.org /* $OpenBSD: authfd.c,v 1.127 2021/01/26 00:46:17 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  * Functions for connecting the local authentication agent.
795def098SDamien 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  *
14ad833b3eSDamien Miller  * SSH2 implementation,
15ad833b3eSDamien Miller  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
16ad833b3eSDamien Miller  *
17e4340be5SDamien Miller  * Redistribution and use in source and binary forms, with or without
18e4340be5SDamien Miller  * modification, are permitted provided that the following conditions
19e4340be5SDamien Miller  * are met:
20e4340be5SDamien Miller  * 1. Redistributions of source code must retain the above copyright
21e4340be5SDamien Miller  *    notice, this list of conditions and the following disclaimer.
22e4340be5SDamien Miller  * 2. Redistributions in binary form must reproduce the above copyright
23e4340be5SDamien Miller  *    notice, this list of conditions and the following disclaimer in the
24e4340be5SDamien Miller  *    documentation and/or other materials provided with the distribution.
25e4340be5SDamien Miller  *
26e4340be5SDamien Miller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27e4340be5SDamien Miller  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28e4340be5SDamien Miller  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29e4340be5SDamien Miller  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30e4340be5SDamien Miller  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31e4340be5SDamien Miller  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32e4340be5SDamien Miller  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33e4340be5SDamien Miller  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34e4340be5SDamien Miller  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35e4340be5SDamien Miller  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36d4a8b7e3SDamien Miller  */
37d4a8b7e3SDamien Miller 
38d4a8b7e3SDamien Miller #include "includes.h"
39574c41fdSDamien Miller 
40574c41fdSDamien Miller #include <sys/types.h>
41574c41fdSDamien Miller #include <sys/un.h>
42e3b60b52SDamien Miller #include <sys/socket.h>
43226cfa03SBen Lindstrom 
4457cf6385SDamien Miller #include <fcntl.h>
45e7a1e5cfSDamien Miller #include <stdlib.h>
46d7834353SDamien Miller #include <signal.h>
47e3476ed0SDamien Miller #include <string.h>
4872687c8eSderaadt@openbsd.org #include <stdarg.h>
49e6b3b610SDamien Miller #include <unistd.h>
50141efe49Sdjm@openbsd.org #include <errno.h>
5157cf6385SDamien Miller 
52d7834353SDamien Miller #include "xmalloc.h"
53d4a8b7e3SDamien Miller #include "ssh.h"
54141efe49Sdjm@openbsd.org #include "sshbuf.h"
55141efe49Sdjm@openbsd.org #include "sshkey.h"
56994cf142SDamien Miller #include "authfd.h"
57226cfa03SBen Lindstrom #include "cipher.h"
5862cee007SDamien Miller #include "compat.h"
59226cfa03SBen Lindstrom #include "log.h"
60226cfa03SBen Lindstrom #include "atomicio.h"
613f941889SDamien Miller #include "misc.h"
62141efe49Sdjm@openbsd.org #include "ssherr.h"
63d4a8b7e3SDamien Miller 
64141efe49Sdjm@openbsd.org #define MAX_AGENT_IDENTITIES	2048		/* Max keys in agent reply */
65141efe49Sdjm@openbsd.org #define MAX_AGENT_REPLY_LEN	(256 * 1024)	/* Max bytes in agent reply */
6637023965SDamien Miller 
67874d77bbSDamien Miller /* macro to check for "agent failure" message */
68874d77bbSDamien Miller #define agent_failed(x) \
69141efe49Sdjm@openbsd.org     ((x == SSH_AGENT_FAILURE) || \
70141efe49Sdjm@openbsd.org     (x == SSH_COM_AGENT2_FAILURE) || \
71c9a26364SBen Lindstrom     (x == SSH2_AGENT_FAILURE))
72874d77bbSDamien Miller 
73141efe49Sdjm@openbsd.org /* Convert success/failure response from agent to a err.h status */
74141efe49Sdjm@openbsd.org static int
decode_reply(u_char type)75141efe49Sdjm@openbsd.org decode_reply(u_char type)
76789e95dbSDamien Miller {
77141efe49Sdjm@openbsd.org 	if (agent_failed(type))
78141efe49Sdjm@openbsd.org 		return SSH_ERR_AGENT_FAILURE;
79141efe49Sdjm@openbsd.org 	else if (type == SSH_AGENT_SUCCESS)
80789e95dbSDamien Miller 		return 0;
81141efe49Sdjm@openbsd.org 	else
82141efe49Sdjm@openbsd.org 		return SSH_ERR_INVALID_FORMAT;
83789e95dbSDamien Miller }
84789e95dbSDamien Miller 
8540be78f5Sdjm@openbsd.org /*
8640be78f5Sdjm@openbsd.org  * Opens an authentication socket at the provided path and stores the file
8740be78f5Sdjm@openbsd.org  * descriptor in fdp. Returns 0 on success and an error on failure.
8840be78f5Sdjm@openbsd.org  */
89d4a8b7e3SDamien Miller int
ssh_get_authentication_socket_path(const char * authsocket,int * fdp)9040be78f5Sdjm@openbsd.org ssh_get_authentication_socket_path(const char *authsocket, int *fdp)
91d4a8b7e3SDamien Miller {
92141efe49Sdjm@openbsd.org 	int sock, oerrno;
93d4a8b7e3SDamien Miller 	struct sockaddr_un sunaddr;
94d4a8b7e3SDamien Miller 
951d2c4564SDamien Miller 	memset(&sunaddr, 0, sizeof(sunaddr));
96d4a8b7e3SDamien Miller 	sunaddr.sun_family = AF_UNIX;
97d4a8b7e3SDamien Miller 	strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
98d4a8b7e3SDamien Miller 
994d28fa78Sderaadt@openbsd.org 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
100141efe49Sdjm@openbsd.org 		return SSH_ERR_SYSTEM_ERROR;
101d4a8b7e3SDamien Miller 
10210f6f6baSDamien Miller 	/* close on exec */
103141efe49Sdjm@openbsd.org 	if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 ||
1044d28fa78Sderaadt@openbsd.org 	    connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
105141efe49Sdjm@openbsd.org 		oerrno = errno;
10610f6f6baSDamien Miller 		close(sock);
107141efe49Sdjm@openbsd.org 		errno = oerrno;
108141efe49Sdjm@openbsd.org 		return SSH_ERR_SYSTEM_ERROR;
10910f6f6baSDamien Miller 	}
110141efe49Sdjm@openbsd.org 	if (fdp != NULL)
111141efe49Sdjm@openbsd.org 		*fdp = sock;
112141efe49Sdjm@openbsd.org 	else
113d4a8b7e3SDamien Miller 		close(sock);
114141efe49Sdjm@openbsd.org 	return 0;
115d4a8b7e3SDamien Miller }
116d4a8b7e3SDamien Miller 
11740be78f5Sdjm@openbsd.org /*
11840be78f5Sdjm@openbsd.org  * Opens the default authentication socket and stores the file descriptor in
11940be78f5Sdjm@openbsd.org  * fdp. Returns 0 on success and an error on failure.
12040be78f5Sdjm@openbsd.org  */
12140be78f5Sdjm@openbsd.org int
ssh_get_authentication_socket(int * fdp)12240be78f5Sdjm@openbsd.org ssh_get_authentication_socket(int *fdp)
12340be78f5Sdjm@openbsd.org {
12440be78f5Sdjm@openbsd.org 	const char *authsocket;
12540be78f5Sdjm@openbsd.org 
12640be78f5Sdjm@openbsd.org 	if (fdp != NULL)
12740be78f5Sdjm@openbsd.org 		*fdp = -1;
12840be78f5Sdjm@openbsd.org 
12940be78f5Sdjm@openbsd.org 	authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
13040be78f5Sdjm@openbsd.org 	if (authsocket == NULL || *authsocket == '\0')
13140be78f5Sdjm@openbsd.org 		return SSH_ERR_AGENT_NOT_PRESENT;
13240be78f5Sdjm@openbsd.org 
13340be78f5Sdjm@openbsd.org 	return ssh_get_authentication_socket_path(authsocket, fdp);
13440be78f5Sdjm@openbsd.org }
13540be78f5Sdjm@openbsd.org 
136141efe49Sdjm@openbsd.org /* Communicate with agent: send request and read reply */
137bba81213SBen Lindstrom static int
ssh_request_reply(int sock,struct sshbuf * request,struct sshbuf * reply)138141efe49Sdjm@openbsd.org ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
139942da039SDamien Miller {
140141efe49Sdjm@openbsd.org 	int r;
141141efe49Sdjm@openbsd.org 	size_t l, len;
142942da039SDamien Miller 	char buf[1024];
143942da039SDamien Miller 
144942da039SDamien Miller 	/* Get the length of the message, and format it in the buffer. */
145141efe49Sdjm@openbsd.org 	len = sshbuf_len(request);
1461b11ea7cSmarkus@openbsd.org 	POKE_U32(buf, len);
147942da039SDamien Miller 
148942da039SDamien Miller 	/* Send the length and then the packet to the agent. */
149141efe49Sdjm@openbsd.org 	if (atomicio(vwrite, sock, buf, 4) != 4 ||
15049f47e65Smarkus@openbsd.org 	    atomicio(vwrite, sock, sshbuf_mutable_ptr(request),
151141efe49Sdjm@openbsd.org 	    sshbuf_len(request)) != sshbuf_len(request))
152141efe49Sdjm@openbsd.org 		return SSH_ERR_AGENT_COMMUNICATION;
153942da039SDamien Miller 	/*
154942da039SDamien Miller 	 * Wait for response from the agent.  First read the length of the
155942da039SDamien Miller 	 * response packet.
156942da039SDamien Miller 	 */
157141efe49Sdjm@openbsd.org 	if (atomicio(read, sock, buf, 4) != 4)
158141efe49Sdjm@openbsd.org 	    return SSH_ERR_AGENT_COMMUNICATION;
159942da039SDamien Miller 
160942da039SDamien Miller 	/* Extract the length, and check it for sanity. */
1611b11ea7cSmarkus@openbsd.org 	len = PEEK_U32(buf);
162141efe49Sdjm@openbsd.org 	if (len > MAX_AGENT_REPLY_LEN)
163141efe49Sdjm@openbsd.org 		return SSH_ERR_INVALID_FORMAT;
164942da039SDamien Miller 
165942da039SDamien Miller 	/* Read the rest of the response in to the buffer. */
166141efe49Sdjm@openbsd.org 	sshbuf_reset(reply);
167942da039SDamien Miller 	while (len > 0) {
168942da039SDamien Miller 		l = len;
169942da039SDamien Miller 		if (l > sizeof(buf))
170942da039SDamien Miller 			l = sizeof(buf);
171141efe49Sdjm@openbsd.org 		if (atomicio(read, sock, buf, l) != l)
172141efe49Sdjm@openbsd.org 			return SSH_ERR_AGENT_COMMUNICATION;
173141efe49Sdjm@openbsd.org 		if ((r = sshbuf_put(reply, buf, l)) != 0)
174141efe49Sdjm@openbsd.org 			return r;
175942da039SDamien Miller 		len -= l;
176942da039SDamien Miller 	}
177141efe49Sdjm@openbsd.org 	return 0;
178942da039SDamien Miller }
179942da039SDamien Miller 
180*cb7b22eaSdjm@openbsd.org /* Communicate with agent: sent request, read and decode status reply */
181*cb7b22eaSdjm@openbsd.org static int
ssh_request_reply_decode(int sock,struct sshbuf * request)182*cb7b22eaSdjm@openbsd.org ssh_request_reply_decode(int sock, struct sshbuf *request)
183*cb7b22eaSdjm@openbsd.org {
184*cb7b22eaSdjm@openbsd.org 	struct sshbuf *reply;
185*cb7b22eaSdjm@openbsd.org 	int r;
186*cb7b22eaSdjm@openbsd.org 	u_char type;
187*cb7b22eaSdjm@openbsd.org 
188*cb7b22eaSdjm@openbsd.org 	if ((reply = sshbuf_new()) == NULL)
189*cb7b22eaSdjm@openbsd.org 		return SSH_ERR_ALLOC_FAIL;
190*cb7b22eaSdjm@openbsd.org 	if ((r = ssh_request_reply(sock, request, reply)) != 0 ||
191*cb7b22eaSdjm@openbsd.org 	    (r = sshbuf_get_u8(reply, &type)) != 0 ||
192*cb7b22eaSdjm@openbsd.org 	    (r = decode_reply(type)) != 0)
193*cb7b22eaSdjm@openbsd.org 		goto out;
194*cb7b22eaSdjm@openbsd.org 	/* success */
195*cb7b22eaSdjm@openbsd.org 	r = 0;
196*cb7b22eaSdjm@openbsd.org  out:
197*cb7b22eaSdjm@openbsd.org 	sshbuf_free(reply);
198*cb7b22eaSdjm@openbsd.org 	return r;
199*cb7b22eaSdjm@openbsd.org }
200*cb7b22eaSdjm@openbsd.org 
2015428f646SDamien Miller /*
2025428f646SDamien Miller  * Closes the agent socket if it should be closed (depends on how it was
2035428f646SDamien Miller  * obtained).  The argument must have been returned by
2045428f646SDamien Miller  * ssh_get_authentication_socket().
2055428f646SDamien Miller  */
20695def098SDamien Miller void
ssh_close_authentication_socket(int sock)20795def098SDamien Miller ssh_close_authentication_socket(int sock)
208d4a8b7e3SDamien Miller {
209d4a8b7e3SDamien Miller 	if (getenv(SSH_AUTHSOCKET_ENV_NAME))
210d4a8b7e3SDamien Miller 		close(sock);
211d4a8b7e3SDamien Miller }
212d4a8b7e3SDamien Miller 
2132f71704bSBen Lindstrom /* Lock/unlock agent */
2142f71704bSBen Lindstrom int
ssh_lock_agent(int sock,int lock,const char * password)215141efe49Sdjm@openbsd.org ssh_lock_agent(int sock, int lock, const char *password)
2162f71704bSBen Lindstrom {
217141efe49Sdjm@openbsd.org 	int r;
218141efe49Sdjm@openbsd.org 	u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK;
219141efe49Sdjm@openbsd.org 	struct sshbuf *msg;
2202f71704bSBen Lindstrom 
221141efe49Sdjm@openbsd.org 	if ((msg = sshbuf_new()) == NULL)
222141efe49Sdjm@openbsd.org 		return SSH_ERR_ALLOC_FAIL;
223141efe49Sdjm@openbsd.org 	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
224*cb7b22eaSdjm@openbsd.org 	    (r = sshbuf_put_cstring(msg, password)) != 0 ||
225*cb7b22eaSdjm@openbsd.org 	    (r = ssh_request_reply_decode(sock, msg)) != 0)
226141efe49Sdjm@openbsd.org 		goto out;
227*cb7b22eaSdjm@openbsd.org 	/* success */
228*cb7b22eaSdjm@openbsd.org 	r = 0;
229141efe49Sdjm@openbsd.org  out:
230141efe49Sdjm@openbsd.org 	sshbuf_free(msg);
231141efe49Sdjm@openbsd.org 	return r;
2322f71704bSBen Lindstrom }
233141efe49Sdjm@openbsd.org 
234141efe49Sdjm@openbsd.org 
235141efe49Sdjm@openbsd.org static int
deserialise_identity2(struct sshbuf * ids,struct sshkey ** keyp,char ** commentp)236141efe49Sdjm@openbsd.org deserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp)
237141efe49Sdjm@openbsd.org {
238141efe49Sdjm@openbsd.org 	int r;
239141efe49Sdjm@openbsd.org 	char *comment = NULL;
240141efe49Sdjm@openbsd.org 	const u_char *blob;
241141efe49Sdjm@openbsd.org 	size_t blen;
242141efe49Sdjm@openbsd.org 
243141efe49Sdjm@openbsd.org 	if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 ||
244141efe49Sdjm@openbsd.org 	    (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0)
245141efe49Sdjm@openbsd.org 		goto out;
246141efe49Sdjm@openbsd.org 	if ((r = sshkey_from_blob(blob, blen, keyp)) != 0)
247141efe49Sdjm@openbsd.org 		goto out;
248141efe49Sdjm@openbsd.org 	if (commentp != NULL) {
249141efe49Sdjm@openbsd.org 		*commentp = comment;
250141efe49Sdjm@openbsd.org 		comment = NULL;
251141efe49Sdjm@openbsd.org 	}
252141efe49Sdjm@openbsd.org 	r = 0;
253141efe49Sdjm@openbsd.org  out:
254141efe49Sdjm@openbsd.org 	free(comment);
255141efe49Sdjm@openbsd.org 	return r;
2562f71704bSBen Lindstrom }
2572f71704bSBen Lindstrom 
2585428f646SDamien Miller /*
259141efe49Sdjm@openbsd.org  * Fetch list of identities held by the agent.
2605428f646SDamien Miller  */
2610bc1bd81SDamien Miller int
ssh_fetch_identitylist(int sock,struct ssh_identitylist ** idlp)2623e371bd2Snaddy@openbsd.org ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp)
263d4a8b7e3SDamien Miller {
2643e371bd2Snaddy@openbsd.org 	u_char type;
265141efe49Sdjm@openbsd.org 	u_int32_t num, i;
266141efe49Sdjm@openbsd.org 	struct sshbuf *msg;
267141efe49Sdjm@openbsd.org 	struct ssh_identitylist *idl = NULL;
268141efe49Sdjm@openbsd.org 	int r;
269ad833b3eSDamien Miller 
2705428f646SDamien Miller 	/*
2715428f646SDamien Miller 	 * Send a message to the agent requesting for a list of the
2725428f646SDamien Miller 	 * identities it can represent.
2735428f646SDamien Miller 	 */
274141efe49Sdjm@openbsd.org 	if ((msg = sshbuf_new()) == NULL)
275141efe49Sdjm@openbsd.org 		return SSH_ERR_ALLOC_FAIL;
2763e371bd2Snaddy@openbsd.org 	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REQUEST_IDENTITIES)) != 0)
277141efe49Sdjm@openbsd.org 		goto out;
278d4a8b7e3SDamien Miller 
279141efe49Sdjm@openbsd.org 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
280141efe49Sdjm@openbsd.org 		goto out;
281d4a8b7e3SDamien Miller 
282d4a8b7e3SDamien Miller 	/* Get message type, and verify that we got a proper answer. */
283141efe49Sdjm@openbsd.org 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
284141efe49Sdjm@openbsd.org 		goto out;
285874d77bbSDamien Miller 	if (agent_failed(type)) {
286141efe49Sdjm@openbsd.org 		r = SSH_ERR_AGENT_FAILURE;
287141efe49Sdjm@openbsd.org 		goto out;
2883e371bd2Snaddy@openbsd.org 	} else if (type != SSH2_AGENT_IDENTITIES_ANSWER) {
289141efe49Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
290141efe49Sdjm@openbsd.org 		goto out;
291ad833b3eSDamien Miller 	}
292d4a8b7e3SDamien Miller 
293d4a8b7e3SDamien Miller 	/* Get the number of entries in the response and check it for sanity. */
294141efe49Sdjm@openbsd.org 	if ((r = sshbuf_get_u32(msg, &num)) != 0)
295141efe49Sdjm@openbsd.org 		goto out;
296141efe49Sdjm@openbsd.org 	if (num > MAX_AGENT_IDENTITIES) {
297141efe49Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
298141efe49Sdjm@openbsd.org 		goto out;
299141efe49Sdjm@openbsd.org 	}
300141efe49Sdjm@openbsd.org 	if (num == 0) {
301141efe49Sdjm@openbsd.org 		r = SSH_ERR_AGENT_NO_IDENTITIES;
302141efe49Sdjm@openbsd.org 		goto out;
3030bc1bd81SDamien Miller 	}
3040bc1bd81SDamien Miller 
305141efe49Sdjm@openbsd.org 	/* Deserialise the response into a list of keys/comments */
306141efe49Sdjm@openbsd.org 	if ((idl = calloc(1, sizeof(*idl))) == NULL ||
307141efe49Sdjm@openbsd.org 	    (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL ||
308141efe49Sdjm@openbsd.org 	    (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) {
309141efe49Sdjm@openbsd.org 		r = SSH_ERR_ALLOC_FAIL;
310141efe49Sdjm@openbsd.org 		goto out;
311d4a8b7e3SDamien Miller 	}
312141efe49Sdjm@openbsd.org 	for (i = 0; i < num;) {
3133e371bd2Snaddy@openbsd.org 		if ((r = deserialise_identity2(msg, &(idl->keys[i]),
3143e371bd2Snaddy@openbsd.org 		    &(idl->comments[i]))) != 0) {
315141efe49Sdjm@openbsd.org 			if (r == SSH_ERR_KEY_TYPE_UNKNOWN) {
316141efe49Sdjm@openbsd.org 				/* Gracefully skip unknown key types */
317141efe49Sdjm@openbsd.org 				num--;
318141efe49Sdjm@openbsd.org 				continue;
319141efe49Sdjm@openbsd.org 			} else
320141efe49Sdjm@openbsd.org 				goto out;
321ad833b3eSDamien Miller 		}
322141efe49Sdjm@openbsd.org 		i++;
323141efe49Sdjm@openbsd.org 	}
324141efe49Sdjm@openbsd.org 	idl->nkeys = num;
325141efe49Sdjm@openbsd.org 	*idlp = idl;
326141efe49Sdjm@openbsd.org 	idl = NULL;
327141efe49Sdjm@openbsd.org 	r = 0;
328141efe49Sdjm@openbsd.org  out:
329141efe49Sdjm@openbsd.org 	sshbuf_free(msg);
330141efe49Sdjm@openbsd.org 	if (idl != NULL)
331141efe49Sdjm@openbsd.org 		ssh_free_identitylist(idl);
332141efe49Sdjm@openbsd.org 	return r;
333141efe49Sdjm@openbsd.org }
334141efe49Sdjm@openbsd.org 
335141efe49Sdjm@openbsd.org void
ssh_free_identitylist(struct ssh_identitylist * idl)336141efe49Sdjm@openbsd.org ssh_free_identitylist(struct ssh_identitylist *idl)
337141efe49Sdjm@openbsd.org {
338141efe49Sdjm@openbsd.org 	size_t i;
339141efe49Sdjm@openbsd.org 
340141efe49Sdjm@openbsd.org 	if (idl == NULL)
341141efe49Sdjm@openbsd.org 		return;
342141efe49Sdjm@openbsd.org 	for (i = 0; i < idl->nkeys; i++) {
343141efe49Sdjm@openbsd.org 		if (idl->keys != NULL)
344141efe49Sdjm@openbsd.org 			sshkey_free(idl->keys[i]);
345141efe49Sdjm@openbsd.org 		if (idl->comments != NULL)
346141efe49Sdjm@openbsd.org 			free(idl->comments[i]);
347141efe49Sdjm@openbsd.org 	}
3482ab5a846Sdjm@openbsd.org 	free(idl->keys);
3492ab5a846Sdjm@openbsd.org 	free(idl->comments);
350141efe49Sdjm@openbsd.org 	free(idl);
351d4a8b7e3SDamien Miller }
352d4a8b7e3SDamien Miller 
3535428f646SDamien Miller /*
35406af3583Sdjm@openbsd.org  * Check if the ssh agent has a given key.
35506af3583Sdjm@openbsd.org  * Returns 0 if found, or a negative SSH_ERR_* error code on failure.
35606af3583Sdjm@openbsd.org  */
35706af3583Sdjm@openbsd.org int
ssh_agent_has_key(int sock,const struct sshkey * key)358a3e0c376Sdjm@openbsd.org ssh_agent_has_key(int sock, const struct sshkey *key)
35906af3583Sdjm@openbsd.org {
36006af3583Sdjm@openbsd.org 	int r, ret = SSH_ERR_KEY_NOT_FOUND;
36106af3583Sdjm@openbsd.org 	size_t i;
36206af3583Sdjm@openbsd.org 	struct ssh_identitylist *idlist = NULL;
36306af3583Sdjm@openbsd.org 
364bc30b446Smarkus@openbsd.org 	if ((r = ssh_fetch_identitylist(sock, &idlist)) != 0) {
36506af3583Sdjm@openbsd.org 		return r;
36606af3583Sdjm@openbsd.org 	}
36706af3583Sdjm@openbsd.org 
36806af3583Sdjm@openbsd.org 	for (i = 0; i < idlist->nkeys; i++) {
36906af3583Sdjm@openbsd.org 		if (sshkey_equal_public(idlist->keys[i], key)) {
37006af3583Sdjm@openbsd.org 			ret = 0;
37106af3583Sdjm@openbsd.org 			break;
37206af3583Sdjm@openbsd.org 		}
37306af3583Sdjm@openbsd.org 	}
37406af3583Sdjm@openbsd.org 
37506af3583Sdjm@openbsd.org 	ssh_free_identitylist(idlist);
37606af3583Sdjm@openbsd.org 	return ret;
37706af3583Sdjm@openbsd.org }
37806af3583Sdjm@openbsd.org 
37906af3583Sdjm@openbsd.org /*
380141efe49Sdjm@openbsd.org  * Sends a challenge (typically from a server via ssh(1)) to the agent,
381141efe49Sdjm@openbsd.org  * and waits for a response from the agent.
382141efe49Sdjm@openbsd.org  * Returns true (non-zero) if the agent gave the correct answer, zero
383141efe49Sdjm@openbsd.org  * otherwise.
3845428f646SDamien Miller  */
385d4a8b7e3SDamien Miller 
386d4a8b7e3SDamien Miller 
387001aa554Sdjm@openbsd.org /* encode signature algorithm in flag bits, so we can keep the msg format */
38876c9fbbeSmarkus@openbsd.org static u_int
agent_encode_alg(const struct sshkey * key,const char * alg)389a98339edSdjm@openbsd.org agent_encode_alg(const struct sshkey *key, const char *alg)
39076c9fbbeSmarkus@openbsd.org {
391007a88b4Sdjm@openbsd.org 	if (alg != NULL && sshkey_type_plain(key->type) == KEY_RSA) {
392007a88b4Sdjm@openbsd.org 		if (strcmp(alg, "rsa-sha2-256") == 0 ||
393007a88b4Sdjm@openbsd.org 		    strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0)
39476c9fbbeSmarkus@openbsd.org 			return SSH_AGENT_RSA_SHA2_256;
395007a88b4Sdjm@openbsd.org 		if (strcmp(alg, "rsa-sha2-512") == 0 ||
396007a88b4Sdjm@openbsd.org 		    strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0)
39776c9fbbeSmarkus@openbsd.org 			return SSH_AGENT_RSA_SHA2_512;
39876c9fbbeSmarkus@openbsd.org 	}
39976c9fbbeSmarkus@openbsd.org 	return 0;
40076c9fbbeSmarkus@openbsd.org }
40176c9fbbeSmarkus@openbsd.org 
402141efe49Sdjm@openbsd.org /* ask agent to sign data, returns err.h code on error, 0 on success */
403ad833b3eSDamien Miller int
ssh_agent_sign(int sock,const struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t datalen,const char * alg,u_int compat)404a98339edSdjm@openbsd.org ssh_agent_sign(int sock, const struct sshkey *key,
405141efe49Sdjm@openbsd.org     u_char **sigp, size_t *lenp,
40676c9fbbeSmarkus@openbsd.org     const u_char *data, size_t datalen, const char *alg, u_int compat)
407ad833b3eSDamien Miller {
408141efe49Sdjm@openbsd.org 	struct sshbuf *msg;
4094ba0d547Sdjm@openbsd.org 	u_char *sig = NULL, type = 0;
4104ba0d547Sdjm@openbsd.org 	size_t len = 0;
411141efe49Sdjm@openbsd.org 	u_int flags = 0;
412141efe49Sdjm@openbsd.org 	int r = SSH_ERR_INTERNAL_ERROR;
413ad833b3eSDamien Miller 
414141efe49Sdjm@openbsd.org 	*sigp = NULL;
415141efe49Sdjm@openbsd.org 	*lenp = 0;
416ad833b3eSDamien Miller 
417141efe49Sdjm@openbsd.org 	if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
418141efe49Sdjm@openbsd.org 		return SSH_ERR_INVALID_ARGUMENT;
419141efe49Sdjm@openbsd.org 	if ((msg = sshbuf_new()) == NULL)
420141efe49Sdjm@openbsd.org 		return SSH_ERR_ALLOC_FAIL;
42176c9fbbeSmarkus@openbsd.org 	flags |= agent_encode_alg(key, alg);
422141efe49Sdjm@openbsd.org 	if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
4234ba0d547Sdjm@openbsd.org 	    (r = sshkey_puts(key, msg)) != 0 ||
424141efe49Sdjm@openbsd.org 	    (r = sshbuf_put_string(msg, data, datalen)) != 0 ||
425141efe49Sdjm@openbsd.org 	    (r = sshbuf_put_u32(msg, flags)) != 0)
426141efe49Sdjm@openbsd.org 		goto out;
427f3a3ea18Sjsg@openbsd.org 	if ((r = ssh_request_reply(sock, msg, msg)) != 0)
428141efe49Sdjm@openbsd.org 		goto out;
429141efe49Sdjm@openbsd.org 	if ((r = sshbuf_get_u8(msg, &type)) != 0)
430141efe49Sdjm@openbsd.org 		goto out;
431874d77bbSDamien Miller 	if (agent_failed(type)) {
432141efe49Sdjm@openbsd.org 		r = SSH_ERR_AGENT_FAILURE;
433141efe49Sdjm@openbsd.org 		goto out;
434ad833b3eSDamien Miller 	} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
435141efe49Sdjm@openbsd.org 		r = SSH_ERR_INVALID_FORMAT;
436141efe49Sdjm@openbsd.org 		goto out;
437ad833b3eSDamien Miller 	}
4384ba0d547Sdjm@openbsd.org 	if ((r = sshbuf_get_string(msg, &sig, &len)) != 0)
439141efe49Sdjm@openbsd.org 		goto out;
4404ba0d547Sdjm@openbsd.org 	/* Check what we actually got back from the agent. */
4414ba0d547Sdjm@openbsd.org 	if ((r = sshkey_check_sigtype(sig, len, alg)) != 0)
4424ba0d547Sdjm@openbsd.org 		goto out;
4434ba0d547Sdjm@openbsd.org 	/* success */
4444ba0d547Sdjm@openbsd.org 	*sigp = sig;
445141efe49Sdjm@openbsd.org 	*lenp = len;
4464ba0d547Sdjm@openbsd.org 	sig = NULL;
4474ba0d547Sdjm@openbsd.org 	len = 0;
448141efe49Sdjm@openbsd.org 	r = 0;
449141efe49Sdjm@openbsd.org  out:
4504ba0d547Sdjm@openbsd.org 	freezero(sig, len);
451141efe49Sdjm@openbsd.org 	sshbuf_free(msg);
452141efe49Sdjm@openbsd.org 	return r;
453ad833b3eSDamien Miller }
454ad833b3eSDamien Miller 
455994cf142SDamien Miller /* Encode key for a message to the agent. */
456994cf142SDamien Miller 
457994cf142SDamien Miller 
458141efe49Sdjm@openbsd.org static int
encode_constraints(struct sshbuf * m,u_int life,u_int confirm,u_int maxsign,const char * provider)459b9dd14d3Sdjm@openbsd.org encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
460b9dd14d3Sdjm@openbsd.org     const char *provider)
461141efe49Sdjm@openbsd.org {
462141efe49Sdjm@openbsd.org 	int r;
463141efe49Sdjm@openbsd.org 
464141efe49Sdjm@openbsd.org 	if (life != 0) {
465141efe49Sdjm@openbsd.org 		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 ||
466141efe49Sdjm@openbsd.org 		    (r = sshbuf_put_u32(m, life)) != 0)
467141efe49Sdjm@openbsd.org 			goto out;
468141efe49Sdjm@openbsd.org 	}
469141efe49Sdjm@openbsd.org 	if (confirm != 0) {
470141efe49Sdjm@openbsd.org 		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0)
471141efe49Sdjm@openbsd.org 			goto out;
472141efe49Sdjm@openbsd.org 	}
4731b11ea7cSmarkus@openbsd.org 	if (maxsign != 0) {
4741b11ea7cSmarkus@openbsd.org 		if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_MAXSIGN)) != 0 ||
4751b11ea7cSmarkus@openbsd.org 		    (r = sshbuf_put_u32(m, maxsign)) != 0)
4761b11ea7cSmarkus@openbsd.org 			goto out;
4771b11ea7cSmarkus@openbsd.org 	}
478b9dd14d3Sdjm@openbsd.org 	if (provider != NULL) {
479b9dd14d3Sdjm@openbsd.org 		if ((r = sshbuf_put_u8(m,
480b9dd14d3Sdjm@openbsd.org 		    SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 ||
481b9dd14d3Sdjm@openbsd.org 		    (r = sshbuf_put_cstring(m,
482b9dd14d3Sdjm@openbsd.org 		    "sk-provider@openssh.com")) != 0 ||
483b9dd14d3Sdjm@openbsd.org 		    (r = sshbuf_put_cstring(m, provider)) != 0)
484b9dd14d3Sdjm@openbsd.org 			goto out;
485b9dd14d3Sdjm@openbsd.org 	}
486141efe49Sdjm@openbsd.org 	r = 0;
487141efe49Sdjm@openbsd.org  out:
488141efe49Sdjm@openbsd.org 	return r;
489994cf142SDamien Miller }
490994cf142SDamien Miller 
4915428f646SDamien Miller /*
492141efe49Sdjm@openbsd.org  * Adds an identity to the authentication server.
493141efe49Sdjm@openbsd.org  * This call is intended only for use by ssh-add(1) and like applications.
4945428f646SDamien Miller  */
49595def098SDamien Miller int
ssh_add_identity_constrained(int sock,struct sshkey * key,const char * comment,u_int life,u_int confirm,u_int maxsign,const char * provider)4964f7a56d5Sdjm@openbsd.org ssh_add_identity_constrained(int sock, struct sshkey *key,
497b9dd14d3Sdjm@openbsd.org     const char *comment, u_int life, u_int confirm, u_int maxsign,
498b9dd14d3Sdjm@openbsd.org     const char *provider)
499d4a8b7e3SDamien Miller {
500141efe49Sdjm@openbsd.org 	struct sshbuf *msg;
501b9dd14d3Sdjm@openbsd.org 	int r, constrained = (life || confirm || maxsign || provider);
502141efe49Sdjm@openbsd.org 	u_char type;
503d4a8b7e3SDamien Miller 
504141efe49Sdjm@openbsd.org 	if ((msg = sshbuf_new()) == NULL)
505141efe49Sdjm@openbsd.org 		return SSH_ERR_ALLOC_FAIL;
506994cf142SDamien Miller 
507994cf142SDamien Miller 	switch (key->type) {
5081f0311c7SDamien Miller #ifdef WITH_OPENSSL
5090bc1bd81SDamien Miller 	case KEY_RSA:
5100a80ca19SDamien Miller 	case KEY_RSA_CERT:
511994cf142SDamien Miller 	case KEY_DSA:
5120a80ca19SDamien Miller 	case KEY_DSA_CERT:
513eb8b60e3SDamien Miller 	case KEY_ECDSA:
514eb8b60e3SDamien Miller 	case KEY_ECDSA_CERT:
515b9dd14d3Sdjm@openbsd.org 	case KEY_ECDSA_SK:
516b9dd14d3Sdjm@openbsd.org 	case KEY_ECDSA_SK_CERT:
5171f0311c7SDamien Miller #endif
5185be9d9e3SDamien Miller 	case KEY_ED25519:
5195be9d9e3SDamien Miller 	case KEY_ED25519_CERT:
5202c55744aSmarkus@openbsd.org 	case KEY_ED25519_SK:
5212c55744aSmarkus@openbsd.org 	case KEY_ED25519_SK_CERT:
5221b11ea7cSmarkus@openbsd.org 	case KEY_XMSS:
5231b11ea7cSmarkus@openbsd.org 	case KEY_XMSS_CERT:
5242b266b7fSBen Lindstrom 		type = constrained ?
5252b266b7fSBen Lindstrom 		    SSH2_AGENTC_ADD_ID_CONSTRAINED :
5262b266b7fSBen Lindstrom 		    SSH2_AGENTC_ADD_IDENTITY;
527141efe49Sdjm@openbsd.org 		if ((r = sshbuf_put_u8(msg, type)) != 0 ||
5281b11ea7cSmarkus@openbsd.org 		    (r = sshkey_private_serialize_maxsign(key, msg, maxsign,
529eab2888cSdtucker@openbsd.org 		    0)) != 0 ||
5301b11ea7cSmarkus@openbsd.org 		    (r = sshbuf_put_cstring(msg, comment)) != 0)
531141efe49Sdjm@openbsd.org 			goto out;
532994cf142SDamien Miller 		break;
533994cf142SDamien Miller 	default:
534141efe49Sdjm@openbsd.org 		r = SSH_ERR_INVALID_ARGUMENT;
535141efe49Sdjm@openbsd.org 		goto out;
536994cf142SDamien Miller 	}
537141efe49Sdjm@openbsd.org 	if (constrained &&
538b9dd14d3Sdjm@openbsd.org 	    (r = encode_constraints(msg, life, confirm, maxsign,
539b9dd14d3Sdjm@openbsd.org 	    provider)) != 0)
540141efe49Sdjm@openbsd.org 		goto out;
541*cb7b22eaSdjm@openbsd.org 	if ((r = ssh_request_reply_decode(sock, msg)) != 0)
542141efe49Sdjm@openbsd.org 		goto out;
543*cb7b22eaSdjm@openbsd.org 	/* success */
544*cb7b22eaSdjm@openbsd.org 	r = 0;
545141efe49Sdjm@openbsd.org  out:
546141efe49Sdjm@openbsd.org 	sshbuf_free(msg);
547141efe49Sdjm@openbsd.org 	return r;
548d4a8b7e3SDamien Miller }
549d4a8b7e3SDamien Miller 
550