blob: 7eb6f0dc52360b9e308c9b0cf9c6404ba042bc82 [file] [log] [blame]
naddy@openbsd.orga47f6a62020-02-06 22:30:54 +00001/* $OpenBSD: ssh-agent.c,v 1.255 2020/02/06 22:30:54 naddy Exp $ */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10002/*
Damien Miller95def091999-11-25 00:26:21 +11003 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved
Damien Miller95def091999-11-25 00:26:21 +11006 * The authentication agent program.
Damien Millerad833b32000-08-23 10:46:23 +10007 *
Damien Millere4340be2000-09-16 13:29:08 +11008 * As far as I am concerned, the code I have written for this software
9 * can be used freely for any purpose. Any derived versions of this
10 * software must be clearly marked as such, and if the derived work is
11 * incompatible with the protocol description in the RFC file, it must be
12 * called by a name other than "ssh" or "Secure Shell".
13 *
Ben Lindstrom44697232001-07-04 03:32:30 +000014 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millere4340be2000-09-16 13:29:08 +110015 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Damien Miller95def091999-11-25 00:26:21 +110035 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100036
37#include "includes.h"
Damien Miller574c41f2006-03-15 11:40:10 +110038
39#include <sys/types.h>
Damien Miller8dbffe72006-08-05 11:02:17 +100040#include <sys/param.h>
41#include <sys/resource.h>
Damien Miller42fb0682006-03-15 14:03:06 +110042#include <sys/stat.h>
Damien Millere3b60b52006-07-10 21:08:03 +100043#include <sys/socket.h>
djm@openbsd.org07da39f2019-10-31 21:22:01 +000044#include <sys/wait.h>
Damien Miller9aec9192006-08-05 10:57:45 +100045#ifdef HAVE_SYS_TIME_H
46# include <sys/time.h>
47#endif
Damien Miller574c41f2006-03-15 11:40:10 +110048#ifdef HAVE_SYS_UN_H
49# include <sys/un.h>
50#endif
Damien Miller9b481512002-09-12 10:43:29 +100051#include "openbsd-compat/sys-queue.h"
Damien Millere3b60b52006-07-10 21:08:03 +100052
Damien Miller1f0311c2014-05-15 14:24:09 +100053#ifdef WITH_OPENSSL
Damien Millere3476ed2006-07-24 14:13:33 +100054#include <openssl/evp.h>
Darren Tuckerbfaaf962008-02-28 19:13:52 +110055#include "openbsd-compat/openssl-compat.h"
Damien Miller1f0311c2014-05-15 14:24:09 +100056#endif
Damien Millere3476ed2006-07-24 14:13:33 +100057
Darren Tucker39972492006-07-12 22:22:46 +100058#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100059#include <fcntl.h>
deraadt@openbsd.org2ae4f332015-01-16 06:40:12 +000060#include <limits.h>
Damien Miller03e20032006-03-15 11:16:59 +110061#ifdef HAVE_PATHS_H
Damien Millera9263d02006-03-15 11:18:26 +110062# include <paths.h>
Damien Miller03e20032006-03-15 11:16:59 +110063#endif
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +000064#ifdef HAVE_POLL_H
65# include <poll.h>
66#endif
Damien Miller6ff3cad2006-03-15 11:52:09 +110067#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100068#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100069#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100070#include <stdlib.h>
Damien Miller5598b4f2006-07-24 14:09:40 +100071#include <time.h>
Damien Millere3476ed2006-07-24 14:13:33 +100072#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100073#include <unistd.h>
Damien Millere97201f2015-05-21 17:55:15 +100074#ifdef HAVE_UTIL_H
75# include <util.h>
76#endif
Damien Miller6ff3cad2006-03-15 11:52:09 +110077
Damien Millerd7834352006-08-05 12:39:39 +100078#include "xmalloc.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100079#include "ssh.h"
markus@openbsd.org139ca812015-01-14 13:09:09 +000080#include "sshbuf.h"
81#include "sshkey.h"
Damien Miller994cf142000-07-21 10:19:44 +100082#include "authfd.h"
Damien Miller62cee002000-09-23 17:15:56 +110083#include "compat.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000084#include "log.h"
Damien Miller6c711792003-01-24 11:36:23 +110085#include "misc.h"
Damien Miller4a1c7aa2014-02-04 11:03:36 +110086#include "digest.h"
markus@openbsd.org139ca812015-01-14 13:09:09 +000087#include "ssherr.h"
djm@openbsd.org786d5992016-11-30 03:07:37 +000088#include "match.h"
djm@openbsd.org07da39f2019-10-31 21:22:01 +000089#include "msg.h"
djm@openbsd.org45ffa362019-11-15 02:38:07 +000090#include "ssherr.h"
djm@openbsd.org07da39f2019-10-31 21:22:01 +000091#include "pathnames.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110092#include "ssh-pkcs11.h"
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +000093#include "sk-api.h"
Ben Lindstrom3f471632001-07-04 03:53:15 +000094
djm@openbsd.org07da39f2019-10-31 21:22:01 +000095#ifndef DEFAULT_PROVIDER_WHITELIST
96# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
djm@openbsd.org786d5992016-11-30 03:07:37 +000097#endif
98
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +000099/* Maximum accepted message length */
100#define AGENT_MAX_LEN (256*1024)
djm@openbsd.orgd6915882019-01-22 22:58:50 +0000101/* Maximum bytes to read from client socket */
102#define AGENT_RBUF_LEN (4096)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000103
Ben Lindstrom65366a82001-12-06 16:32:47 +0000104typedef enum {
105 AUTH_UNUSED,
106 AUTH_SOCKET,
107 AUTH_CONNECTION
108} sock_type;
109
Damien Miller95def091999-11-25 00:26:21 +1100110typedef struct {
111 int fd;
Ben Lindstrom65366a82001-12-06 16:32:47 +0000112 sock_type type;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000113 struct sshbuf *input;
114 struct sshbuf *output;
115 struct sshbuf *request;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000116} SocketEntry;
117
Ben Lindstrom46c16222000-12-22 01:43:59 +0000118u_int sockets_alloc = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000119SocketEntry *sockets = NULL;
120
Damien Miller1a534ae2002-01-22 23:26:13 +1100121typedef struct identity {
122 TAILQ_ENTRY(identity) next;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000123 struct sshkey *key;
Damien Miller95def091999-11-25 00:26:21 +1100124 char *comment;
Damien Miller7ea845e2010-02-12 09:21:02 +1100125 char *provider;
Darren Tucker55119252013-06-02 07:43:59 +1000126 time_t death;
Damien Miller6c711792003-01-24 11:36:23 +1100127 u_int confirm;
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000128 char *sk_provider;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000129} Identity;
130
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000131struct idtable {
Damien Millerad833b32000-08-23 10:46:23 +1000132 int nentries;
Damien Miller1a534ae2002-01-22 23:26:13 +1100133 TAILQ_HEAD(idqueue, identity) idlist;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000134};
Damien Millerad833b32000-08-23 10:46:23 +1000135
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000136/* private key table */
137struct idtable *idtab;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000138
139int max_fd = 0;
140
141/* pid of shell == parent of agent */
Damien Miller166fca82000-04-20 07:42:21 +1000142pid_t parent_pid = -1;
Darren Tucker073f7952013-06-02 23:47:11 +1000143time_t parent_alive_interval = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000144
Damien Millerb1e967c2014-07-03 21:22:40 +1000145/* pid of process for which cleanup_socket is applicable */
146pid_t cleanup_pid = 0;
147
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000148/* pathname and directory for AUTH_SOCKET */
deraadt@openbsd.org2ae4f332015-01-16 06:40:12 +0000149char socket_name[PATH_MAX];
150char socket_dir[PATH_MAX];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000151
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000152/* PKCS#11/Security key path whitelist */
153static char *provider_whitelist;
djm@openbsd.org786d5992016-11-30 03:07:37 +0000154
Ben Lindstrom2f717042002-06-06 21:52:03 +0000155/* locking */
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000156#define LOCK_SIZE 32
157#define LOCK_SALT_SIZE 16
158#define LOCK_ROUNDS 1
Ben Lindstrom2f717042002-06-06 21:52:03 +0000159int locked = 0;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000160u_char lock_pwhash[LOCK_SIZE];
161u_char lock_salt[LOCK_SALT_SIZE];
Ben Lindstrom2f717042002-06-06 21:52:03 +0000162
Damien Miller95def091999-11-25 00:26:21 +1100163extern char *__progname;
Damien Miller95def091999-11-25 00:26:21 +1100164
Darren Tucker55119252013-06-02 07:43:59 +1000165/* Default lifetime in seconds (0 == forever) */
166static long lifetime = 0;
Damien Miller53d81482003-01-22 11:47:19 +1100167
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000168static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
169
Ben Lindstrombba81212001-06-25 05:01:22 +0000170static void
Damien Miller58f34862002-09-04 16:31:21 +1000171close_socket(SocketEntry *e)
172{
Damien Miller58f34862002-09-04 16:31:21 +1000173 close(e->fd);
174 e->fd = -1;
175 e->type = AUTH_UNUSED;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000176 sshbuf_free(e->input);
177 sshbuf_free(e->output);
178 sshbuf_free(e->request);
Damien Miller58f34862002-09-04 16:31:21 +1000179}
180
181static void
Damien Millerad833b32000-08-23 10:46:23 +1000182idtab_init(void)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000183{
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000184 idtab = xcalloc(1, sizeof(*idtab));
185 TAILQ_INIT(&idtab->idlist);
186 idtab->nentries = 0;
Damien Millerad833b32000-08-23 10:46:23 +1000187}
188
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000189static void
190free_identity(Identity *id)
191{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000192 sshkey_free(id->key);
Darren Tuckera627d422013-06-02 07:31:17 +1000193 free(id->provider);
194 free(id->comment);
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000195 free(id->sk_provider);
Darren Tuckera627d422013-06-02 07:31:17 +1000196 free(id);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000197}
198
Damien Millerad833b32000-08-23 10:46:23 +1000199/* return matching private key for given public key */
Damien Miller1a534ae2002-01-22 23:26:13 +1100200static Identity *
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000201lookup_identity(struct sshkey *key)
Damien Millerad833b32000-08-23 10:46:23 +1000202{
Damien Miller1a534ae2002-01-22 23:26:13 +1100203 Identity *id;
204
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000205 TAILQ_FOREACH(id, &idtab->idlist, next) {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000206 if (sshkey_equal(key, id->key))
Damien Miller1a534ae2002-01-22 23:26:13 +1100207 return (id);
Damien Millerad833b32000-08-23 10:46:23 +1000208 }
Damien Miller1a534ae2002-01-22 23:26:13 +1100209 return (NULL);
210}
211
Damien Miller6c711792003-01-24 11:36:23 +1100212/* Check confirmation of keysign request */
213static int
214confirm_key(Identity *id)
215{
Darren Tuckerce327b62004-11-05 20:38:03 +1100216 char *p;
Damien Miller6c711792003-01-24 11:36:23 +1100217 int ret = -1;
218
markus@openbsd.org139ca812015-01-14 13:09:09 +0000219 p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT);
djm@openbsd.org9ce86c92015-01-28 22:36:00 +0000220 if (p != NULL &&
221 ask_permission("Allow use of key %s?\nKey fingerprint %s.",
Darren Tuckerce327b62004-11-05 20:38:03 +1100222 id->comment, p))
223 ret = 0;
Darren Tuckera627d422013-06-02 07:31:17 +1000224 free(p);
Darren Tuckerce327b62004-11-05 20:38:03 +1100225
Damien Miller6c711792003-01-24 11:36:23 +1100226 return (ret);
227}
228
markus@openbsd.org139ca812015-01-14 13:09:09 +0000229static void
230send_status(SocketEntry *e, int success)
231{
232 int r;
233
234 if ((r = sshbuf_put_u32(e->output, 1)) != 0 ||
235 (r = sshbuf_put_u8(e->output, success ?
236 SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0)
237 fatal("%s: buffer error: %s", __func__, ssh_err(r));
238}
239
Damien Millerad833b32000-08-23 10:46:23 +1000240/* send list of supported public keys to 'client' */
Ben Lindstrombba81212001-06-25 05:01:22 +0000241static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000242process_request_identities(SocketEntry *e)
Damien Millerad833b32000-08-23 10:46:23 +1000243{
Damien Miller1a534ae2002-01-22 23:26:13 +1100244 Identity *id;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000245 struct sshbuf *msg;
246 int r;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000247
markus@openbsd.org139ca812015-01-14 13:09:09 +0000248 if ((msg = sshbuf_new()) == NULL)
249 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000250 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
251 (r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
markus@openbsd.org139ca812015-01-14 13:09:09 +0000252 fatal("%s: buffer error: %s", __func__, ssh_err(r));
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000253 TAILQ_FOREACH(id, &idtab->idlist, next) {
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000254 if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO))
255 != 0 ||
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000256 (r = sshbuf_put_cstring(msg, id->comment)) != 0) {
257 error("%s: put key/comment: %s", __func__,
djm@openbsd.org873d3e72017-04-30 23:18:44 +0000258 ssh_err(r));
259 continue;
Damien Millerad833b32000-08-23 10:46:23 +1000260 }
Damien Miller95def091999-11-25 00:26:21 +1100261 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000262 if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
263 fatal("%s: buffer error: %s", __func__, ssh_err(r));
264 sshbuf_free(msg);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000265}
266
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000267
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000268static char *
269agent_decode_alg(struct sshkey *key, u_int flags)
270{
271 if (key->type == KEY_RSA) {
272 if (flags & SSH_AGENT_RSA_SHA2_256)
273 return "rsa-sha2-256";
274 else if (flags & SSH_AGENT_RSA_SHA2_512)
275 return "rsa-sha2-512";
djm@openbsd.org2317ce42019-06-14 03:51:47 +0000276 } else if (key->type == KEY_RSA_CERT) {
277 if (flags & SSH_AGENT_RSA_SHA2_256)
278 return "rsa-sha2-256-cert-v01@openssh.com";
279 else if (flags & SSH_AGENT_RSA_SHA2_512)
280 return "rsa-sha2-512-cert-v01@openssh.com";
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000281 }
282 return NULL;
283}
284
Damien Millerad833b32000-08-23 10:46:23 +1000285/* ssh2 only */
Ben Lindstrombba81212001-06-25 05:01:22 +0000286static void
Damien Millerad833b32000-08-23 10:46:23 +1000287process_sign_request2(SocketEntry *e)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000288{
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000289 const u_char *data;
290 u_char *signature = NULL;
291 size_t dlen, slen = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000292 u_int compat = 0, flags;
djm@openbsd.org05daa212019-11-16 22:36:48 +0000293 int r, ok = -1;
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +0000294 char *fp = NULL;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000295 struct sshbuf *msg;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000296 struct sshkey *key = NULL;
djm@openbsd.org0088c572015-01-14 19:33:41 +0000297 struct identity *id;
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +0000298 struct notifier_ctx *notifier = NULL;
Damien Millerad833b32000-08-23 10:46:23 +1000299
djm@openbsd.org0088c572015-01-14 19:33:41 +0000300 if ((msg = sshbuf_new()) == NULL)
301 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000302 if ((r = sshkey_froms(e->request, &key)) != 0 ||
303 (r = sshbuf_get_string_direct(e->request, &data, &dlen)) != 0 ||
djm@openbsd.org@openbsd.org93c68a82017-11-15 00:13:40 +0000304 (r = sshbuf_get_u32(e->request, &flags)) != 0) {
305 error("%s: couldn't parse request: %s", __func__, ssh_err(r));
306 goto send;
307 }
308
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000309 if ((id = lookup_identity(key)) == NULL) {
djm@openbsd.org0088c572015-01-14 19:33:41 +0000310 verbose("%s: %s key not found", __func__, sshkey_type(key));
311 goto send;
312 }
313 if (id->confirm && confirm_key(id) != 0) {
314 verbose("%s: user refused key", __func__);
315 goto send;
316 }
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +0000317 if (sshkey_is_sk(id->key) &&
318 (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
319 if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
320 SSH_FP_DEFAULT)) == NULL)
321 fatal("%s: fingerprint failed", __func__);
322 notifier = notify_start(0,
323 "Confirm user presence for key %s %s",
324 sshkey_type(id->key), fp);
325 }
326 if ((r = sshkey_sign(id->key, &signature, &slen,
327 data, dlen, agent_decode_alg(key, flags),
328 id->sk_provider, compat)) != 0) {
329 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
330 goto send;
djm@openbsd.org0088c572015-01-14 19:33:41 +0000331 }
332 /* Success */
333 ok = 0;
334 send:
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +0000335 notify_complete(notifier);
djm@openbsd.org0088c572015-01-14 19:33:41 +0000336 sshkey_free(key);
djm@openbsd.orgb52ec0b2019-12-13 19:09:10 +0000337 free(fp);
Damien Millerad833b32000-08-23 10:46:23 +1000338 if (ok == 0) {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000339 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
340 (r = sshbuf_put_string(msg, signature, slen)) != 0)
341 fatal("%s: buffer error: %s", __func__, ssh_err(r));
342 } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
343 fatal("%s: buffer error: %s", __func__, ssh_err(r));
344
345 if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
346 fatal("%s: buffer error: %s", __func__, ssh_err(r));
347
348 sshbuf_free(msg);
Darren Tuckera627d422013-06-02 07:31:17 +1000349 free(signature);
Damien Millerad833b32000-08-23 10:46:23 +1000350}
351
352/* shared */
Ben Lindstrombba81212001-06-25 05:01:22 +0000353static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000354process_remove_identity(SocketEntry *e)
Damien Millerad833b32000-08-23 10:46:23 +1000355{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000356 int r, success = 0;
357 struct sshkey *key = NULL;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000358 Identity *id;
Damien Miller7e8e8201999-11-16 13:37:16 +1100359
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000360 if ((r = sshkey_froms(e->request, &key)) != 0) {
361 error("%s: get key: %s", __func__, ssh_err(r));
362 goto done;
Damien Millerad833b32000-08-23 10:46:23 +1000363 }
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000364 if ((id = lookup_identity(key)) == NULL) {
365 debug("%s: key not found", __func__);
366 goto done;
Damien Millerad833b32000-08-23 10:46:23 +1000367 }
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000368 /* We have this key, free it. */
369 if (idtab->nentries < 1)
370 fatal("%s: internal error: nentries %d",
371 __func__, idtab->nentries);
372 TAILQ_REMOVE(&idtab->idlist, id, next);
373 free_identity(id);
374 idtab->nentries--;
375 sshkey_free(key);
376 success = 1;
377 done:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000378 send_status(e, success);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000379}
380
Ben Lindstrombba81212001-06-25 05:01:22 +0000381static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000382process_remove_all_identities(SocketEntry *e)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000383{
Damien Miller1a534ae2002-01-22 23:26:13 +1100384 Identity *id;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000385
Damien Miller95def091999-11-25 00:26:21 +1100386 /* Loop over all identities and clear the keys. */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000387 for (id = TAILQ_FIRST(&idtab->idlist); id;
388 id = TAILQ_FIRST(&idtab->idlist)) {
389 TAILQ_REMOVE(&idtab->idlist, id, next);
Damien Miller1a534ae2002-01-22 23:26:13 +1100390 free_identity(id);
Damien Miller95def091999-11-25 00:26:21 +1100391 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000392
Damien Miller95def091999-11-25 00:26:21 +1100393 /* Mark that there are no identities. */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000394 idtab->nentries = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000395
396 /* Send success. */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000397 send_status(e, 1);
Damien Miller95def091999-11-25 00:26:21 +1100398}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000399
Darren Tucker2812dc92007-03-21 20:45:06 +1100400/* removes expired keys and returns number of seconds until the next expiry */
Darren Tucker55119252013-06-02 07:43:59 +1000401static time_t
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000402reaper(void)
403{
Darren Tuckerb759c9c2013-06-02 07:46:16 +1000404 time_t deadline = 0, now = monotime();
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000405 Identity *id, *nxt;
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000406
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000407 for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
408 nxt = TAILQ_NEXT(id, next);
409 if (id->death == 0)
410 continue;
411 if (now >= id->death) {
412 debug("expiring key '%s'", id->comment);
413 TAILQ_REMOVE(&idtab->idlist, id, next);
414 free_identity(id);
415 idtab->nentries--;
416 } else
417 deadline = (deadline == 0) ? id->death :
418 MINIMUM(deadline, id->death);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000419 }
Darren Tucker2812dc92007-03-21 20:45:06 +1100420 if (deadline == 0 || deadline <= now)
421 return 0;
422 else
423 return (deadline - now);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000424}
425
426static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000427process_add_identity(SocketEntry *e)
Damien Miller95def091999-11-25 00:26:21 +1100428{
Damien Miller4c7728c2007-10-26 14:25:31 +1000429 Identity *id;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000430 int success = 0, confirm = 0;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000431 u_int seconds, maxsign;
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000432 char *fp, *comment = NULL, *ext_name = NULL, *sk_provider = NULL;
djm@openbsd.orge5a278a2020-01-25 00:06:48 +0000433 char canonical_provider[PATH_MAX];
Darren Tucker55119252013-06-02 07:43:59 +1000434 time_t death = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000435 struct sshkey *k = NULL;
436 u_char ctype;
437 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller95def091999-11-25 00:26:21 +1100438
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000439 if ((r = sshkey_private_deserialize(e->request, &k)) != 0 ||
440 k == NULL ||
markus@openbsd.org139ca812015-01-14 13:09:09 +0000441 (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) {
442 error("%s: decode private key: %s", __func__, ssh_err(r));
443 goto err;
444 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000445 while (sshbuf_len(e->request)) {
446 if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) {
447 error("%s: buffer error: %s", __func__, ssh_err(r));
448 goto err;
449 }
450 switch (ctype) {
Ben Lindstromc90f8a92002-06-21 00:06:54 +0000451 case SSH_AGENT_CONSTRAIN_LIFETIME:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000452 if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) {
453 error("%s: bad lifetime constraint: %s",
454 __func__, ssh_err(r));
455 goto err;
456 }
457 death = monotime() + seconds;
Ben Lindstrom4eb4c4e2002-06-21 00:04:48 +0000458 break;
Damien Miller6c711792003-01-24 11:36:23 +1100459 case SSH_AGENT_CONSTRAIN_CONFIRM:
460 confirm = 1;
461 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000462 case SSH_AGENT_CONSTRAIN_MAXSIGN:
463 if ((r = sshbuf_get_u32(e->request, &maxsign)) != 0) {
464 error("%s: bad maxsign constraint: %s",
465 __func__, ssh_err(r));
466 goto err;
467 }
468 if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) {
469 error("%s: cannot enable maxsign: %s",
470 __func__, ssh_err(r));
471 goto err;
472 }
473 break;
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000474 case SSH_AGENT_CONSTRAIN_EXTENSION:
475 if ((r = sshbuf_get_cstring(e->request,
476 &ext_name, NULL)) != 0) {
477 error("%s: cannot parse extension: %s",
478 __func__, ssh_err(r));
479 goto err;
480 }
481 debug("%s: constraint ext %s", __func__, ext_name);
482 if (strcmp(ext_name, "sk-provider@openssh.com") == 0) {
483 if (sk_provider != NULL) {
484 error("%s already set", ext_name);
485 goto err;
486 }
487 if ((r = sshbuf_get_cstring(e->request,
488 &sk_provider, NULL)) != 0) {
489 error("%s: cannot parse %s: %s",
490 __func__, ext_name, ssh_err(r));
491 goto err;
492 }
493 } else {
494 error("%s: unsupported constraint \"%s\"",
495 __func__, ext_name);
496 goto err;
497 }
498 free(ext_name);
499 break;
Ben Lindstrom4eb4c4e2002-06-21 00:04:48 +0000500 default:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000501 error("%s: Unknown constraint %d", __func__, ctype);
502 err:
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000503 free(sk_provider);
504 free(ext_name);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000505 sshbuf_reset(e->request);
Darren Tuckera627d422013-06-02 07:31:17 +1000506 free(comment);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000507 sshkey_free(k);
Damien Miller1cfadab2008-06-30 00:05:21 +1000508 goto send;
Ben Lindstrom4eb4c4e2002-06-21 00:04:48 +0000509 }
510 }
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000511 if (sk_provider != NULL) {
markus@openbsd.org2c557442019-11-12 19:33:08 +0000512 if (!sshkey_is_sk(k)) {
naddy@openbsd.orga47f6a62020-02-06 22:30:54 +0000513 error("Cannot add provider: %s is not an "
514 "authenticator-hosted key", sshkey_type(k));
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000515 free(sk_provider);
516 goto send;
517 }
djm@openbsd.orge5a278a2020-01-25 00:06:48 +0000518 if (strcasecmp(sk_provider, "internal") == 0) {
djm@openbsd.orgab360062019-11-15 04:12:32 +0000519 debug("%s: internal provider", __func__);
djm@openbsd.orge5a278a2020-01-25 00:06:48 +0000520 } else {
521 if (realpath(sk_provider, canonical_provider) == NULL) {
522 verbose("failed provider \"%.100s\": "
523 "realpath: %s", sk_provider,
524 strerror(errno));
525 free(sk_provider);
526 goto send;
527 }
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000528 free(sk_provider);
djm@openbsd.orge5a278a2020-01-25 00:06:48 +0000529 sk_provider = xstrdup(canonical_provider);
530 if (match_pattern_list(sk_provider,
531 provider_whitelist, 0) != 1) {
532 error("Refusing add key: "
533 "provider %s not whitelisted", sk_provider);
534 free(sk_provider);
535 goto send;
536 }
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000537 }
538 }
markus@openbsd.orgbf219922019-11-13 07:53:10 +0000539 if ((r = sshkey_shield_private(k)) != 0) {
540 error("%s: shield private key: %s", __func__, ssh_err(r));
541 goto err;
542 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000543
Damien Miller1cfadab2008-06-30 00:05:21 +1000544 success = 1;
Damien Miller53d81482003-01-22 11:47:19 +1100545 if (lifetime && !death)
Darren Tuckerb759c9c2013-06-02 07:46:16 +1000546 death = monotime() + lifetime;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000547 if ((id = lookup_identity(k)) == NULL) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100548 id = xcalloc(1, sizeof(Identity));
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000549 TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000550 /* Increment the number of identities. */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000551 idtab->nentries++;
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000552 } else {
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000553 /* key state might have been updated */
554 sshkey_free(id->key);
Darren Tuckera627d422013-06-02 07:31:17 +1000555 free(id->comment);
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000556 free(id->sk_provider);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000557 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000558 id->key = k;
Damien Miller4c7728c2007-10-26 14:25:31 +1000559 id->comment = comment;
560 id->death = death;
561 id->confirm = confirm;
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000562 id->sk_provider = sk_provider;
563
564 if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT,
565 SSH_FP_DEFAULT)) == NULL)
566 fatal("%s: sshkey_fingerprint failed", __func__);
567 debug("%s: add %s %s \"%.100s\" (life: %u) (confirm: %u) "
568 "(provider: %s)", __func__, sshkey_ssh_name(k), fp, comment,
569 seconds, confirm, sk_provider == NULL ? "none" : sk_provider);
570 free(fp);
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000571send:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000572 send_status(e, success);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000573}
574
Ben Lindstrom2f717042002-06-06 21:52:03 +0000575/* XXX todo: encrypt sensitive data with passphrase */
576static void
577process_lock_agent(SocketEntry *e, int lock)
578{
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000579 int r, success = 0, delay;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000580 char *passwd;
581 u_char passwdhash[LOCK_SIZE];
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000582 static u_int fail_count = 0;
583 size_t pwlen;
Ben Lindstrom2f717042002-06-06 21:52:03 +0000584
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000585 /*
586 * This is deliberately fatal: the user has requested that we lock,
587 * but we can't parse their request properly. The only safe thing to
588 * do is abort.
589 */
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000590 if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0)
markus@openbsd.org139ca812015-01-14 13:09:09 +0000591 fatal("%s: buffer error: %s", __func__, ssh_err(r));
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000592 if (pwlen == 0) {
593 debug("empty password not supported");
594 } else if (locked && !lock) {
595 if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt),
596 passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0)
597 fatal("bcrypt_pbkdf");
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000598 if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE) == 0) {
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000599 debug("agent unlocked");
600 locked = 0;
601 fail_count = 0;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000602 explicit_bzero(lock_pwhash, sizeof(lock_pwhash));
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000603 success = 1;
604 } else {
605 /* delay in 0.1s increments up to 10s */
606 if (fail_count < 100)
607 fail_count++;
608 delay = 100000 * fail_count;
609 debug("unlock failed, delaying %0.1lf seconds",
610 (double)delay/1000000);
611 usleep(delay);
612 }
613 explicit_bzero(passwdhash, sizeof(passwdhash));
Ben Lindstrom2f717042002-06-06 21:52:03 +0000614 } else if (!locked && lock) {
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000615 debug("agent locked");
Ben Lindstrom2f717042002-06-06 21:52:03 +0000616 locked = 1;
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000617 arc4random_buf(lock_salt, sizeof(lock_salt));
618 if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt),
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000619 lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS) < 0)
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000620 fatal("bcrypt_pbkdf");
Ben Lindstrom2f717042002-06-06 21:52:03 +0000621 success = 1;
622 }
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000623 explicit_bzero(passwd, pwlen);
Darren Tuckera627d422013-06-02 07:31:17 +1000624 free(passwd);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000625 send_status(e, success);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000626}
627
628static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000629no_identities(SocketEntry *e)
Ben Lindstrom2f717042002-06-06 21:52:03 +0000630{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000631 struct sshbuf *msg;
632 int r;
Ben Lindstrom2f717042002-06-06 21:52:03 +0000633
markus@openbsd.org139ca812015-01-14 13:09:09 +0000634 if ((msg = sshbuf_new()) == NULL)
635 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000636 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
markus@openbsd.org139ca812015-01-14 13:09:09 +0000637 (r = sshbuf_put_u32(msg, 0)) != 0 ||
638 (r = sshbuf_put_stringb(e->output, msg)) != 0)
639 fatal("%s: buffer error: %s", __func__, ssh_err(r));
640 sshbuf_free(msg);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000641}
Ben Lindstrom3f471632001-07-04 03:53:15 +0000642
Damien Miller7ea845e2010-02-12 09:21:02 +1100643#ifdef ENABLE_PKCS11
Ben Lindstrom3f471632001-07-04 03:53:15 +0000644static void
Damien Miller1cfadab2008-06-30 00:05:21 +1000645process_add_smartcard_key(SocketEntry *e)
Ben Lindstrom3f471632001-07-04 03:53:15 +0000646{
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000647 char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
djm@openbsd.org89a8d452020-01-25 00:03:36 +0000648 char **comments = NULL;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000649 int r, i, count = 0, success = 0, confirm = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000650 u_int seconds;
Darren Tucker55119252013-06-02 07:43:59 +1000651 time_t death = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000652 u_char type;
653 struct sshkey **keys = NULL, *k;
Ben Lindstrom822b6342002-06-23 21:38:49 +0000654 Identity *id;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100655
markus@openbsd.org139ca812015-01-14 13:09:09 +0000656 if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000657 (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) {
658 error("%s: buffer error: %s", __func__, ssh_err(r));
659 goto send;
660 }
Damien Millerd94f20d2003-06-11 22:06:33 +1000661
markus@openbsd.org139ca812015-01-14 13:09:09 +0000662 while (sshbuf_len(e->request)) {
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000663 if ((r = sshbuf_get_u8(e->request, &type)) != 0) {
664 error("%s: buffer error: %s", __func__, ssh_err(r));
665 goto send;
666 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000667 switch (type) {
Damien Millerd94f20d2003-06-11 22:06:33 +1000668 case SSH_AGENT_CONSTRAIN_LIFETIME:
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000669 if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) {
670 error("%s: buffer error: %s",
markus@openbsd.org139ca812015-01-14 13:09:09 +0000671 __func__, ssh_err(r));
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000672 goto send;
673 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000674 death = monotime() + seconds;
Damien Millerd94f20d2003-06-11 22:06:33 +1000675 break;
676 case SSH_AGENT_CONSTRAIN_CONFIRM:
677 confirm = 1;
678 break;
679 default:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000680 error("%s: Unknown constraint type %d", __func__, type);
Damien Miller1cfadab2008-06-30 00:05:21 +1000681 goto send;
Damien Millerd94f20d2003-06-11 22:06:33 +1000682 }
683 }
djm@openbsd.org786d5992016-11-30 03:07:37 +0000684 if (realpath(provider, canonical_provider) == NULL) {
685 verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
686 provider, strerror(errno));
687 goto send;
688 }
djm@openbsd.org07da39f2019-10-31 21:22:01 +0000689 if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) {
djm@openbsd.org786d5992016-11-30 03:07:37 +0000690 verbose("refusing PKCS#11 add of \"%.100s\": "
691 "provider not whitelisted", canonical_provider);
692 goto send;
693 }
694 debug("%s: add %.100s", __func__, canonical_provider);
Damien Millerd94f20d2003-06-11 22:06:33 +1000695 if (lifetime && !death)
Darren Tuckerb759c9c2013-06-02 07:46:16 +1000696 death = monotime() + lifetime;
Damien Millerd94f20d2003-06-11 22:06:33 +1000697
djm@openbsd.org89a8d452020-01-25 00:03:36 +0000698 count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
Damien Miller7ea845e2010-02-12 09:21:02 +1100699 for (i = 0; i < count; i++) {
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000700 k = keys[i];
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000701 if (lookup_identity(k) == NULL) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100702 id = xcalloc(1, sizeof(Identity));
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000703 id->key = k;
djm@openbsd.org89a8d452020-01-25 00:03:36 +0000704 keys[i] = NULL; /* transferred */
djm@openbsd.org786d5992016-11-30 03:07:37 +0000705 id->provider = xstrdup(canonical_provider);
djm@openbsd.org89a8d452020-01-25 00:03:36 +0000706 if (*comments[i] != '\0') {
707 id->comment = comments[i];
708 comments[i] = NULL; /* transferred */
709 } else {
710 id->comment = xstrdup(canonical_provider);
711 }
Damien Millerd94f20d2003-06-11 22:06:33 +1000712 id->death = death;
713 id->confirm = confirm;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000714 TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
715 idtab->nentries++;
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000716 success = 1;
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000717 }
djm@openbsd.org89a8d452020-01-25 00:03:36 +0000718 sshkey_free(keys[i]);
719 free(comments[i]);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000720 }
Ben Lindstrom3f471632001-07-04 03:53:15 +0000721send:
Darren Tuckera627d422013-06-02 07:31:17 +1000722 free(pin);
723 free(provider);
724 free(keys);
djm@openbsd.org89a8d452020-01-25 00:03:36 +0000725 free(comments);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000726 send_status(e, success);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000727}
728
729static void
730process_remove_smartcard_key(SocketEntry *e)
731{
djm@openbsd.org25f83762017-03-15 02:25:09 +0000732 char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000733 int r, success = 0;
Damien Miller7ea845e2010-02-12 09:21:02 +1100734 Identity *id, *nxt;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000735
markus@openbsd.org139ca812015-01-14 13:09:09 +0000736 if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000737 (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) {
738 error("%s: buffer error: %s", __func__, ssh_err(r));
739 goto send;
740 }
Darren Tuckera627d422013-06-02 07:31:17 +1000741 free(pin);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000742
djm@openbsd.org25f83762017-03-15 02:25:09 +0000743 if (realpath(provider, canonical_provider) == NULL) {
744 verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
745 provider, strerror(errno));
746 goto send;
747 }
748
749 debug("%s: remove %.100s", __func__, canonical_provider);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000750 for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
751 nxt = TAILQ_NEXT(id, next);
752 /* Skip file--based keys */
753 if (id->provider == NULL)
754 continue;
755 if (!strcmp(canonical_provider, id->provider)) {
756 TAILQ_REMOVE(&idtab->idlist, id, next);
757 free_identity(id);
758 idtab->nentries--;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000759 }
Ben Lindstrom3f471632001-07-04 03:53:15 +0000760 }
djm@openbsd.org25f83762017-03-15 02:25:09 +0000761 if (pkcs11_del_provider(canonical_provider) == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +1100762 success = 1;
763 else
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000764 error("%s: pkcs11_del_provider failed", __func__);
deraadt@openbsd.org1a321bf2017-03-15 03:52:30 +0000765send:
Darren Tuckera627d422013-06-02 07:31:17 +1000766 free(provider);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000767 send_status(e, success);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000768}
Damien Miller7ea845e2010-02-12 09:21:02 +1100769#endif /* ENABLE_PKCS11 */
Ben Lindstrom3f471632001-07-04 03:53:15 +0000770
Damien Millerad833b32000-08-23 10:46:23 +1000771/* dispatch incoming messages */
772
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000773static int
774process_message(u_int socknum)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000775{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000776 u_int msg_len;
777 u_char type;
778 const u_char *cp;
779 int r;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000780 SocketEntry *e;
781
782 if (socknum >= sockets_alloc) {
783 fatal("%s: socket number %u >= allocated %u",
784 __func__, socknum, sockets_alloc);
785 }
786 e = &sockets[socknum];
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000787
markus@openbsd.org139ca812015-01-14 13:09:09 +0000788 if (sshbuf_len(e->input) < 5)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000789 return 0; /* Incomplete message header. */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000790 cp = sshbuf_ptr(e->input);
791 msg_len = PEEK_U32(cp);
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000792 if (msg_len > AGENT_MAX_LEN) {
793 debug("%s: socket %u (fd=%d) message too long %u > %u",
794 __func__, socknum, e->fd, msg_len, AGENT_MAX_LEN);
795 return -1;
Damien Miller95def091999-11-25 00:26:21 +1100796 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000797 if (sshbuf_len(e->input) < msg_len + 4)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000798 return 0; /* Incomplete message body. */
Ben Lindstrom21d1ed82002-06-06 21:48:57 +0000799
800 /* move the current input to e->request */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000801 sshbuf_reset(e->request);
802 if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 ||
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000803 (r = sshbuf_get_u8(e->request, &type)) != 0) {
804 if (r == SSH_ERR_MESSAGE_INCOMPLETE ||
805 r == SSH_ERR_STRING_TOO_LARGE) {
806 debug("%s: buffer error: %s", __func__, ssh_err(r));
807 return -1;
808 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000809 fatal("%s: buffer error: %s", __func__, ssh_err(r));
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000810 }
811
812 debug("%s: socket %u (fd=%d) type %d", __func__, socknum, e->fd, type);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000813
djm@openbsd.org001aa552018-04-10 00:10:49 +0000814 /* check whether agent is locked */
Ben Lindstrom2f717042002-06-06 21:52:03 +0000815 if (locked && type != SSH_AGENTC_UNLOCK) {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000816 sshbuf_reset(e->request);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000817 switch (type) {
Ben Lindstrom2f717042002-06-06 21:52:03 +0000818 case SSH2_AGENTC_REQUEST_IDENTITIES:
819 /* send empty lists */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000820 no_identities(e);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000821 break;
822 default:
823 /* send a fail message for all other request types */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000824 send_status(e, 0);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000825 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000826 return 0;
Ben Lindstrom2f717042002-06-06 21:52:03 +0000827 }
828
Damien Miller95def091999-11-25 00:26:21 +1100829 switch (type) {
Ben Lindstrom2f717042002-06-06 21:52:03 +0000830 case SSH_AGENTC_LOCK:
831 case SSH_AGENTC_UNLOCK:
832 process_lock_agent(e, type == SSH_AGENTC_LOCK);
833 break;
djm@openbsd.org2f04af92015-03-04 21:12:59 +0000834 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000835 process_remove_all_identities(e); /* safe for !WITH_SSH1 */
djm@openbsd.org2f04af92015-03-04 21:12:59 +0000836 break;
Damien Millerad833b32000-08-23 10:46:23 +1000837 /* ssh2 */
838 case SSH2_AGENTC_SIGN_REQUEST:
839 process_sign_request2(e);
840 break;
841 case SSH2_AGENTC_REQUEST_IDENTITIES:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000842 process_request_identities(e);
Damien Millerad833b32000-08-23 10:46:23 +1000843 break;
844 case SSH2_AGENTC_ADD_IDENTITY:
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000845 case SSH2_AGENTC_ADD_ID_CONSTRAINED:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000846 process_add_identity(e);
Damien Millerad833b32000-08-23 10:46:23 +1000847 break;
848 case SSH2_AGENTC_REMOVE_IDENTITY:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000849 process_remove_identity(e);
Damien Millerad833b32000-08-23 10:46:23 +1000850 break;
851 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000852 process_remove_all_identities(e);
Damien Miller95def091999-11-25 00:26:21 +1100853 break;
Damien Miller7ea845e2010-02-12 09:21:02 +1100854#ifdef ENABLE_PKCS11
Ben Lindstrom3f471632001-07-04 03:53:15 +0000855 case SSH_AGENTC_ADD_SMARTCARD_KEY:
Damien Millerd94f20d2003-06-11 22:06:33 +1000856 case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
Ben Lindstrom3f471632001-07-04 03:53:15 +0000857 process_add_smartcard_key(e);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100858 break;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000859 case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
860 process_remove_smartcard_key(e);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100861 break;
Damien Miller7ea845e2010-02-12 09:21:02 +1100862#endif /* ENABLE_PKCS11 */
Damien Miller95def091999-11-25 00:26:21 +1100863 default:
864 /* Unknown message. Respond with failure. */
865 error("Unknown message %d", type);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000866 sshbuf_reset(e->request);
867 send_status(e, 0);
Damien Miller95def091999-11-25 00:26:21 +1100868 break;
869 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000870 return 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000871}
872
Ben Lindstrombba81212001-06-25 05:01:22 +0000873static void
Ben Lindstrom65366a82001-12-06 16:32:47 +0000874new_socket(sock_type type, int fd)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000875{
Darren Tuckerfb16b242003-09-22 21:04:23 +1000876 u_int i, old_alloc, new_alloc;
Ben Lindstrom822b6342002-06-23 21:38:49 +0000877
Damien Miller232711f2004-06-15 10:35:30 +1000878 set_nonblock(fd);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000879
Damien Miller95def091999-11-25 00:26:21 +1100880 if (fd > max_fd)
881 max_fd = fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000882
Damien Miller95def091999-11-25 00:26:21 +1100883 for (i = 0; i < sockets_alloc; i++)
884 if (sockets[i].type == AUTH_UNUSED) {
885 sockets[i].fd = fd;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000886 if ((sockets[i].input = sshbuf_new()) == NULL)
887 fatal("%s: sshbuf_new failed", __func__);
888 if ((sockets[i].output = sshbuf_new()) == NULL)
889 fatal("%s: sshbuf_new failed", __func__);
890 if ((sockets[i].request = sshbuf_new()) == NULL)
891 fatal("%s: sshbuf_new failed", __func__);
Darren Tuckerfb16b242003-09-22 21:04:23 +1000892 sockets[i].type = type;
Damien Miller95def091999-11-25 00:26:21 +1100893 return;
894 }
895 old_alloc = sockets_alloc;
Darren Tuckerfb16b242003-09-22 21:04:23 +1000896 new_alloc = sockets_alloc + 10;
deraadt@openbsd.org657a5fb2015-04-24 01:36:00 +0000897 sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0]));
Darren Tuckerfb16b242003-09-22 21:04:23 +1000898 for (i = old_alloc; i < new_alloc; i++)
Damien Miller95def091999-11-25 00:26:21 +1100899 sockets[i].type = AUTH_UNUSED;
Darren Tuckerfb16b242003-09-22 21:04:23 +1000900 sockets_alloc = new_alloc;
Damien Miller95def091999-11-25 00:26:21 +1100901 sockets[old_alloc].fd = fd;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000902 if ((sockets[old_alloc].input = sshbuf_new()) == NULL)
903 fatal("%s: sshbuf_new failed", __func__);
904 if ((sockets[old_alloc].output = sshbuf_new()) == NULL)
905 fatal("%s: sshbuf_new failed", __func__);
906 if ((sockets[old_alloc].request = sshbuf_new()) == NULL)
907 fatal("%s: sshbuf_new failed", __func__);
Darren Tuckerfb16b242003-09-22 21:04:23 +1000908 sockets[old_alloc].type = type;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000909}
910
Ben Lindstrombba81212001-06-25 05:01:22 +0000911static int
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000912handle_socket_read(u_int socknum)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000913{
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000914 struct sockaddr_un sunaddr;
915 socklen_t slen;
916 uid_t euid;
917 gid_t egid;
918 int fd;
919
920 slen = sizeof(sunaddr);
921 fd = accept(sockets[socknum].fd, (struct sockaddr *)&sunaddr, &slen);
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +0000922 if (fd == -1) {
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000923 error("accept from AUTH_SOCKET: %s", strerror(errno));
924 return -1;
925 }
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +0000926 if (getpeereid(fd, &euid, &egid) == -1) {
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000927 error("getpeereid %d failed: %s", fd, strerror(errno));
928 close(fd);
929 return -1;
930 }
931 if ((euid != 0) && (getuid() != euid)) {
932 error("uid mismatch: peer euid %u != uid %u",
933 (u_int) euid, (u_int) getuid());
934 close(fd);
935 return -1;
936 }
937 new_socket(AUTH_CONNECTION, fd);
938 return 0;
939}
940
941static int
942handle_conn_read(u_int socknum)
943{
djm@openbsd.orgd6915882019-01-22 22:58:50 +0000944 char buf[AGENT_RBUF_LEN];
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000945 ssize_t len;
946 int r;
947
948 if ((len = read(sockets[socknum].fd, buf, sizeof(buf))) <= 0) {
949 if (len == -1) {
950 if (errno == EAGAIN || errno == EINTR)
951 return 0;
952 error("%s: read error on socket %u (fd %d): %s",
953 __func__, socknum, sockets[socknum].fd,
954 strerror(errno));
955 }
956 return -1;
957 }
958 if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0)
959 fatal("%s: buffer error: %s", __func__, ssh_err(r));
960 explicit_bzero(buf, sizeof(buf));
961 process_message(socknum);
962 return 0;
963}
964
965static int
966handle_conn_write(u_int socknum)
967{
968 ssize_t len;
969 int r;
970
971 if (sshbuf_len(sockets[socknum].output) == 0)
972 return 0; /* shouldn't happen */
973 if ((len = write(sockets[socknum].fd,
974 sshbuf_ptr(sockets[socknum].output),
975 sshbuf_len(sockets[socknum].output))) <= 0) {
976 if (len == -1) {
977 if (errno == EAGAIN || errno == EINTR)
978 return 0;
979 error("%s: read error on socket %u (fd %d): %s",
980 __func__, socknum, sockets[socknum].fd,
981 strerror(errno));
982 }
983 return -1;
984 }
985 if ((r = sshbuf_consume(sockets[socknum].output, len)) != 0)
986 fatal("%s: buffer error: %s", __func__, ssh_err(r));
987 return 0;
988}
989
990static void
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000991after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000992{
993 size_t i;
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000994 u_int socknum, activefds = npfd;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000995
996 for (i = 0; i < npfd; i++) {
997 if (pfd[i].revents == 0)
998 continue;
999 /* Find sockets entry */
1000 for (socknum = 0; socknum < sockets_alloc; socknum++) {
1001 if (sockets[socknum].type != AUTH_SOCKET &&
1002 sockets[socknum].type != AUTH_CONNECTION)
1003 continue;
1004 if (pfd[i].fd == sockets[socknum].fd)
1005 break;
1006 }
1007 if (socknum >= sockets_alloc) {
1008 error("%s: no socket for fd %d", __func__, pfd[i].fd);
1009 continue;
1010 }
1011 /* Process events */
1012 switch (sockets[socknum].type) {
1013 case AUTH_SOCKET:
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001014 if ((pfd[i].revents & (POLLIN|POLLERR)) == 0)
1015 break;
1016 if (npfd > maxfds) {
1017 debug3("out of fds (active %u >= limit %u); "
1018 "skipping accept", activefds, maxfds);
1019 break;
1020 }
1021 if (handle_socket_read(socknum) == 0)
1022 activefds++;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001023 break;
1024 case AUTH_CONNECTION:
1025 if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 &&
1026 handle_conn_read(socknum) != 0) {
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001027 goto close_sock;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001028 }
1029 if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 &&
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001030 handle_conn_write(socknum) != 0) {
1031 close_sock:
1032 if (activefds == 0)
1033 fatal("activefds == 0 at close_sock");
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001034 close_socket(&sockets[socknum]);
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001035 activefds--;
1036 break;
1037 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001038 break;
1039 default:
1040 break;
1041 }
1042 }
1043}
1044
1045static int
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001046prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001047{
1048 struct pollfd *pfd = *pfdp;
1049 size_t i, j, npfd = 0;
Darren Tucker55119252013-06-02 07:43:59 +10001050 time_t deadline;
djm@openbsd.orgd6915882019-01-22 22:58:50 +00001051 int r;
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001052
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001053 /* Count active sockets */
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001054 for (i = 0; i < sockets_alloc; i++) {
Damien Miller95def091999-11-25 00:26:21 +11001055 switch (sockets[i].type) {
1056 case AUTH_SOCKET:
1057 case AUTH_CONNECTION:
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001058 npfd++;
Damien Miller95def091999-11-25 00:26:21 +11001059 break;
1060 case AUTH_UNUSED:
1061 break;
1062 default:
1063 fatal("Unknown socket type %d", sockets[i].type);
1064 break;
1065 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001066 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001067 if (npfd != *npfdp &&
1068 (pfd = recallocarray(pfd, *npfdp, npfd, sizeof(*pfd))) == NULL)
1069 fatal("%s: recallocarray failed", __func__);
1070 *pfdp = pfd;
1071 *npfdp = npfd;
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001072
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001073 for (i = j = 0; i < sockets_alloc; i++) {
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001074 switch (sockets[i].type) {
1075 case AUTH_SOCKET:
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001076 if (npfd > maxfds) {
1077 debug3("out of fds (active %zu >= limit %u); "
1078 "skipping arming listener", npfd, maxfds);
1079 break;
1080 }
1081 pfd[j].fd = sockets[i].fd;
1082 pfd[j].revents = 0;
1083 pfd[j].events = POLLIN;
1084 j++;
1085 break;
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001086 case AUTH_CONNECTION:
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001087 pfd[j].fd = sockets[i].fd;
1088 pfd[j].revents = 0;
djm@openbsd.orgd6915882019-01-22 22:58:50 +00001089 /*
1090 * Only prepare to read if we can handle a full-size
1091 * input read buffer and enqueue a max size reply..
1092 */
1093 if ((r = sshbuf_check_reserve(sockets[i].input,
1094 AGENT_RBUF_LEN)) == 0 &&
1095 (r = sshbuf_check_reserve(sockets[i].output,
1096 AGENT_MAX_LEN)) == 0)
1097 pfd[j].events = POLLIN;
1098 else if (r != SSH_ERR_NO_BUFFER_SPACE) {
1099 fatal("%s: buffer error: %s",
1100 __func__, ssh_err(r));
1101 }
markus@openbsd.org139ca812015-01-14 13:09:09 +00001102 if (sshbuf_len(sockets[i].output) > 0)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001103 pfd[j].events |= POLLOUT;
1104 j++;
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001105 break;
1106 default:
1107 break;
1108 }
1109 }
Darren Tucker2812dc92007-03-21 20:45:06 +11001110 deadline = reaper();
1111 if (parent_alive_interval != 0)
1112 deadline = (deadline == 0) ? parent_alive_interval :
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001113 MINIMUM(deadline, parent_alive_interval);
Darren Tucker2812dc92007-03-21 20:45:06 +11001114 if (deadline == 0) {
djm@openbsd.org9f0e44e2017-07-24 04:34:28 +00001115 *timeoutp = -1; /* INFTIM */
Darren Tucker2812dc92007-03-21 20:45:06 +11001116 } else {
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001117 if (deadline > INT_MAX / 1000)
1118 *timeoutp = INT_MAX / 1000;
1119 else
1120 *timeoutp = deadline * 1000;
Darren Tucker2812dc92007-03-21 20:45:06 +11001121 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001122 return (1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001123}
1124
Ben Lindstrombba81212001-06-25 05:01:22 +00001125static void
Darren Tucker6fa8abd2003-09-22 21:10:21 +10001126cleanup_socket(void)
Damien Miller792c5111999-10-29 09:47:09 +10001127{
Damien Millerb1e967c2014-07-03 21:22:40 +10001128 if (cleanup_pid != 0 && getpid() != cleanup_pid)
1129 return;
1130 debug("%s: cleanup", __func__);
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001131 if (socket_name[0])
1132 unlink(socket_name);
1133 if (socket_dir[0])
1134 rmdir(socket_dir);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001135}
1136
Darren Tucker3e33cec2003-10-02 16:12:36 +10001137void
Damien Miller792c5111999-10-29 09:47:09 +10001138cleanup_exit(int i)
1139{
Darren Tucker6fa8abd2003-09-22 21:10:21 +10001140 cleanup_socket();
1141 _exit(i);
Damien Miller792c5111999-10-29 09:47:09 +10001142}
1143
Damien Miller1c13bd82006-03-26 14:28:14 +11001144/*ARGSUSED*/
Ben Lindstrombba81212001-06-25 05:01:22 +00001145static void
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001146cleanup_handler(int sig)
1147{
Darren Tucker6fa8abd2003-09-22 21:10:21 +10001148 cleanup_socket();
Damien Miller7ea845e2010-02-12 09:21:02 +11001149#ifdef ENABLE_PKCS11
1150 pkcs11_terminate();
1151#endif
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001152 _exit(2);
1153}
1154
Ben Lindstrombba81212001-06-25 05:01:22 +00001155static void
Darren Tucker2812dc92007-03-21 20:45:06 +11001156check_parent_exists(void)
Ben Lindstrom0250da02001-07-22 20:44:00 +00001157{
Darren Tucker3e78a512011-06-03 14:14:16 +10001158 /*
1159 * If our parent has exited then getppid() will return (pid_t)1,
1160 * so testing for that should be safe.
1161 */
1162 if (parent_pid != -1 && getppid() != parent_pid) {
Ben Lindstrom0250da02001-07-22 20:44:00 +00001163 /* printf("Parent has died - Authentication agent exiting.\n"); */
Darren Tucker2812dc92007-03-21 20:45:06 +11001164 cleanup_socket();
1165 _exit(2);
Ben Lindstrom0250da02001-07-22 20:44:00 +00001166 }
Ben Lindstrom0250da02001-07-22 20:44:00 +00001167}
1168
1169static void
Ben Lindstrom28072eb2001-02-10 23:13:41 +00001170usage(void)
Damien Miller792c5111999-10-29 09:47:09 +10001171{
Damien Millerf0858de2014-04-20 13:01:30 +10001172 fprintf(stderr,
jmc@openbsd.org4402d6c2019-11-19 16:02:32 +00001173 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
1174 " [-P provider_whitelist] [-t life] [command [arg ...]]\n"
1175 " ssh-agent [-c | -s] -k\n");
Damien Miller95def091999-11-25 00:26:21 +11001176 exit(1);
Damien Miller792c5111999-10-29 09:47:09 +10001177}
1178
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001179int
1180main(int ac, char **av)
1181{
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001182 int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0;
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001183 int sock, fd, ch, result, saved_errno;
Ben Lindstrom822b6342002-06-23 21:38:49 +00001184 char *shell, *format, *pidstr, *agentsocket = NULL;
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001185#ifdef HAVE_SETRLIMIT
Ben Lindstromc72745a2000-12-02 19:03:54 +00001186 struct rlimit rlim;
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001187#endif
Damien Miller615f9392000-05-17 22:53:33 +10001188 extern int optind;
Ben Lindstrom883844d2002-06-23 00:20:50 +00001189 extern char *optarg;
Ben Lindstrom822b6342002-06-23 21:38:49 +00001190 pid_t pid;
1191 char pidstrbuf[1 + 3 * sizeof pid];
Darren Tucker90133232009-06-21 17:50:15 +10001192 size_t len;
Damien Millerab2ec582014-07-18 15:04:47 +10001193 mode_t prev_mask;
djm@openbsd.org9f0e44e2017-07-24 04:34:28 +00001194 int timeout = -1; /* INFTIM */
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001195 struct pollfd *pfd = NULL;
1196 size_t npfd = 0;
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001197 u_int maxfds;
Kevin Stevesde41bc62000-12-14 00:04:08 +00001198
Darren Tuckerce321d82005-10-03 18:11:24 +10001199 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1200 sanitise_stdfd();
1201
Damien Miller6cffb9a2002-09-04 16:20:26 +10001202 /* drop */
1203 setegid(getgid());
1204 setgid(getgid());
1205
Darren Tucker0fb7f592016-06-09 16:23:07 +10001206 platform_disable_tracing(0); /* strict=no */
Damien Miller6c4914a2004-03-03 11:08:59 +11001207
Darren Tucker7694e9d2019-10-28 17:05:36 +11001208#ifdef RLIMIT_NOFILE
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001209 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
1210 fatal("%s: getrlimit: %s", __progname, strerror(errno));
Darren Tucker7694e9d2019-10-28 17:05:36 +11001211#endif
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001212
Damien Miller59d3d5b2003-08-22 09:34:41 +10001213 __progname = ssh_get_progname(av[0]);
Damien Miller60bc5172001-03-19 09:38:15 +11001214 seed_rng();
Kevin Stevesef4eea92001-02-05 12:42:17 +00001215
djm@openbsd.org786d5992016-11-30 03:07:37 +00001216 while ((ch = getopt(ac, av, "cDdksE:a:P:t:")) != -1) {
Damien Miller95def091999-11-25 00:26:21 +11001217 switch (ch) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001218 case 'E':
1219 fingerprint_hash = ssh_digest_alg_by_name(optarg);
1220 if (fingerprint_hash == -1)
1221 fatal("Invalid hash algorithm \"%s\"", optarg);
1222 break;
Damien Miller95def091999-11-25 00:26:21 +11001223 case 'c':
1224 if (s_flag)
1225 usage();
1226 c_flag++;
1227 break;
1228 case 'k':
1229 k_flag++;
1230 break;
djm@openbsd.org786d5992016-11-30 03:07:37 +00001231 case 'P':
djm@openbsd.org07da39f2019-10-31 21:22:01 +00001232 if (provider_whitelist != NULL)
djm@openbsd.org786d5992016-11-30 03:07:37 +00001233 fatal("-P option already specified");
djm@openbsd.org07da39f2019-10-31 21:22:01 +00001234 provider_whitelist = xstrdup(optarg);
djm@openbsd.org786d5992016-11-30 03:07:37 +00001235 break;
Damien Miller95def091999-11-25 00:26:21 +11001236 case 's':
1237 if (c_flag)
1238 usage();
1239 s_flag++;
1240 break;
Ben Lindstromd94580c2001-07-04 03:48:02 +00001241 case 'd':
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001242 if (d_flag || D_flag)
Ben Lindstromd94580c2001-07-04 03:48:02 +00001243 usage();
1244 d_flag++;
1245 break;
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001246 case 'D':
1247 if (d_flag || D_flag)
1248 usage();
1249 D_flag++;
1250 break;
Ben Lindstromb7788f32002-06-06 21:46:08 +00001251 case 'a':
1252 agentsocket = optarg;
1253 break;
Damien Miller53d81482003-01-22 11:47:19 +11001254 case 't':
1255 if ((lifetime = convtime(optarg)) == -1) {
1256 fprintf(stderr, "Invalid lifetime\n");
1257 usage();
1258 }
1259 break;
Damien Miller95def091999-11-25 00:26:21 +11001260 default:
1261 usage();
1262 }
Damien Miller792c5111999-10-29 09:47:09 +10001263 }
Damien Miller95def091999-11-25 00:26:21 +11001264 ac -= optind;
1265 av += optind;
Damien Miller792c5111999-10-29 09:47:09 +10001266
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001267 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
Damien Miller95def091999-11-25 00:26:21 +11001268 usage();
Damien Miller792c5111999-10-29 09:47:09 +10001269
djm@openbsd.org07da39f2019-10-31 21:22:01 +00001270 if (provider_whitelist == NULL)
1271 provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST);
djm@openbsd.org786d5992016-11-30 03:07:37 +00001272
Ben Lindstromeecdf232002-04-02 21:03:51 +00001273 if (ac == 0 && !c_flag && !s_flag) {
Damien Miller95def091999-11-25 00:26:21 +11001274 shell = getenv("SHELL");
Darren Tucker90133232009-06-21 17:50:15 +10001275 if (shell != NULL && (len = strlen(shell)) > 2 &&
1276 strncmp(shell + len - 3, "csh", 3) == 0)
Damien Miller95def091999-11-25 00:26:21 +11001277 c_flag = 1;
Damien Miller792c5111999-10-29 09:47:09 +10001278 }
Damien Miller95def091999-11-25 00:26:21 +11001279 if (k_flag) {
Damien Miller89c3fe42006-03-31 23:11:28 +11001280 const char *errstr = NULL;
1281
Damien Miller95def091999-11-25 00:26:21 +11001282 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
1283 if (pidstr == NULL) {
1284 fprintf(stderr, "%s not set, cannot kill agent\n",
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001285 SSH_AGENTPID_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +11001286 exit(1);
1287 }
Damien Miller89c3fe42006-03-31 23:11:28 +11001288 pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr);
1289 if (errstr) {
1290 fprintf(stderr,
1291 "%s=\"%s\", which is not a good PID: %s\n",
1292 SSH_AGENTPID_ENV_NAME, pidstr, errstr);
Damien Miller95def091999-11-25 00:26:21 +11001293 exit(1);
1294 }
1295 if (kill(pid, SIGTERM) == -1) {
1296 perror("kill");
1297 exit(1);
1298 }
1299 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
1300 printf(format, SSH_AUTHSOCKET_ENV_NAME);
1301 printf(format, SSH_AGENTPID_ENV_NAME);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001302 printf("echo Agent pid %ld killed;\n", (long)pid);
Damien Miller95def091999-11-25 00:26:21 +11001303 exit(0);
Damien Miller792c5111999-10-29 09:47:09 +10001304 }
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001305
1306 /*
1307 * Minimum file descriptors:
1308 * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) +
1309 * a few spare for libc / stack protectors / sanitisers, etc.
1310 */
1311#define SSH_AGENT_MIN_FDS (3+1+1+1+4)
1312 if (rlim.rlim_cur < SSH_AGENT_MIN_FDS)
djm@openbsd.org960e7c62018-11-09 02:57:58 +00001313 fatal("%s: file descriptor rlimit %lld too low (minimum %u)",
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001314 __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS);
1315 maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS;
1316
Damien Miller95def091999-11-25 00:26:21 +11001317 parent_pid = getpid();
1318
Ben Lindstromb7788f32002-06-06 21:46:08 +00001319 if (agentsocket == NULL) {
1320 /* Create private directory for agent socket */
Damien Miller2cd62932010-12-01 11:50:35 +11001321 mktemp_proto(socket_dir, sizeof(socket_dir));
Ben Lindstromb7788f32002-06-06 21:46:08 +00001322 if (mkdtemp(socket_dir) == NULL) {
1323 perror("mkdtemp: private socket dir");
1324 exit(1);
1325 }
Ben Lindstromce0f6342002-06-11 16:42:49 +00001326 snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
1327 (long)parent_pid);
Ben Lindstromb7788f32002-06-06 21:46:08 +00001328 } else {
1329 /* Try to use specified agent socket */
1330 socket_dir[0] = '\0';
1331 strlcpy(socket_name, agentsocket, sizeof socket_name);
Damien Miller792c5111999-10-29 09:47:09 +10001332 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001333
Damien Miller5428f641999-11-25 11:54:57 +11001334 /*
1335 * Create socket early so it will exist before command gets run from
1336 * the parent.
1337 */
Damien Millerab2ec582014-07-18 15:04:47 +10001338 prev_mask = umask(0177);
Damien Miller7acefbb2014-07-18 14:11:24 +10001339 sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0);
Damien Miller95def091999-11-25 00:26:21 +11001340 if (sock < 0) {
Damien Miller7acefbb2014-07-18 14:11:24 +10001341 /* XXX - unix_listener() calls error() not perror() */
Darren Tucker1dee8682004-11-05 20:26:49 +11001342 *socket_name = '\0'; /* Don't unlink any existing file */
Damien Miller95def091999-11-25 00:26:21 +11001343 cleanup_exit(1);
Damien Miller792c5111999-10-29 09:47:09 +10001344 }
Damien Millerab2ec582014-07-18 15:04:47 +10001345 umask(prev_mask);
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001346
Damien Miller5428f641999-11-25 11:54:57 +11001347 /*
1348 * Fork, and have the parent execute the command, if any, or present
1349 * the socket data. The child continues as the authentication agent.
1350 */
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001351 if (D_flag || d_flag) {
1352 log_init(__progname,
1353 d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
1354 SYSLOG_FACILITY_AUTH, 1);
Ben Lindstromd94580c2001-07-04 03:48:02 +00001355 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
1356 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
1357 SSH_AUTHSOCKET_ENV_NAME);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001358 printf("echo Agent pid %ld;\n", (long)parent_pid);
dtucker@openbsd.org79394ed2015-12-11 02:29:03 +00001359 fflush(stdout);
Ben Lindstromd94580c2001-07-04 03:48:02 +00001360 goto skip;
1361 }
Damien Miller95def091999-11-25 00:26:21 +11001362 pid = fork();
1363 if (pid == -1) {
1364 perror("fork");
Damien Millerc653b892002-02-08 22:05:41 +11001365 cleanup_exit(1);
Damien Miller95def091999-11-25 00:26:21 +11001366 }
1367 if (pid != 0) { /* Parent - execute the given command. */
1368 close(sock);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001369 snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid);
Damien Miller95def091999-11-25 00:26:21 +11001370 if (ac == 0) {
1371 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
1372 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001373 SSH_AUTHSOCKET_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +11001374 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001375 SSH_AGENTPID_ENV_NAME);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001376 printf("echo Agent pid %ld;\n", (long)pid);
Damien Miller95def091999-11-25 00:26:21 +11001377 exit(0);
1378 }
Damien Millere4340be2000-09-16 13:29:08 +11001379 if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
1380 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) {
1381 perror("setenv");
1382 exit(1);
1383 }
Damien Miller95def091999-11-25 00:26:21 +11001384 execvp(av[0], av);
1385 perror(av[0]);
1386 exit(1);
1387 }
Damien Millerc653b892002-02-08 22:05:41 +11001388 /* child */
1389 log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrom3fdf8762001-07-22 20:40:24 +00001390
1391 if (setsid() == -1) {
Damien Millerc653b892002-02-08 22:05:41 +11001392 error("setsid: %s", strerror(errno));
Ben Lindstrom3fdf8762001-07-22 20:40:24 +00001393 cleanup_exit(1);
1394 }
1395
1396 (void)chdir("/");
Damien Miller6c711792003-01-24 11:36:23 +11001397 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
1398 /* XXX might close listen socket */
1399 (void)dup2(fd, STDIN_FILENO);
1400 (void)dup2(fd, STDOUT_FILENO);
1401 (void)dup2(fd, STDERR_FILENO);
1402 if (fd > 2)
1403 close(fd);
1404 }
Damien Miller95def091999-11-25 00:26:21 +11001405
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001406#ifdef HAVE_SETRLIMIT
Ben Lindstromc72745a2000-12-02 19:03:54 +00001407 /* deny core dumps, since memory contains unencrypted private keys */
1408 rlim.rlim_cur = rlim.rlim_max = 0;
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +00001409 if (setrlimit(RLIMIT_CORE, &rlim) == -1) {
Damien Millerc653b892002-02-08 22:05:41 +11001410 error("setrlimit RLIMIT_CORE: %s", strerror(errno));
Ben Lindstromc72745a2000-12-02 19:03:54 +00001411 cleanup_exit(1);
1412 }
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001413#endif
Ben Lindstromd94580c2001-07-04 03:48:02 +00001414
1415skip:
Damien Miller7ea845e2010-02-12 09:21:02 +11001416
Damien Millerb1e967c2014-07-03 21:22:40 +10001417 cleanup_pid = getpid();
1418
Damien Miller7ea845e2010-02-12 09:21:02 +11001419#ifdef ENABLE_PKCS11
1420 pkcs11_init(0);
1421#endif
Damien Miller95def091999-11-25 00:26:21 +11001422 new_socket(AUTH_SOCKET, sock);
Darren Tucker2812dc92007-03-21 20:45:06 +11001423 if (ac > 0)
1424 parent_alive_interval = 10;
Damien Millerad833b32000-08-23 10:46:23 +10001425 idtab_init();
dtucker@openbsd.org3bf2a6a2020-01-23 07:10:22 +00001426 ssh_signal(SIGPIPE, SIG_IGN);
1427 ssh_signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN);
1428 ssh_signal(SIGHUP, cleanup_handler);
1429 ssh_signal(SIGTERM, cleanup_handler);
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +00001430
djm@openbsd.org786d5992016-11-30 03:07:37 +00001431 if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1)
djm@openbsd.orgd9521622015-12-01 23:29:24 +00001432 fatal("%s: pledge: %s", __progname, strerror(errno));
Damien Miller4626cba2016-01-08 14:24:56 +11001433 platform_pledge_agent();
djm@openbsd.orgd9521622015-12-01 23:29:24 +00001434
Damien Miller95def091999-11-25 00:26:21 +11001435 while (1) {
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001436 prepare_poll(&pfd, &npfd, &timeout, maxfds);
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001437 result = poll(pfd, npfd, timeout);
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001438 saved_errno = errno;
Darren Tucker2812dc92007-03-21 20:45:06 +11001439 if (parent_alive_interval != 0)
1440 check_parent_exists();
1441 (void) reaper(); /* remove expired keys */
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +00001442 if (result == -1) {
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001443 if (saved_errno == EINTR)
Damien Miller95def091999-11-25 00:26:21 +11001444 continue;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001445 fatal("poll: %s", strerror(saved_errno));
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001446 } else if (result > 0)
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001447 after_poll(pfd, npfd, maxfds);
Damien Miller95def091999-11-25 00:26:21 +11001448 }
1449 /* NOTREACHED */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001450}