blob: 9c6680a2504c74e9f37f00b69fd41c455a00a5b5 [file] [log] [blame]
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +00001/* $OpenBSD: ssh-agent.c,v 1.237 2019/06/28 13:35:04 deraadt 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>
Damien Miller9aec9192006-08-05 10:57:45 +100044#ifdef HAVE_SYS_TIME_H
45# include <sys/time.h>
46#endif
Damien Miller574c41f2006-03-15 11:40:10 +110047#ifdef HAVE_SYS_UN_H
48# include <sys/un.h>
49#endif
Damien Miller9b481512002-09-12 10:43:29 +100050#include "openbsd-compat/sys-queue.h"
Damien Millere3b60b52006-07-10 21:08:03 +100051
Damien Miller1f0311c2014-05-15 14:24:09 +100052#ifdef WITH_OPENSSL
Damien Millere3476ed2006-07-24 14:13:33 +100053#include <openssl/evp.h>
Darren Tuckerbfaaf962008-02-28 19:13:52 +110054#include "openbsd-compat/openssl-compat.h"
Damien Miller1f0311c2014-05-15 14:24:09 +100055#endif
Damien Millere3476ed2006-07-24 14:13:33 +100056
Darren Tucker39972492006-07-12 22:22:46 +100057#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100058#include <fcntl.h>
deraadt@openbsd.org2ae4f332015-01-16 06:40:12 +000059#include <limits.h>
Damien Miller03e20032006-03-15 11:16:59 +110060#ifdef HAVE_PATHS_H
Damien Millera9263d02006-03-15 11:18:26 +110061# include <paths.h>
Damien Miller03e20032006-03-15 11:16:59 +110062#endif
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +000063#ifdef HAVE_POLL_H
64# include <poll.h>
65#endif
Damien Miller6ff3cad2006-03-15 11:52:09 +110066#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100067#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100068#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100069#include <stdlib.h>
Damien Miller5598b4f2006-07-24 14:09:40 +100070#include <time.h>
Damien Millere3476ed2006-07-24 14:13:33 +100071#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100072#include <unistd.h>
Damien Millere97201f2015-05-21 17:55:15 +100073#ifdef HAVE_UTIL_H
74# include <util.h>
75#endif
Damien Miller6ff3cad2006-03-15 11:52:09 +110076
Damien Millerd7834352006-08-05 12:39:39 +100077#include "xmalloc.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100078#include "ssh.h"
markus@openbsd.org139ca812015-01-14 13:09:09 +000079#include "sshbuf.h"
80#include "sshkey.h"
Damien Miller994cf142000-07-21 10:19:44 +100081#include "authfd.h"
Damien Miller62cee002000-09-23 17:15:56 +110082#include "compat.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000083#include "log.h"
Damien Miller6c711792003-01-24 11:36:23 +110084#include "misc.h"
Damien Miller4a1c7aa2014-02-04 11:03:36 +110085#include "digest.h"
markus@openbsd.org139ca812015-01-14 13:09:09 +000086#include "ssherr.h"
djm@openbsd.org786d5992016-11-30 03:07:37 +000087#include "match.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100088
Damien Miller7ea845e2010-02-12 09:21:02 +110089#ifdef ENABLE_PKCS11
90#include "ssh-pkcs11.h"
Ben Lindstrombcc18082001-08-06 21:59:25 +000091#endif
Ben Lindstrom3f471632001-07-04 03:53:15 +000092
djm@openbsd.org786d5992016-11-30 03:07:37 +000093#ifndef DEFAULT_PKCS11_WHITELIST
djm@openbsd.orgb108ce92017-01-04 02:21:43 +000094# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*"
djm@openbsd.org786d5992016-11-30 03:07:37 +000095#endif
96
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +000097/* Maximum accepted message length */
98#define AGENT_MAX_LEN (256*1024)
djm@openbsd.orgd6915882019-01-22 22:58:50 +000099/* Maximum bytes to read from client socket */
100#define AGENT_RBUF_LEN (4096)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000101
Ben Lindstrom65366a82001-12-06 16:32:47 +0000102typedef enum {
103 AUTH_UNUSED,
104 AUTH_SOCKET,
105 AUTH_CONNECTION
106} sock_type;
107
Damien Miller95def091999-11-25 00:26:21 +1100108typedef struct {
109 int fd;
Ben Lindstrom65366a82001-12-06 16:32:47 +0000110 sock_type type;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000111 struct sshbuf *input;
112 struct sshbuf *output;
113 struct sshbuf *request;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000114} SocketEntry;
115
Ben Lindstrom46c16222000-12-22 01:43:59 +0000116u_int sockets_alloc = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000117SocketEntry *sockets = NULL;
118
Damien Miller1a534ae2002-01-22 23:26:13 +1100119typedef struct identity {
120 TAILQ_ENTRY(identity) next;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000121 struct sshkey *key;
Damien Miller95def091999-11-25 00:26:21 +1100122 char *comment;
Damien Miller7ea845e2010-02-12 09:21:02 +1100123 char *provider;
Darren Tucker55119252013-06-02 07:43:59 +1000124 time_t death;
Damien Miller6c711792003-01-24 11:36:23 +1100125 u_int confirm;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000126} Identity;
127
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000128struct idtable {
Damien Millerad833b32000-08-23 10:46:23 +1000129 int nentries;
Damien Miller1a534ae2002-01-22 23:26:13 +1100130 TAILQ_HEAD(idqueue, identity) idlist;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000131};
Damien Millerad833b32000-08-23 10:46:23 +1000132
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000133/* private key table */
134struct idtable *idtab;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000135
136int max_fd = 0;
137
138/* pid of shell == parent of agent */
Damien Miller166fca82000-04-20 07:42:21 +1000139pid_t parent_pid = -1;
Darren Tucker073f7952013-06-02 23:47:11 +1000140time_t parent_alive_interval = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000141
Damien Millerb1e967c2014-07-03 21:22:40 +1000142/* pid of process for which cleanup_socket is applicable */
143pid_t cleanup_pid = 0;
144
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000145/* pathname and directory for AUTH_SOCKET */
deraadt@openbsd.org2ae4f332015-01-16 06:40:12 +0000146char socket_name[PATH_MAX];
147char socket_dir[PATH_MAX];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000148
djm@openbsd.org786d5992016-11-30 03:07:37 +0000149/* PKCS#11 path whitelist */
150static char *pkcs11_whitelist;
151
Ben Lindstrom2f717042002-06-06 21:52:03 +0000152/* locking */
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000153#define LOCK_SIZE 32
154#define LOCK_SALT_SIZE 16
155#define LOCK_ROUNDS 1
Ben Lindstrom2f717042002-06-06 21:52:03 +0000156int locked = 0;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000157u_char lock_pwhash[LOCK_SIZE];
158u_char lock_salt[LOCK_SALT_SIZE];
Ben Lindstrom2f717042002-06-06 21:52:03 +0000159
Damien Miller95def091999-11-25 00:26:21 +1100160extern char *__progname;
Damien Miller95def091999-11-25 00:26:21 +1100161
Darren Tucker55119252013-06-02 07:43:59 +1000162/* Default lifetime in seconds (0 == forever) */
163static long lifetime = 0;
Damien Miller53d81482003-01-22 11:47:19 +1100164
djm@openbsd.org56d1c832014-12-21 22:27:55 +0000165static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
166
Ben Lindstrombba81212001-06-25 05:01:22 +0000167static void
Damien Miller58f34862002-09-04 16:31:21 +1000168close_socket(SocketEntry *e)
169{
Damien Miller58f34862002-09-04 16:31:21 +1000170 close(e->fd);
171 e->fd = -1;
172 e->type = AUTH_UNUSED;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000173 sshbuf_free(e->input);
174 sshbuf_free(e->output);
175 sshbuf_free(e->request);
Damien Miller58f34862002-09-04 16:31:21 +1000176}
177
178static void
Damien Millerad833b32000-08-23 10:46:23 +1000179idtab_init(void)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000180{
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000181 idtab = xcalloc(1, sizeof(*idtab));
182 TAILQ_INIT(&idtab->idlist);
183 idtab->nentries = 0;
Damien Millerad833b32000-08-23 10:46:23 +1000184}
185
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000186static void
187free_identity(Identity *id)
188{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000189 sshkey_free(id->key);
Darren Tuckera627d422013-06-02 07:31:17 +1000190 free(id->provider);
191 free(id->comment);
192 free(id);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000193}
194
Damien Millerad833b32000-08-23 10:46:23 +1000195/* return matching private key for given public key */
Damien Miller1a534ae2002-01-22 23:26:13 +1100196static Identity *
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000197lookup_identity(struct sshkey *key)
Damien Millerad833b32000-08-23 10:46:23 +1000198{
Damien Miller1a534ae2002-01-22 23:26:13 +1100199 Identity *id;
200
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000201 TAILQ_FOREACH(id, &idtab->idlist, next) {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000202 if (sshkey_equal(key, id->key))
Damien Miller1a534ae2002-01-22 23:26:13 +1100203 return (id);
Damien Millerad833b32000-08-23 10:46:23 +1000204 }
Damien Miller1a534ae2002-01-22 23:26:13 +1100205 return (NULL);
206}
207
Damien Miller6c711792003-01-24 11:36:23 +1100208/* Check confirmation of keysign request */
209static int
210confirm_key(Identity *id)
211{
Darren Tuckerce327b62004-11-05 20:38:03 +1100212 char *p;
Damien Miller6c711792003-01-24 11:36:23 +1100213 int ret = -1;
214
markus@openbsd.org139ca812015-01-14 13:09:09 +0000215 p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT);
djm@openbsd.org9ce86c92015-01-28 22:36:00 +0000216 if (p != NULL &&
217 ask_permission("Allow use of key %s?\nKey fingerprint %s.",
Darren Tuckerce327b62004-11-05 20:38:03 +1100218 id->comment, p))
219 ret = 0;
Darren Tuckera627d422013-06-02 07:31:17 +1000220 free(p);
Darren Tuckerce327b62004-11-05 20:38:03 +1100221
Damien Miller6c711792003-01-24 11:36:23 +1100222 return (ret);
223}
224
markus@openbsd.org139ca812015-01-14 13:09:09 +0000225static void
226send_status(SocketEntry *e, int success)
227{
228 int r;
229
230 if ((r = sshbuf_put_u32(e->output, 1)) != 0 ||
231 (r = sshbuf_put_u8(e->output, success ?
232 SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0)
233 fatal("%s: buffer error: %s", __func__, ssh_err(r));
234}
235
Damien Millerad833b32000-08-23 10:46:23 +1000236/* send list of supported public keys to 'client' */
Ben Lindstrombba81212001-06-25 05:01:22 +0000237static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000238process_request_identities(SocketEntry *e)
Damien Millerad833b32000-08-23 10:46:23 +1000239{
Damien Miller1a534ae2002-01-22 23:26:13 +1100240 Identity *id;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000241 struct sshbuf *msg;
242 int r;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000243
markus@openbsd.org139ca812015-01-14 13:09:09 +0000244 if ((msg = sshbuf_new()) == NULL)
245 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000246 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
247 (r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
markus@openbsd.org139ca812015-01-14 13:09:09 +0000248 fatal("%s: buffer error: %s", __func__, ssh_err(r));
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000249 TAILQ_FOREACH(id, &idtab->idlist, next) {
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000250 if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO))
251 != 0 ||
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000252 (r = sshbuf_put_cstring(msg, id->comment)) != 0) {
253 error("%s: put key/comment: %s", __func__,
djm@openbsd.org873d3e72017-04-30 23:18:44 +0000254 ssh_err(r));
255 continue;
Damien Millerad833b32000-08-23 10:46:23 +1000256 }
Damien Miller95def091999-11-25 00:26:21 +1100257 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000258 if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
259 fatal("%s: buffer error: %s", __func__, ssh_err(r));
260 sshbuf_free(msg);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000261}
262
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000263
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000264static char *
265agent_decode_alg(struct sshkey *key, u_int flags)
266{
267 if (key->type == KEY_RSA) {
268 if (flags & SSH_AGENT_RSA_SHA2_256)
269 return "rsa-sha2-256";
270 else if (flags & SSH_AGENT_RSA_SHA2_512)
271 return "rsa-sha2-512";
djm@openbsd.org2317ce42019-06-14 03:51:47 +0000272 } else if (key->type == KEY_RSA_CERT) {
273 if (flags & SSH_AGENT_RSA_SHA2_256)
274 return "rsa-sha2-256-cert-v01@openssh.com";
275 else if (flags & SSH_AGENT_RSA_SHA2_512)
276 return "rsa-sha2-512-cert-v01@openssh.com";
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000277 }
278 return NULL;
279}
280
Damien Millerad833b32000-08-23 10:46:23 +1000281/* ssh2 only */
Ben Lindstrombba81212001-06-25 05:01:22 +0000282static void
Damien Millerad833b32000-08-23 10:46:23 +1000283process_sign_request2(SocketEntry *e)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000284{
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000285 const u_char *data;
286 u_char *signature = NULL;
287 size_t dlen, slen = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000288 u_int compat = 0, flags;
289 int r, ok = -1;
290 struct sshbuf *msg;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000291 struct sshkey *key = NULL;
djm@openbsd.org0088c572015-01-14 19:33:41 +0000292 struct identity *id;
Damien Millerad833b32000-08-23 10:46:23 +1000293
djm@openbsd.org0088c572015-01-14 19:33:41 +0000294 if ((msg = sshbuf_new()) == NULL)
295 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000296 if ((r = sshkey_froms(e->request, &key)) != 0 ||
297 (r = sshbuf_get_string_direct(e->request, &data, &dlen)) != 0 ||
djm@openbsd.org@openbsd.org93c68a82017-11-15 00:13:40 +0000298 (r = sshbuf_get_u32(e->request, &flags)) != 0) {
299 error("%s: couldn't parse request: %s", __func__, ssh_err(r));
300 goto send;
301 }
302
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000303 if ((id = lookup_identity(key)) == NULL) {
djm@openbsd.org0088c572015-01-14 19:33:41 +0000304 verbose("%s: %s key not found", __func__, sshkey_type(key));
305 goto send;
306 }
307 if (id->confirm && confirm_key(id) != 0) {
308 verbose("%s: user refused key", __func__);
309 goto send;
310 }
311 if ((r = sshkey_sign(id->key, &signature, &slen,
markus@openbsd.org76c9fbb2015-12-04 16:41:28 +0000312 data, dlen, agent_decode_alg(key, flags), compat)) != 0) {
djm@openbsd.org39736be2015-12-11 02:20:28 +0000313 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
djm@openbsd.org0088c572015-01-14 19:33:41 +0000314 goto send;
315 }
316 /* Success */
317 ok = 0;
318 send:
319 sshkey_free(key);
Damien Millerad833b32000-08-23 10:46:23 +1000320 if (ok == 0) {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000321 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 ||
322 (r = sshbuf_put_string(msg, signature, slen)) != 0)
323 fatal("%s: buffer error: %s", __func__, ssh_err(r));
324 } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
325 fatal("%s: buffer error: %s", __func__, ssh_err(r));
326
327 if ((r = sshbuf_put_stringb(e->output, msg)) != 0)
328 fatal("%s: buffer error: %s", __func__, ssh_err(r));
329
330 sshbuf_free(msg);
Darren Tuckera627d422013-06-02 07:31:17 +1000331 free(signature);
Damien Millerad833b32000-08-23 10:46:23 +1000332}
333
334/* shared */
Ben Lindstrombba81212001-06-25 05:01:22 +0000335static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000336process_remove_identity(SocketEntry *e)
Damien Millerad833b32000-08-23 10:46:23 +1000337{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000338 int r, success = 0;
339 struct sshkey *key = NULL;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000340 Identity *id;
Damien Miller7e8e8201999-11-16 13:37:16 +1100341
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000342 if ((r = sshkey_froms(e->request, &key)) != 0) {
343 error("%s: get key: %s", __func__, ssh_err(r));
344 goto done;
Damien Millerad833b32000-08-23 10:46:23 +1000345 }
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000346 if ((id = lookup_identity(key)) == NULL) {
347 debug("%s: key not found", __func__);
348 goto done;
Damien Millerad833b32000-08-23 10:46:23 +1000349 }
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000350 /* We have this key, free it. */
351 if (idtab->nentries < 1)
352 fatal("%s: internal error: nentries %d",
353 __func__, idtab->nentries);
354 TAILQ_REMOVE(&idtab->idlist, id, next);
355 free_identity(id);
356 idtab->nentries--;
357 sshkey_free(key);
358 success = 1;
359 done:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000360 send_status(e, success);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000361}
362
Ben Lindstrombba81212001-06-25 05:01:22 +0000363static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000364process_remove_all_identities(SocketEntry *e)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000365{
Damien Miller1a534ae2002-01-22 23:26:13 +1100366 Identity *id;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000367
Damien Miller95def091999-11-25 00:26:21 +1100368 /* Loop over all identities and clear the keys. */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000369 for (id = TAILQ_FIRST(&idtab->idlist); id;
370 id = TAILQ_FIRST(&idtab->idlist)) {
371 TAILQ_REMOVE(&idtab->idlist, id, next);
Damien Miller1a534ae2002-01-22 23:26:13 +1100372 free_identity(id);
Damien Miller95def091999-11-25 00:26:21 +1100373 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000374
Damien Miller95def091999-11-25 00:26:21 +1100375 /* Mark that there are no identities. */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000376 idtab->nentries = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000377
378 /* Send success. */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000379 send_status(e, 1);
Damien Miller95def091999-11-25 00:26:21 +1100380}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000381
Darren Tucker2812dc92007-03-21 20:45:06 +1100382/* removes expired keys and returns number of seconds until the next expiry */
Darren Tucker55119252013-06-02 07:43:59 +1000383static time_t
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000384reaper(void)
385{
Darren Tuckerb759c9c2013-06-02 07:46:16 +1000386 time_t deadline = 0, now = monotime();
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000387 Identity *id, *nxt;
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000388
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000389 for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
390 nxt = TAILQ_NEXT(id, next);
391 if (id->death == 0)
392 continue;
393 if (now >= id->death) {
394 debug("expiring key '%s'", id->comment);
395 TAILQ_REMOVE(&idtab->idlist, id, next);
396 free_identity(id);
397 idtab->nentries--;
398 } else
399 deadline = (deadline == 0) ? id->death :
400 MINIMUM(deadline, id->death);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000401 }
Darren Tucker2812dc92007-03-21 20:45:06 +1100402 if (deadline == 0 || deadline <= now)
403 return 0;
404 else
405 return (deadline - now);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000406}
407
408static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000409process_add_identity(SocketEntry *e)
Damien Miller95def091999-11-25 00:26:21 +1100410{
Damien Miller4c7728c2007-10-26 14:25:31 +1000411 Identity *id;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000412 int success = 0, confirm = 0;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000413 u_int seconds, maxsign;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000414 char *comment = NULL;
Darren Tucker55119252013-06-02 07:43:59 +1000415 time_t death = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000416 struct sshkey *k = NULL;
417 u_char ctype;
418 int r = SSH_ERR_INTERNAL_ERROR;
Damien Miller95def091999-11-25 00:26:21 +1100419
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000420 if ((r = sshkey_private_deserialize(e->request, &k)) != 0 ||
421 k == NULL ||
markus@openbsd.org139ca812015-01-14 13:09:09 +0000422 (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) {
423 error("%s: decode private key: %s", __func__, ssh_err(r));
424 goto err;
425 }
djm@openbsd.org4f7a56d2019-06-21 04:21:04 +0000426 if ((r = sshkey_shield_private(k)) != 0) {
427 error("%s: shield private key: %s", __func__, ssh_err(r));
428 goto err;
429 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000430 while (sshbuf_len(e->request)) {
431 if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) {
432 error("%s: buffer error: %s", __func__, ssh_err(r));
433 goto err;
434 }
435 switch (ctype) {
Ben Lindstromc90f8a92002-06-21 00:06:54 +0000436 case SSH_AGENT_CONSTRAIN_LIFETIME:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000437 if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) {
438 error("%s: bad lifetime constraint: %s",
439 __func__, ssh_err(r));
440 goto err;
441 }
442 death = monotime() + seconds;
Ben Lindstrom4eb4c4e2002-06-21 00:04:48 +0000443 break;
Damien Miller6c711792003-01-24 11:36:23 +1100444 case SSH_AGENT_CONSTRAIN_CONFIRM:
445 confirm = 1;
446 break;
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000447 case SSH_AGENT_CONSTRAIN_MAXSIGN:
448 if ((r = sshbuf_get_u32(e->request, &maxsign)) != 0) {
449 error("%s: bad maxsign constraint: %s",
450 __func__, ssh_err(r));
451 goto err;
452 }
453 if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) {
454 error("%s: cannot enable maxsign: %s",
455 __func__, ssh_err(r));
456 goto err;
457 }
458 break;
Ben Lindstrom4eb4c4e2002-06-21 00:04:48 +0000459 default:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000460 error("%s: Unknown constraint %d", __func__, ctype);
461 err:
462 sshbuf_reset(e->request);
Darren Tuckera627d422013-06-02 07:31:17 +1000463 free(comment);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000464 sshkey_free(k);
Damien Miller1cfadab2008-06-30 00:05:21 +1000465 goto send;
Ben Lindstrom4eb4c4e2002-06-21 00:04:48 +0000466 }
467 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000468
Damien Miller1cfadab2008-06-30 00:05:21 +1000469 success = 1;
Damien Miller53d81482003-01-22 11:47:19 +1100470 if (lifetime && !death)
Darren Tuckerb759c9c2013-06-02 07:46:16 +1000471 death = monotime() + lifetime;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000472 if ((id = lookup_identity(k)) == NULL) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100473 id = xcalloc(1, sizeof(Identity));
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000474 TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000475 /* Increment the number of identities. */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000476 idtab->nentries++;
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000477 } else {
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000478 /* key state might have been updated */
479 sshkey_free(id->key);
Darren Tuckera627d422013-06-02 07:31:17 +1000480 free(id->comment);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000481 }
markus@openbsd.org1b11ea72018-02-23 15:58:37 +0000482 id->key = k;
Damien Miller4c7728c2007-10-26 14:25:31 +1000483 id->comment = comment;
484 id->death = death;
485 id->confirm = confirm;
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000486send:
markus@openbsd.org139ca812015-01-14 13:09:09 +0000487 send_status(e, success);
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000488}
489
Ben Lindstrom2f717042002-06-06 21:52:03 +0000490/* XXX todo: encrypt sensitive data with passphrase */
491static void
492process_lock_agent(SocketEntry *e, int lock)
493{
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000494 int r, success = 0, delay;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000495 char *passwd;
496 u_char passwdhash[LOCK_SIZE];
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000497 static u_int fail_count = 0;
498 size_t pwlen;
Ben Lindstrom2f717042002-06-06 21:52:03 +0000499
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000500 /*
501 * This is deliberately fatal: the user has requested that we lock,
502 * but we can't parse their request properly. The only safe thing to
503 * do is abort.
504 */
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000505 if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0)
markus@openbsd.org139ca812015-01-14 13:09:09 +0000506 fatal("%s: buffer error: %s", __func__, ssh_err(r));
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000507 if (pwlen == 0) {
508 debug("empty password not supported");
509 } else if (locked && !lock) {
510 if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt),
511 passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0)
512 fatal("bcrypt_pbkdf");
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000513 if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE) == 0) {
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000514 debug("agent unlocked");
515 locked = 0;
516 fail_count = 0;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000517 explicit_bzero(lock_pwhash, sizeof(lock_pwhash));
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000518 success = 1;
519 } else {
520 /* delay in 0.1s increments up to 10s */
521 if (fail_count < 100)
522 fail_count++;
523 delay = 100000 * fail_count;
524 debug("unlock failed, delaying %0.1lf seconds",
525 (double)delay/1000000);
526 usleep(delay);
527 }
528 explicit_bzero(passwdhash, sizeof(passwdhash));
Ben Lindstrom2f717042002-06-06 21:52:03 +0000529 } else if (!locked && lock) {
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000530 debug("agent locked");
Ben Lindstrom2f717042002-06-06 21:52:03 +0000531 locked = 1;
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000532 arc4random_buf(lock_salt, sizeof(lock_salt));
533 if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt),
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000534 lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS) < 0)
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000535 fatal("bcrypt_pbkdf");
Ben Lindstrom2f717042002-06-06 21:52:03 +0000536 success = 1;
537 }
dtucker@openbsd.org9173d0f2015-05-15 05:44:21 +0000538 explicit_bzero(passwd, pwlen);
Darren Tuckera627d422013-06-02 07:31:17 +1000539 free(passwd);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000540 send_status(e, success);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000541}
542
543static void
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000544no_identities(SocketEntry *e)
Ben Lindstrom2f717042002-06-06 21:52:03 +0000545{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000546 struct sshbuf *msg;
547 int r;
Ben Lindstrom2f717042002-06-06 21:52:03 +0000548
markus@openbsd.org139ca812015-01-14 13:09:09 +0000549 if ((msg = sshbuf_new()) == NULL)
550 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000551 if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 ||
markus@openbsd.org139ca812015-01-14 13:09:09 +0000552 (r = sshbuf_put_u32(msg, 0)) != 0 ||
553 (r = sshbuf_put_stringb(e->output, msg)) != 0)
554 fatal("%s: buffer error: %s", __func__, ssh_err(r));
555 sshbuf_free(msg);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000556}
Ben Lindstrom3f471632001-07-04 03:53:15 +0000557
Damien Miller7ea845e2010-02-12 09:21:02 +1100558#ifdef ENABLE_PKCS11
Ben Lindstrom3f471632001-07-04 03:53:15 +0000559static void
Damien Miller1cfadab2008-06-30 00:05:21 +1000560process_add_smartcard_key(SocketEntry *e)
Ben Lindstrom3f471632001-07-04 03:53:15 +0000561{
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000562 char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000563 int r, i, count = 0, success = 0, confirm = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000564 u_int seconds;
Darren Tucker55119252013-06-02 07:43:59 +1000565 time_t death = 0;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000566 u_char type;
567 struct sshkey **keys = NULL, *k;
Ben Lindstrom822b6342002-06-23 21:38:49 +0000568 Identity *id;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100569
markus@openbsd.org139ca812015-01-14 13:09:09 +0000570 if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000571 (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) {
572 error("%s: buffer error: %s", __func__, ssh_err(r));
573 goto send;
574 }
Damien Millerd94f20d2003-06-11 22:06:33 +1000575
markus@openbsd.org139ca812015-01-14 13:09:09 +0000576 while (sshbuf_len(e->request)) {
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000577 if ((r = sshbuf_get_u8(e->request, &type)) != 0) {
578 error("%s: buffer error: %s", __func__, ssh_err(r));
579 goto send;
580 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000581 switch (type) {
Damien Millerd94f20d2003-06-11 22:06:33 +1000582 case SSH_AGENT_CONSTRAIN_LIFETIME:
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000583 if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) {
584 error("%s: buffer error: %s",
markus@openbsd.org139ca812015-01-14 13:09:09 +0000585 __func__, ssh_err(r));
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000586 goto send;
587 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000588 death = monotime() + seconds;
Damien Millerd94f20d2003-06-11 22:06:33 +1000589 break;
590 case SSH_AGENT_CONSTRAIN_CONFIRM:
591 confirm = 1;
592 break;
593 default:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000594 error("%s: Unknown constraint type %d", __func__, type);
Damien Miller1cfadab2008-06-30 00:05:21 +1000595 goto send;
Damien Millerd94f20d2003-06-11 22:06:33 +1000596 }
597 }
djm@openbsd.org786d5992016-11-30 03:07:37 +0000598 if (realpath(provider, canonical_provider) == NULL) {
599 verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
600 provider, strerror(errno));
601 goto send;
602 }
603 if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) {
604 verbose("refusing PKCS#11 add of \"%.100s\": "
605 "provider not whitelisted", canonical_provider);
606 goto send;
607 }
608 debug("%s: add %.100s", __func__, canonical_provider);
Damien Millerd94f20d2003-06-11 22:06:33 +1000609 if (lifetime && !death)
Darren Tuckerb759c9c2013-06-02 07:46:16 +1000610 death = monotime() + lifetime;
Damien Millerd94f20d2003-06-11 22:06:33 +1000611
djm@openbsd.org786d5992016-11-30 03:07:37 +0000612 count = pkcs11_add_provider(canonical_provider, pin, &keys);
Damien Miller7ea845e2010-02-12 09:21:02 +1100613 for (i = 0; i < count; i++) {
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000614 k = keys[i];
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000615 if (lookup_identity(k) == NULL) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100616 id = xcalloc(1, sizeof(Identity));
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000617 id->key = k;
djm@openbsd.org786d5992016-11-30 03:07:37 +0000618 id->provider = xstrdup(canonical_provider);
619 id->comment = xstrdup(canonical_provider); /* XXX */
Damien Millerd94f20d2003-06-11 22:06:33 +1000620 id->death = death;
621 id->confirm = confirm;
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000622 TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
623 idtab->nentries++;
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000624 success = 1;
625 } else {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000626 sshkey_free(k);
Ben Lindstrom0936a5b2002-03-26 03:17:42 +0000627 }
628 keys[i] = NULL;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000629 }
Ben Lindstrom3f471632001-07-04 03:53:15 +0000630send:
Darren Tuckera627d422013-06-02 07:31:17 +1000631 free(pin);
632 free(provider);
633 free(keys);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000634 send_status(e, success);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000635}
636
637static void
638process_remove_smartcard_key(SocketEntry *e)
639{
djm@openbsd.org25f83762017-03-15 02:25:09 +0000640 char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000641 int r, success = 0;
Damien Miller7ea845e2010-02-12 09:21:02 +1100642 Identity *id, *nxt;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000643
markus@openbsd.org139ca812015-01-14 13:09:09 +0000644 if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
djm@openbsd.org@openbsd.org83a1e5d2017-11-15 02:10:16 +0000645 (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) {
646 error("%s: buffer error: %s", __func__, ssh_err(r));
647 goto send;
648 }
Darren Tuckera627d422013-06-02 07:31:17 +1000649 free(pin);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000650
djm@openbsd.org25f83762017-03-15 02:25:09 +0000651 if (realpath(provider, canonical_provider) == NULL) {
652 verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
653 provider, strerror(errno));
654 goto send;
655 }
656
657 debug("%s: remove %.100s", __func__, canonical_provider);
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000658 for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) {
659 nxt = TAILQ_NEXT(id, next);
660 /* Skip file--based keys */
661 if (id->provider == NULL)
662 continue;
663 if (!strcmp(canonical_provider, id->provider)) {
664 TAILQ_REMOVE(&idtab->idlist, id, next);
665 free_identity(id);
666 idtab->nentries--;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000667 }
Ben Lindstrom3f471632001-07-04 03:53:15 +0000668 }
djm@openbsd.org25f83762017-03-15 02:25:09 +0000669 if (pkcs11_del_provider(canonical_provider) == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +1100670 success = 1;
671 else
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000672 error("%s: pkcs11_del_provider failed", __func__);
deraadt@openbsd.org1a321bf2017-03-15 03:52:30 +0000673send:
Darren Tuckera627d422013-06-02 07:31:17 +1000674 free(provider);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000675 send_status(e, success);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000676}
Damien Miller7ea845e2010-02-12 09:21:02 +1100677#endif /* ENABLE_PKCS11 */
Ben Lindstrom3f471632001-07-04 03:53:15 +0000678
Damien Millerad833b32000-08-23 10:46:23 +1000679/* dispatch incoming messages */
680
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000681static int
682process_message(u_int socknum)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000683{
markus@openbsd.org139ca812015-01-14 13:09:09 +0000684 u_int msg_len;
685 u_char type;
686 const u_char *cp;
687 int r;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000688 SocketEntry *e;
689
690 if (socknum >= sockets_alloc) {
691 fatal("%s: socket number %u >= allocated %u",
692 __func__, socknum, sockets_alloc);
693 }
694 e = &sockets[socknum];
Ben Lindstrom61d328a2002-06-06 21:54:57 +0000695
markus@openbsd.org139ca812015-01-14 13:09:09 +0000696 if (sshbuf_len(e->input) < 5)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000697 return 0; /* Incomplete message header. */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000698 cp = sshbuf_ptr(e->input);
699 msg_len = PEEK_U32(cp);
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000700 if (msg_len > AGENT_MAX_LEN) {
701 debug("%s: socket %u (fd=%d) message too long %u > %u",
702 __func__, socknum, e->fd, msg_len, AGENT_MAX_LEN);
703 return -1;
Damien Miller95def091999-11-25 00:26:21 +1100704 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000705 if (sshbuf_len(e->input) < msg_len + 4)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000706 return 0; /* Incomplete message body. */
Ben Lindstrom21d1ed82002-06-06 21:48:57 +0000707
708 /* move the current input to e->request */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000709 sshbuf_reset(e->request);
710 if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 ||
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000711 (r = sshbuf_get_u8(e->request, &type)) != 0) {
712 if (r == SSH_ERR_MESSAGE_INCOMPLETE ||
713 r == SSH_ERR_STRING_TOO_LARGE) {
714 debug("%s: buffer error: %s", __func__, ssh_err(r));
715 return -1;
716 }
markus@openbsd.org139ca812015-01-14 13:09:09 +0000717 fatal("%s: buffer error: %s", __func__, ssh_err(r));
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000718 }
719
720 debug("%s: socket %u (fd=%d) type %d", __func__, socknum, e->fd, type);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000721
djm@openbsd.org001aa552018-04-10 00:10:49 +0000722 /* check whether agent is locked */
Ben Lindstrom2f717042002-06-06 21:52:03 +0000723 if (locked && type != SSH_AGENTC_UNLOCK) {
markus@openbsd.org139ca812015-01-14 13:09:09 +0000724 sshbuf_reset(e->request);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000725 switch (type) {
Ben Lindstrom2f717042002-06-06 21:52:03 +0000726 case SSH2_AGENTC_REQUEST_IDENTITIES:
727 /* send empty lists */
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000728 no_identities(e);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000729 break;
730 default:
731 /* send a fail message for all other request types */
markus@openbsd.org139ca812015-01-14 13:09:09 +0000732 send_status(e, 0);
Ben Lindstrom2f717042002-06-06 21:52:03 +0000733 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000734 return 0;
Ben Lindstrom2f717042002-06-06 21:52:03 +0000735 }
736
Damien Miller95def091999-11-25 00:26:21 +1100737 switch (type) {
Ben Lindstrom2f717042002-06-06 21:52:03 +0000738 case SSH_AGENTC_LOCK:
739 case SSH_AGENTC_UNLOCK:
740 process_lock_agent(e, type == SSH_AGENTC_LOCK);
741 break;
djm@openbsd.org2f04af92015-03-04 21:12:59 +0000742 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000743 process_remove_all_identities(e); /* safe for !WITH_SSH1 */
djm@openbsd.org2f04af92015-03-04 21:12:59 +0000744 break;
Damien Millerad833b32000-08-23 10:46:23 +1000745 /* ssh2 */
746 case SSH2_AGENTC_SIGN_REQUEST:
747 process_sign_request2(e);
748 break;
749 case SSH2_AGENTC_REQUEST_IDENTITIES:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000750 process_request_identities(e);
Damien Millerad833b32000-08-23 10:46:23 +1000751 break;
752 case SSH2_AGENTC_ADD_IDENTITY:
Ben Lindstrom2b266b72002-06-21 00:08:39 +0000753 case SSH2_AGENTC_ADD_ID_CONSTRAINED:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000754 process_add_identity(e);
Damien Millerad833b32000-08-23 10:46:23 +1000755 break;
756 case SSH2_AGENTC_REMOVE_IDENTITY:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000757 process_remove_identity(e);
Damien Millerad833b32000-08-23 10:46:23 +1000758 break;
759 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
djm@openbsd.orgf4a6a882017-04-30 23:29:10 +0000760 process_remove_all_identities(e);
Damien Miller95def091999-11-25 00:26:21 +1100761 break;
Damien Miller7ea845e2010-02-12 09:21:02 +1100762#ifdef ENABLE_PKCS11
Ben Lindstrom3f471632001-07-04 03:53:15 +0000763 case SSH_AGENTC_ADD_SMARTCARD_KEY:
Damien Millerd94f20d2003-06-11 22:06:33 +1000764 case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
Ben Lindstrom3f471632001-07-04 03:53:15 +0000765 process_add_smartcard_key(e);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100766 break;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000767 case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
768 process_remove_smartcard_key(e);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100769 break;
Damien Miller7ea845e2010-02-12 09:21:02 +1100770#endif /* ENABLE_PKCS11 */
Damien Miller95def091999-11-25 00:26:21 +1100771 default:
772 /* Unknown message. Respond with failure. */
773 error("Unknown message %d", type);
markus@openbsd.org139ca812015-01-14 13:09:09 +0000774 sshbuf_reset(e->request);
775 send_status(e, 0);
Damien Miller95def091999-11-25 00:26:21 +1100776 break;
777 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000778 return 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000779}
780
Ben Lindstrombba81212001-06-25 05:01:22 +0000781static void
Ben Lindstrom65366a82001-12-06 16:32:47 +0000782new_socket(sock_type type, int fd)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000783{
Darren Tuckerfb16b242003-09-22 21:04:23 +1000784 u_int i, old_alloc, new_alloc;
Ben Lindstrom822b6342002-06-23 21:38:49 +0000785
Damien Miller232711f2004-06-15 10:35:30 +1000786 set_nonblock(fd);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000787
Damien Miller95def091999-11-25 00:26:21 +1100788 if (fd > max_fd)
789 max_fd = fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000790
Damien Miller95def091999-11-25 00:26:21 +1100791 for (i = 0; i < sockets_alloc; i++)
792 if (sockets[i].type == AUTH_UNUSED) {
793 sockets[i].fd = fd;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000794 if ((sockets[i].input = sshbuf_new()) == NULL)
795 fatal("%s: sshbuf_new failed", __func__);
796 if ((sockets[i].output = sshbuf_new()) == NULL)
797 fatal("%s: sshbuf_new failed", __func__);
798 if ((sockets[i].request = sshbuf_new()) == NULL)
799 fatal("%s: sshbuf_new failed", __func__);
Darren Tuckerfb16b242003-09-22 21:04:23 +1000800 sockets[i].type = type;
Damien Miller95def091999-11-25 00:26:21 +1100801 return;
802 }
803 old_alloc = sockets_alloc;
Darren Tuckerfb16b242003-09-22 21:04:23 +1000804 new_alloc = sockets_alloc + 10;
deraadt@openbsd.org657a5fb2015-04-24 01:36:00 +0000805 sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0]));
Darren Tuckerfb16b242003-09-22 21:04:23 +1000806 for (i = old_alloc; i < new_alloc; i++)
Damien Miller95def091999-11-25 00:26:21 +1100807 sockets[i].type = AUTH_UNUSED;
Darren Tuckerfb16b242003-09-22 21:04:23 +1000808 sockets_alloc = new_alloc;
Damien Miller95def091999-11-25 00:26:21 +1100809 sockets[old_alloc].fd = fd;
markus@openbsd.org139ca812015-01-14 13:09:09 +0000810 if ((sockets[old_alloc].input = sshbuf_new()) == NULL)
811 fatal("%s: sshbuf_new failed", __func__);
812 if ((sockets[old_alloc].output = sshbuf_new()) == NULL)
813 fatal("%s: sshbuf_new failed", __func__);
814 if ((sockets[old_alloc].request = sshbuf_new()) == NULL)
815 fatal("%s: sshbuf_new failed", __func__);
Darren Tuckerfb16b242003-09-22 21:04:23 +1000816 sockets[old_alloc].type = type;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000817}
818
Ben Lindstrombba81212001-06-25 05:01:22 +0000819static int
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000820handle_socket_read(u_int socknum)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000821{
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000822 struct sockaddr_un sunaddr;
823 socklen_t slen;
824 uid_t euid;
825 gid_t egid;
826 int fd;
827
828 slen = sizeof(sunaddr);
829 fd = accept(sockets[socknum].fd, (struct sockaddr *)&sunaddr, &slen);
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +0000830 if (fd == -1) {
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000831 error("accept from AUTH_SOCKET: %s", strerror(errno));
832 return -1;
833 }
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +0000834 if (getpeereid(fd, &euid, &egid) == -1) {
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000835 error("getpeereid %d failed: %s", fd, strerror(errno));
836 close(fd);
837 return -1;
838 }
839 if ((euid != 0) && (getuid() != euid)) {
840 error("uid mismatch: peer euid %u != uid %u",
841 (u_int) euid, (u_int) getuid());
842 close(fd);
843 return -1;
844 }
845 new_socket(AUTH_CONNECTION, fd);
846 return 0;
847}
848
849static int
850handle_conn_read(u_int socknum)
851{
djm@openbsd.orgd6915882019-01-22 22:58:50 +0000852 char buf[AGENT_RBUF_LEN];
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000853 ssize_t len;
854 int r;
855
856 if ((len = read(sockets[socknum].fd, buf, sizeof(buf))) <= 0) {
857 if (len == -1) {
858 if (errno == EAGAIN || errno == EINTR)
859 return 0;
860 error("%s: read error on socket %u (fd %d): %s",
861 __func__, socknum, sockets[socknum].fd,
862 strerror(errno));
863 }
864 return -1;
865 }
866 if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0)
867 fatal("%s: buffer error: %s", __func__, ssh_err(r));
868 explicit_bzero(buf, sizeof(buf));
869 process_message(socknum);
870 return 0;
871}
872
873static int
874handle_conn_write(u_int socknum)
875{
876 ssize_t len;
877 int r;
878
879 if (sshbuf_len(sockets[socknum].output) == 0)
880 return 0; /* shouldn't happen */
881 if ((len = write(sockets[socknum].fd,
882 sshbuf_ptr(sockets[socknum].output),
883 sshbuf_len(sockets[socknum].output))) <= 0) {
884 if (len == -1) {
885 if (errno == EAGAIN || errno == EINTR)
886 return 0;
887 error("%s: read error on socket %u (fd %d): %s",
888 __func__, socknum, sockets[socknum].fd,
889 strerror(errno));
890 }
891 return -1;
892 }
893 if ((r = sshbuf_consume(sockets[socknum].output, len)) != 0)
894 fatal("%s: buffer error: %s", __func__, ssh_err(r));
895 return 0;
896}
897
898static void
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000899after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000900{
901 size_t i;
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000902 u_int socknum, activefds = npfd;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000903
904 for (i = 0; i < npfd; i++) {
905 if (pfd[i].revents == 0)
906 continue;
907 /* Find sockets entry */
908 for (socknum = 0; socknum < sockets_alloc; socknum++) {
909 if (sockets[socknum].type != AUTH_SOCKET &&
910 sockets[socknum].type != AUTH_CONNECTION)
911 continue;
912 if (pfd[i].fd == sockets[socknum].fd)
913 break;
914 }
915 if (socknum >= sockets_alloc) {
916 error("%s: no socket for fd %d", __func__, pfd[i].fd);
917 continue;
918 }
919 /* Process events */
920 switch (sockets[socknum].type) {
921 case AUTH_SOCKET:
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000922 if ((pfd[i].revents & (POLLIN|POLLERR)) == 0)
923 break;
924 if (npfd > maxfds) {
925 debug3("out of fds (active %u >= limit %u); "
926 "skipping accept", activefds, maxfds);
927 break;
928 }
929 if (handle_socket_read(socknum) == 0)
930 activefds++;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000931 break;
932 case AUTH_CONNECTION:
933 if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 &&
934 handle_conn_read(socknum) != 0) {
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000935 goto close_sock;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000936 }
937 if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 &&
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000938 handle_conn_write(socknum) != 0) {
939 close_sock:
940 if (activefds == 0)
941 fatal("activefds == 0 at close_sock");
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000942 close_socket(&sockets[socknum]);
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000943 activefds--;
944 break;
945 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000946 break;
947 default:
948 break;
949 }
950 }
951}
952
953static int
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000954prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000955{
956 struct pollfd *pfd = *pfdp;
957 size_t i, j, npfd = 0;
Darren Tucker55119252013-06-02 07:43:59 +1000958 time_t deadline;
djm@openbsd.orgd6915882019-01-22 22:58:50 +0000959 int r;
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000960
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000961 /* Count active sockets */
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000962 for (i = 0; i < sockets_alloc; i++) {
Damien Miller95def091999-11-25 00:26:21 +1100963 switch (sockets[i].type) {
964 case AUTH_SOCKET:
965 case AUTH_CONNECTION:
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000966 npfd++;
Damien Miller95def091999-11-25 00:26:21 +1100967 break;
968 case AUTH_UNUSED:
969 break;
970 default:
971 fatal("Unknown socket type %d", sockets[i].type);
972 break;
973 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000974 }
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000975 if (npfd != *npfdp &&
976 (pfd = recallocarray(pfd, *npfdp, npfd, sizeof(*pfd))) == NULL)
977 fatal("%s: recallocarray failed", __func__);
978 *pfdp = pfd;
979 *npfdp = npfd;
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000980
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000981 for (i = j = 0; i < sockets_alloc; i++) {
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000982 switch (sockets[i].type) {
983 case AUTH_SOCKET:
djm@openbsd.orgb2140a72018-05-11 03:38:51 +0000984 if (npfd > maxfds) {
985 debug3("out of fds (active %zu >= limit %u); "
986 "skipping arming listener", npfd, maxfds);
987 break;
988 }
989 pfd[j].fd = sockets[i].fd;
990 pfd[j].revents = 0;
991 pfd[j].events = POLLIN;
992 j++;
993 break;
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000994 case AUTH_CONNECTION:
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +0000995 pfd[j].fd = sockets[i].fd;
996 pfd[j].revents = 0;
djm@openbsd.orgd6915882019-01-22 22:58:50 +0000997 /*
998 * Only prepare to read if we can handle a full-size
999 * input read buffer and enqueue a max size reply..
1000 */
1001 if ((r = sshbuf_check_reserve(sockets[i].input,
1002 AGENT_RBUF_LEN)) == 0 &&
1003 (r = sshbuf_check_reserve(sockets[i].output,
1004 AGENT_MAX_LEN)) == 0)
1005 pfd[j].events = POLLIN;
1006 else if (r != SSH_ERR_NO_BUFFER_SPACE) {
1007 fatal("%s: buffer error: %s",
1008 __func__, ssh_err(r));
1009 }
markus@openbsd.org139ca812015-01-14 13:09:09 +00001010 if (sshbuf_len(sockets[i].output) > 0)
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001011 pfd[j].events |= POLLOUT;
1012 j++;
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001013 break;
1014 default:
1015 break;
1016 }
1017 }
Darren Tucker2812dc92007-03-21 20:45:06 +11001018 deadline = reaper();
1019 if (parent_alive_interval != 0)
1020 deadline = (deadline == 0) ? parent_alive_interval :
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001021 MINIMUM(deadline, parent_alive_interval);
Darren Tucker2812dc92007-03-21 20:45:06 +11001022 if (deadline == 0) {
djm@openbsd.org9f0e44e2017-07-24 04:34:28 +00001023 *timeoutp = -1; /* INFTIM */
Darren Tucker2812dc92007-03-21 20:45:06 +11001024 } else {
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001025 if (deadline > INT_MAX / 1000)
1026 *timeoutp = INT_MAX / 1000;
1027 else
1028 *timeoutp = deadline * 1000;
Darren Tucker2812dc92007-03-21 20:45:06 +11001029 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001030 return (1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001031}
1032
Ben Lindstrombba81212001-06-25 05:01:22 +00001033static void
Darren Tucker6fa8abd2003-09-22 21:10:21 +10001034cleanup_socket(void)
Damien Miller792c5111999-10-29 09:47:09 +10001035{
Damien Millerb1e967c2014-07-03 21:22:40 +10001036 if (cleanup_pid != 0 && getpid() != cleanup_pid)
1037 return;
1038 debug("%s: cleanup", __func__);
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001039 if (socket_name[0])
1040 unlink(socket_name);
1041 if (socket_dir[0])
1042 rmdir(socket_dir);
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001043}
1044
Darren Tucker3e33cec2003-10-02 16:12:36 +10001045void
Damien Miller792c5111999-10-29 09:47:09 +10001046cleanup_exit(int i)
1047{
Darren Tucker6fa8abd2003-09-22 21:10:21 +10001048 cleanup_socket();
1049 _exit(i);
Damien Miller792c5111999-10-29 09:47:09 +10001050}
1051
Damien Miller1c13bd82006-03-26 14:28:14 +11001052/*ARGSUSED*/
Ben Lindstrombba81212001-06-25 05:01:22 +00001053static void
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001054cleanup_handler(int sig)
1055{
Darren Tucker6fa8abd2003-09-22 21:10:21 +10001056 cleanup_socket();
Damien Miller7ea845e2010-02-12 09:21:02 +11001057#ifdef ENABLE_PKCS11
1058 pkcs11_terminate();
1059#endif
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001060 _exit(2);
1061}
1062
Ben Lindstrombba81212001-06-25 05:01:22 +00001063static void
Darren Tucker2812dc92007-03-21 20:45:06 +11001064check_parent_exists(void)
Ben Lindstrom0250da02001-07-22 20:44:00 +00001065{
Darren Tucker3e78a512011-06-03 14:14:16 +10001066 /*
1067 * If our parent has exited then getppid() will return (pid_t)1,
1068 * so testing for that should be safe.
1069 */
1070 if (parent_pid != -1 && getppid() != parent_pid) {
Ben Lindstrom0250da02001-07-22 20:44:00 +00001071 /* printf("Parent has died - Authentication agent exiting.\n"); */
Darren Tucker2812dc92007-03-21 20:45:06 +11001072 cleanup_socket();
1073 _exit(2);
Ben Lindstrom0250da02001-07-22 20:44:00 +00001074 }
Ben Lindstrom0250da02001-07-22 20:44:00 +00001075}
1076
1077static void
Ben Lindstrom28072eb2001-02-10 23:13:41 +00001078usage(void)
Damien Miller792c5111999-10-29 09:47:09 +10001079{
Damien Millerf0858de2014-04-20 13:01:30 +10001080 fprintf(stderr,
jmc@openbsd.orgb7ca2762015-04-24 06:26:49 +00001081 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
djm@openbsd.org786d5992016-11-30 03:07:37 +00001082 " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n"
Damien Millerf0858de2014-04-20 13:01:30 +10001083 " ssh-agent [-c | -s] -k\n");
Damien Miller95def091999-11-25 00:26:21 +11001084 exit(1);
Damien Miller792c5111999-10-29 09:47:09 +10001085}
1086
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001087int
1088main(int ac, char **av)
1089{
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001090 int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0;
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001091 int sock, fd, ch, result, saved_errno;
Ben Lindstrom822b6342002-06-23 21:38:49 +00001092 char *shell, *format, *pidstr, *agentsocket = NULL;
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001093#ifdef HAVE_SETRLIMIT
Ben Lindstromc72745a2000-12-02 19:03:54 +00001094 struct rlimit rlim;
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001095#endif
Damien Miller615f9392000-05-17 22:53:33 +10001096 extern int optind;
Ben Lindstrom883844d2002-06-23 00:20:50 +00001097 extern char *optarg;
Ben Lindstrom822b6342002-06-23 21:38:49 +00001098 pid_t pid;
1099 char pidstrbuf[1 + 3 * sizeof pid];
Darren Tucker90133232009-06-21 17:50:15 +10001100 size_t len;
Damien Millerab2ec582014-07-18 15:04:47 +10001101 mode_t prev_mask;
djm@openbsd.org9f0e44e2017-07-24 04:34:28 +00001102 int timeout = -1; /* INFTIM */
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001103 struct pollfd *pfd = NULL;
1104 size_t npfd = 0;
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001105 u_int maxfds;
Kevin Stevesde41bc62000-12-14 00:04:08 +00001106
Darren Tuckerce321d82005-10-03 18:11:24 +10001107 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1108 sanitise_stdfd();
1109
Damien Miller6cffb9a2002-09-04 16:20:26 +10001110 /* drop */
1111 setegid(getgid());
1112 setgid(getgid());
1113
Darren Tucker0fb7f592016-06-09 16:23:07 +10001114 platform_disable_tracing(0); /* strict=no */
Damien Miller6c4914a2004-03-03 11:08:59 +11001115
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001116 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
1117 fatal("%s: getrlimit: %s", __progname, strerror(errno));
1118
Damien Miller59d3d5b2003-08-22 09:34:41 +10001119 __progname = ssh_get_progname(av[0]);
Damien Miller60bc5172001-03-19 09:38:15 +11001120 seed_rng();
Kevin Stevesef4eea92001-02-05 12:42:17 +00001121
djm@openbsd.org786d5992016-11-30 03:07:37 +00001122 while ((ch = getopt(ac, av, "cDdksE:a:P:t:")) != -1) {
Damien Miller95def091999-11-25 00:26:21 +11001123 switch (ch) {
djm@openbsd.org56d1c832014-12-21 22:27:55 +00001124 case 'E':
1125 fingerprint_hash = ssh_digest_alg_by_name(optarg);
1126 if (fingerprint_hash == -1)
1127 fatal("Invalid hash algorithm \"%s\"", optarg);
1128 break;
Damien Miller95def091999-11-25 00:26:21 +11001129 case 'c':
1130 if (s_flag)
1131 usage();
1132 c_flag++;
1133 break;
1134 case 'k':
1135 k_flag++;
1136 break;
djm@openbsd.org786d5992016-11-30 03:07:37 +00001137 case 'P':
1138 if (pkcs11_whitelist != NULL)
1139 fatal("-P option already specified");
1140 pkcs11_whitelist = xstrdup(optarg);
1141 break;
Damien Miller95def091999-11-25 00:26:21 +11001142 case 's':
1143 if (c_flag)
1144 usage();
1145 s_flag++;
1146 break;
Ben Lindstromd94580c2001-07-04 03:48:02 +00001147 case 'd':
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001148 if (d_flag || D_flag)
Ben Lindstromd94580c2001-07-04 03:48:02 +00001149 usage();
1150 d_flag++;
1151 break;
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001152 case 'D':
1153 if (d_flag || D_flag)
1154 usage();
1155 D_flag++;
1156 break;
Ben Lindstromb7788f32002-06-06 21:46:08 +00001157 case 'a':
1158 agentsocket = optarg;
1159 break;
Damien Miller53d81482003-01-22 11:47:19 +11001160 case 't':
1161 if ((lifetime = convtime(optarg)) == -1) {
1162 fprintf(stderr, "Invalid lifetime\n");
1163 usage();
1164 }
1165 break;
Damien Miller95def091999-11-25 00:26:21 +11001166 default:
1167 usage();
1168 }
Damien Miller792c5111999-10-29 09:47:09 +10001169 }
Damien Miller95def091999-11-25 00:26:21 +11001170 ac -= optind;
1171 av += optind;
Damien Miller792c5111999-10-29 09:47:09 +10001172
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001173 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
Damien Miller95def091999-11-25 00:26:21 +11001174 usage();
Damien Miller792c5111999-10-29 09:47:09 +10001175
djm@openbsd.org786d5992016-11-30 03:07:37 +00001176 if (pkcs11_whitelist == NULL)
1177 pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST);
1178
Ben Lindstromeecdf232002-04-02 21:03:51 +00001179 if (ac == 0 && !c_flag && !s_flag) {
Damien Miller95def091999-11-25 00:26:21 +11001180 shell = getenv("SHELL");
Darren Tucker90133232009-06-21 17:50:15 +10001181 if (shell != NULL && (len = strlen(shell)) > 2 &&
1182 strncmp(shell + len - 3, "csh", 3) == 0)
Damien Miller95def091999-11-25 00:26:21 +11001183 c_flag = 1;
Damien Miller792c5111999-10-29 09:47:09 +10001184 }
Damien Miller95def091999-11-25 00:26:21 +11001185 if (k_flag) {
Damien Miller89c3fe42006-03-31 23:11:28 +11001186 const char *errstr = NULL;
1187
Damien Miller95def091999-11-25 00:26:21 +11001188 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
1189 if (pidstr == NULL) {
1190 fprintf(stderr, "%s not set, cannot kill agent\n",
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001191 SSH_AGENTPID_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +11001192 exit(1);
1193 }
Damien Miller89c3fe42006-03-31 23:11:28 +11001194 pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr);
1195 if (errstr) {
1196 fprintf(stderr,
1197 "%s=\"%s\", which is not a good PID: %s\n",
1198 SSH_AGENTPID_ENV_NAME, pidstr, errstr);
Damien Miller95def091999-11-25 00:26:21 +11001199 exit(1);
1200 }
1201 if (kill(pid, SIGTERM) == -1) {
1202 perror("kill");
1203 exit(1);
1204 }
1205 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
1206 printf(format, SSH_AUTHSOCKET_ENV_NAME);
1207 printf(format, SSH_AGENTPID_ENV_NAME);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001208 printf("echo Agent pid %ld killed;\n", (long)pid);
Damien Miller95def091999-11-25 00:26:21 +11001209 exit(0);
Damien Miller792c5111999-10-29 09:47:09 +10001210 }
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001211
1212 /*
1213 * Minimum file descriptors:
1214 * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) +
1215 * a few spare for libc / stack protectors / sanitisers, etc.
1216 */
1217#define SSH_AGENT_MIN_FDS (3+1+1+1+4)
1218 if (rlim.rlim_cur < SSH_AGENT_MIN_FDS)
djm@openbsd.org960e7c62018-11-09 02:57:58 +00001219 fatal("%s: file descriptor rlimit %lld too low (minimum %u)",
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001220 __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS);
1221 maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS;
1222
Damien Miller95def091999-11-25 00:26:21 +11001223 parent_pid = getpid();
1224
Ben Lindstromb7788f32002-06-06 21:46:08 +00001225 if (agentsocket == NULL) {
1226 /* Create private directory for agent socket */
Damien Miller2cd62932010-12-01 11:50:35 +11001227 mktemp_proto(socket_dir, sizeof(socket_dir));
Ben Lindstromb7788f32002-06-06 21:46:08 +00001228 if (mkdtemp(socket_dir) == NULL) {
1229 perror("mkdtemp: private socket dir");
1230 exit(1);
1231 }
Ben Lindstromce0f6342002-06-11 16:42:49 +00001232 snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir,
1233 (long)parent_pid);
Ben Lindstromb7788f32002-06-06 21:46:08 +00001234 } else {
1235 /* Try to use specified agent socket */
1236 socket_dir[0] = '\0';
1237 strlcpy(socket_name, agentsocket, sizeof socket_name);
Damien Miller792c5111999-10-29 09:47:09 +10001238 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001239
Damien Miller5428f641999-11-25 11:54:57 +11001240 /*
1241 * Create socket early so it will exist before command gets run from
1242 * the parent.
1243 */
Damien Millerab2ec582014-07-18 15:04:47 +10001244 prev_mask = umask(0177);
Damien Miller7acefbb2014-07-18 14:11:24 +10001245 sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0);
Damien Miller95def091999-11-25 00:26:21 +11001246 if (sock < 0) {
Damien Miller7acefbb2014-07-18 14:11:24 +10001247 /* XXX - unix_listener() calls error() not perror() */
Darren Tucker1dee8682004-11-05 20:26:49 +11001248 *socket_name = '\0'; /* Don't unlink any existing file */
Damien Miller95def091999-11-25 00:26:21 +11001249 cleanup_exit(1);
Damien Miller792c5111999-10-29 09:47:09 +10001250 }
Damien Millerab2ec582014-07-18 15:04:47 +10001251 umask(prev_mask);
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001252
Damien Miller5428f641999-11-25 11:54:57 +11001253 /*
1254 * Fork, and have the parent execute the command, if any, or present
1255 * the socket data. The child continues as the authentication agent.
1256 */
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001257 if (D_flag || d_flag) {
1258 log_init(__progname,
1259 d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
1260 SYSLOG_FACILITY_AUTH, 1);
Ben Lindstromd94580c2001-07-04 03:48:02 +00001261 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
1262 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
1263 SSH_AUTHSOCKET_ENV_NAME);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001264 printf("echo Agent pid %ld;\n", (long)parent_pid);
dtucker@openbsd.org79394ed2015-12-11 02:29:03 +00001265 fflush(stdout);
Ben Lindstromd94580c2001-07-04 03:48:02 +00001266 goto skip;
1267 }
Damien Miller95def091999-11-25 00:26:21 +11001268 pid = fork();
1269 if (pid == -1) {
1270 perror("fork");
Damien Millerc653b892002-02-08 22:05:41 +11001271 cleanup_exit(1);
Damien Miller95def091999-11-25 00:26:21 +11001272 }
1273 if (pid != 0) { /* Parent - execute the given command. */
1274 close(sock);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001275 snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid);
Damien Miller95def091999-11-25 00:26:21 +11001276 if (ac == 0) {
1277 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
1278 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001279 SSH_AUTHSOCKET_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +11001280 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001281 SSH_AGENTPID_ENV_NAME);
Ben Lindstromce0f6342002-06-11 16:42:49 +00001282 printf("echo Agent pid %ld;\n", (long)pid);
Damien Miller95def091999-11-25 00:26:21 +11001283 exit(0);
1284 }
Damien Millere4340be2000-09-16 13:29:08 +11001285 if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
1286 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) {
1287 perror("setenv");
1288 exit(1);
1289 }
Damien Miller95def091999-11-25 00:26:21 +11001290 execvp(av[0], av);
1291 perror(av[0]);
1292 exit(1);
1293 }
Damien Millerc653b892002-02-08 22:05:41 +11001294 /* child */
1295 log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrom3fdf8762001-07-22 20:40:24 +00001296
1297 if (setsid() == -1) {
Damien Millerc653b892002-02-08 22:05:41 +11001298 error("setsid: %s", strerror(errno));
Ben Lindstrom3fdf8762001-07-22 20:40:24 +00001299 cleanup_exit(1);
1300 }
1301
1302 (void)chdir("/");
Damien Miller6c711792003-01-24 11:36:23 +11001303 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
1304 /* XXX might close listen socket */
1305 (void)dup2(fd, STDIN_FILENO);
1306 (void)dup2(fd, STDOUT_FILENO);
1307 (void)dup2(fd, STDERR_FILENO);
1308 if (fd > 2)
1309 close(fd);
1310 }
Damien Miller95def091999-11-25 00:26:21 +11001311
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001312#ifdef HAVE_SETRLIMIT
Ben Lindstromc72745a2000-12-02 19:03:54 +00001313 /* deny core dumps, since memory contains unencrypted private keys */
1314 rlim.rlim_cur = rlim.rlim_max = 0;
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +00001315 if (setrlimit(RLIMIT_CORE, &rlim) == -1) {
Damien Millerc653b892002-02-08 22:05:41 +11001316 error("setrlimit RLIMIT_CORE: %s", strerror(errno));
Ben Lindstromc72745a2000-12-02 19:03:54 +00001317 cleanup_exit(1);
1318 }
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001319#endif
Ben Lindstromd94580c2001-07-04 03:48:02 +00001320
1321skip:
Damien Miller7ea845e2010-02-12 09:21:02 +11001322
Damien Millerb1e967c2014-07-03 21:22:40 +10001323 cleanup_pid = getpid();
1324
Damien Miller7ea845e2010-02-12 09:21:02 +11001325#ifdef ENABLE_PKCS11
1326 pkcs11_init(0);
1327#endif
Damien Miller95def091999-11-25 00:26:21 +11001328 new_socket(AUTH_SOCKET, sock);
Darren Tucker2812dc92007-03-21 20:45:06 +11001329 if (ac > 0)
1330 parent_alive_interval = 10;
Damien Millerad833b32000-08-23 10:46:23 +10001331 idtab_init();
Damien Miller48bfa9c2001-07-14 12:12:55 +10001332 signal(SIGPIPE, SIG_IGN);
djm@openbsd.org2ea97462015-04-24 05:26:44 +00001333 signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN);
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001334 signal(SIGHUP, cleanup_handler);
1335 signal(SIGTERM, cleanup_handler);
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +00001336
djm@openbsd.org786d5992016-11-30 03:07:37 +00001337 if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1)
djm@openbsd.orgd9521622015-12-01 23:29:24 +00001338 fatal("%s: pledge: %s", __progname, strerror(errno));
Damien Miller4626cba2016-01-08 14:24:56 +11001339 platform_pledge_agent();
djm@openbsd.orgd9521622015-12-01 23:29:24 +00001340
Damien Miller95def091999-11-25 00:26:21 +11001341 while (1) {
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001342 prepare_poll(&pfd, &npfd, &timeout, maxfds);
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001343 result = poll(pfd, npfd, timeout);
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001344 saved_errno = errno;
Darren Tucker2812dc92007-03-21 20:45:06 +11001345 if (parent_alive_interval != 0)
1346 check_parent_exists();
1347 (void) reaper(); /* remove expired keys */
deraadt@openbsd.org4d28fa72019-06-28 13:35:04 +00001348 if (result == -1) {
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001349 if (saved_errno == EINTR)
Damien Miller95def091999-11-25 00:26:21 +11001350 continue;
djm@openbsd.orgfd0e8fa2017-07-19 01:15:02 +00001351 fatal("poll: %s", strerror(saved_errno));
Darren Tuckercf0d2db2007-02-28 21:19:58 +11001352 } else if (result > 0)
djm@openbsd.orgb2140a72018-05-11 03:38:51 +00001353 after_poll(pfd, npfd, maxfds);
Damien Miller95def091999-11-25 00:26:21 +11001354 }
1355 /* NOTREACHED */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001356}