1296781Sdes/* $OpenBSD: auth2-pubkey.c,v 1.55 2016/01/27 00:53:12 djm Exp $ */
298675Sdes/*
398675Sdes * Copyright (c) 2000 Markus Friedl.  All rights reserved.
498675Sdes *
598675Sdes * Redistribution and use in source and binary forms, with or without
698675Sdes * modification, are permitted provided that the following conditions
798675Sdes * are met:
898675Sdes * 1. Redistributions of source code must retain the above copyright
998675Sdes *    notice, this list of conditions and the following disclaimer.
1098675Sdes * 2. Redistributions in binary form must reproduce the above copyright
1198675Sdes *    notice, this list of conditions and the following disclaimer in the
1298675Sdes *    documentation and/or other materials provided with the distribution.
1398675Sdes *
1498675Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1598675Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1698675Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1798675Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1898675Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1998675Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2098675Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2198675Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2298675Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2398675Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2498675Sdes */
2598675Sdes
2698675Sdes#include "includes.h"
2798675Sdes
28162852Sdes#include <sys/types.h>
29162852Sdes#include <sys/stat.h>
30248619Sdes#include <sys/wait.h>
31162852Sdes
32248619Sdes#include <errno.h>
33181111Sdes#include <fcntl.h>
34248619Sdes#ifdef HAVE_PATHS_H
35248619Sdes# include <paths.h>
36248619Sdes#endif
37162852Sdes#include <pwd.h>
38248619Sdes#include <signal.h>
39162852Sdes#include <stdio.h>
40162852Sdes#include <stdarg.h>
41204917Sdes#include <string.h>
42204917Sdes#include <time.h>
43181111Sdes#include <unistd.h>
44295367Sdes#include <limits.h>
45162852Sdes
46162852Sdes#include "xmalloc.h"
47146998Sdes#include "ssh.h"
4898675Sdes#include "ssh2.h"
4998675Sdes#include "packet.h"
5098675Sdes#include "buffer.h"
5198675Sdes#include "log.h"
52295367Sdes#include "misc.h"
5398675Sdes#include "servconf.h"
5498675Sdes#include "compat.h"
55162852Sdes#include "key.h"
56162852Sdes#include "hostfile.h"
5798675Sdes#include "auth.h"
5898675Sdes#include "pathnames.h"
5998675Sdes#include "uidswap.h"
6098675Sdes#include "auth-options.h"
6198675Sdes#include "canohost.h"
62162852Sdes#ifdef GSSAPI
63162852Sdes#include "ssh-gss.h"
64162852Sdes#endif
6598675Sdes#include "monitor_wrap.h"
66204917Sdes#include "authfile.h"
67215116Sdes#include "match.h"
68295367Sdes#include "ssherr.h"
69295367Sdes#include "channels.h" /* XXX for session.h */
70295367Sdes#include "session.h" /* XXX for child_set_env(); refactor? */
7198675Sdes
7298675Sdes/* import */
7398675Sdesextern ServerOptions options;
7498675Sdesextern u_char *session_id2;
75124208Sdesextern u_int session_id2_len;
7698675Sdes
7798675Sdesstatic int
7898675Sdesuserauth_pubkey(Authctxt *authctxt)
7998675Sdes{
8098675Sdes	Buffer b;
8198675Sdes	Key *key = NULL;
82296781Sdes	char *pkalg, *userstyle, *fp = NULL;
8398675Sdes	u_char *pkblob, *sig;
8498675Sdes	u_int alen, blen, slen;
8598675Sdes	int have_sig, pktype;
8698675Sdes	int authenticated = 0;
8798675Sdes
8898675Sdes	if (!authctxt->valid) {
89296781Sdes		debug2("%s: disabled because of invalid user", __func__);
9098675Sdes		return 0;
9198675Sdes	}
9298675Sdes	have_sig = packet_get_char();
9398675Sdes	if (datafellows & SSH_BUG_PKAUTH) {
94296781Sdes		debug2("%s: SSH_BUG_PKAUTH", __func__);
9598675Sdes		/* no explicit pkalg given */
9698675Sdes		pkblob = packet_get_string(&blen);
9798675Sdes		buffer_init(&b);
9898675Sdes		buffer_append(&b, pkblob, blen);
9998675Sdes		/* so we have to extract the pkalg from the pkblob */
10098675Sdes		pkalg = buffer_get_string(&b, &alen);
10198675Sdes		buffer_free(&b);
10298675Sdes	} else {
10398675Sdes		pkalg = packet_get_string(&alen);
10498675Sdes		pkblob = packet_get_string(&blen);
10598675Sdes	}
10698675Sdes	pktype = key_type_from_name(pkalg);
10798675Sdes	if (pktype == KEY_UNSPEC) {
10898675Sdes		/* this is perfectly legal */
109296781Sdes		logit("%s: unsupported public key algorithm: %s",
110296781Sdes		    __func__, pkalg);
11198675Sdes		goto done;
11298675Sdes	}
11398675Sdes	key = key_from_blob(pkblob, blen);
11498675Sdes	if (key == NULL) {
115296781Sdes		error("%s: cannot decode key: %s", __func__, pkalg);
11698675Sdes		goto done;
11798675Sdes	}
11898675Sdes	if (key->type != pktype) {
119296781Sdes		error("%s: type mismatch for decoded key "
120296781Sdes		    "(received %d, expected %d)", __func__, key->type, pktype);
12198675Sdes		goto done;
12298675Sdes	}
123262566Sdes	if (key_type_plain(key->type) == KEY_RSA &&
124262566Sdes	    (datafellows & SSH_BUG_RSASIGMD5) != 0) {
125262566Sdes		logit("Refusing RSA key because client uses unsafe "
126262566Sdes		    "signature scheme");
127262566Sdes		goto done;
128262566Sdes	}
129296781Sdes	fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);
130295367Sdes	if (auth2_userkey_already_used(authctxt, key)) {
131295367Sdes		logit("refusing previously-used %s key", key_type(key));
132295367Sdes		goto done;
133295367Sdes	}
134295367Sdes	if (match_pattern_list(sshkey_ssh_name(key),
135295367Sdes	    options.pubkey_key_types, 0) != 1) {
136295367Sdes		logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
137295367Sdes		    __func__, sshkey_ssh_name(key));
138295367Sdes		goto done;
139295367Sdes	}
140295367Sdes
14198675Sdes	if (have_sig) {
142296781Sdes		debug3("%s: have signature for %s %s",
143296781Sdes		    __func__, sshkey_type(key), fp);
14498675Sdes		sig = packet_get_string(&slen);
14598675Sdes		packet_check_eom();
14698675Sdes		buffer_init(&b);
14798675Sdes		if (datafellows & SSH_OLD_SESSIONID) {
14898675Sdes			buffer_append(&b, session_id2, session_id2_len);
14998675Sdes		} else {
15098675Sdes			buffer_put_string(&b, session_id2, session_id2_len);
15198675Sdes		}
15298675Sdes		/* reconstruct packet */
15398675Sdes		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
154255767Sdes		xasprintf(&userstyle, "%s%s%s", authctxt->user,
155255767Sdes		    authctxt->style ? ":" : "",
156255767Sdes		    authctxt->style ? authctxt->style : "");
157255767Sdes		buffer_put_cstring(&b, userstyle);
158255767Sdes		free(userstyle);
15998675Sdes		buffer_put_cstring(&b,
16098675Sdes		    datafellows & SSH_BUG_PKSERVICE ?
16198675Sdes		    "ssh-userauth" :
16298675Sdes		    authctxt->service);
16398675Sdes		if (datafellows & SSH_BUG_PKAUTH) {
16498675Sdes			buffer_put_char(&b, have_sig);
16598675Sdes		} else {
16698675Sdes			buffer_put_cstring(&b, "publickey");
16798675Sdes			buffer_put_char(&b, have_sig);
16898675Sdes			buffer_put_cstring(&b, pkalg);
16998675Sdes		}
17098675Sdes		buffer_put_string(&b, pkblob, blen);
17198675Sdes#ifdef DEBUG_PK
17298675Sdes		buffer_dump(&b);
17398675Sdes#endif
174255767Sdes		pubkey_auth_info(authctxt, key, NULL);
175255767Sdes
17698675Sdes		/* test for correct signature */
17798675Sdes		authenticated = 0;
178295367Sdes		if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
17998675Sdes		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
180295367Sdes		    buffer_len(&b))) == 1) {
18198675Sdes			authenticated = 1;
182295367Sdes			/* Record the successful key to prevent reuse */
183295367Sdes			auth2_record_userkey(authctxt, key);
184295367Sdes			key = NULL; /* Don't free below */
185295367Sdes		}
186126274Sdes		buffer_free(&b);
187255767Sdes		free(sig);
18898675Sdes	} else {
189296781Sdes		debug("%s: test whether pkalg/pkblob are acceptable for %s %s",
190296781Sdes		    __func__, sshkey_type(key), fp);
19198675Sdes		packet_check_eom();
19298675Sdes
19398675Sdes		/* XXX fake reply and always send PK_OK ? */
19498675Sdes		/*
19598675Sdes		 * XXX this allows testing whether a user is allowed
19698675Sdes		 * to login: if you happen to have a valid pubkey this
19798675Sdes		 * message is sent. the message is NEVER sent at all
19898675Sdes		 * if a user is not allowed to login. is this an
19998675Sdes		 * issue? -markus
20098675Sdes		 */
201295367Sdes		if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) {
20298675Sdes			packet_start(SSH2_MSG_USERAUTH_PK_OK);
20398675Sdes			packet_put_string(pkalg, alen);
20498675Sdes			packet_put_string(pkblob, blen);
20598675Sdes			packet_send();
20698675Sdes			packet_write_wait();
20798675Sdes			authctxt->postponed = 1;
20898675Sdes		}
20998675Sdes	}
21098675Sdes	if (authenticated != 1)
21198675Sdes		auth_clear_options();
21298675Sdesdone:
213296781Sdes	debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg);
21498675Sdes	if (key != NULL)
21598675Sdes		key_free(key);
216255767Sdes	free(pkalg);
217255767Sdes	free(pkblob);
218296781Sdes	free(fp);
21998675Sdes	return authenticated;
22098675Sdes}
22198675Sdes
222255767Sdesvoid
223255767Sdespubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
224255767Sdes{
225255767Sdes	char *fp, *extra;
226255767Sdes	va_list ap;
227255767Sdes	int i;
228255767Sdes
229255767Sdes	extra = NULL;
230255767Sdes	if (fmt != NULL) {
231255767Sdes		va_start(ap, fmt);
232255767Sdes		i = vasprintf(&extra, fmt, ap);
233255767Sdes		va_end(ap);
234255767Sdes		if (i < 0 || extra == NULL)
235255767Sdes			fatal("%s: vasprintf failed", __func__);
236255767Sdes	}
237255767Sdes
238255767Sdes	if (key_is_cert(key)) {
239295367Sdes		fp = sshkey_fingerprint(key->cert->signature_key,
240295367Sdes		    options.fingerprint_hash, SSH_FP_DEFAULT);
241255767Sdes		auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s",
242255767Sdes		    key_type(key), key->cert->key_id,
243255767Sdes		    (unsigned long long)key->cert->serial,
244295367Sdes		    key_type(key->cert->signature_key),
245295367Sdes		    fp == NULL ? "(null)" : fp,
246255767Sdes		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
247255767Sdes		free(fp);
248255767Sdes	} else {
249295367Sdes		fp = sshkey_fingerprint(key, options.fingerprint_hash,
250295367Sdes		    SSH_FP_DEFAULT);
251295367Sdes		auth_info(authctxt, "%s %s%s%s", key_type(key),
252295367Sdes		    fp == NULL ? "(null)" : fp,
253255767Sdes		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
254255767Sdes		free(fp);
255255767Sdes	}
256255767Sdes	free(extra);
257255767Sdes}
258255767Sdes
259295367Sdes/*
260295367Sdes * Splits 's' into an argument vector. Handles quoted string and basic
261295367Sdes * escape characters (\\, \", \'). Caller must free the argument vector
262295367Sdes * and its members.
263295367Sdes */
264215116Sdesstatic int
265295367Sdessplit_argv(const char *s, int *argcp, char ***argvp)
266215116Sdes{
267295367Sdes	int r = SSH_ERR_INTERNAL_ERROR;
268295367Sdes	int argc = 0, quote, i, j;
269295367Sdes	char *arg, **argv = xcalloc(1, sizeof(*argv));
270295367Sdes
271295367Sdes	*argvp = NULL;
272295367Sdes	*argcp = 0;
273295367Sdes
274295367Sdes	for (i = 0; s[i] != '\0'; i++) {
275295367Sdes		/* Skip leading whitespace */
276295367Sdes		if (s[i] == ' ' || s[i] == '\t')
277295367Sdes			continue;
278295367Sdes
279295367Sdes		/* Start of a token */
280295367Sdes		quote = 0;
281295367Sdes		if (s[i] == '\\' &&
282295367Sdes		    (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\'))
283295367Sdes			i++;
284295367Sdes		else if (s[i] == '\'' || s[i] == '"')
285295367Sdes			quote = s[i++];
286295367Sdes
287295367Sdes		argv = xreallocarray(argv, (argc + 2), sizeof(*argv));
288295367Sdes		arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1);
289295367Sdes		argv[argc] = NULL;
290295367Sdes
291295367Sdes		/* Copy the token in, removing escapes */
292295367Sdes		for (j = 0; s[i] != '\0'; i++) {
293295367Sdes			if (s[i] == '\\') {
294295367Sdes				if (s[i + 1] == '\'' ||
295295367Sdes				    s[i + 1] == '\"' ||
296295367Sdes				    s[i + 1] == '\\') {
297295367Sdes					i++; /* Skip '\' */
298295367Sdes					arg[j++] = s[i];
299295367Sdes				} else {
300295367Sdes					/* Unrecognised escape */
301295367Sdes					arg[j++] = s[i];
302295367Sdes				}
303295367Sdes			} else if (quote == 0 && (s[i] == ' ' || s[i] == '\t'))
304295367Sdes				break; /* done */
305295367Sdes			else if (quote != 0 && s[i] == quote)
306295367Sdes				break; /* done */
307295367Sdes			else
308295367Sdes				arg[j++] = s[i];
309295367Sdes		}
310295367Sdes		if (s[i] == '\0') {
311295367Sdes			if (quote != 0) {
312295367Sdes				/* Ran out of string looking for close quote */
313295367Sdes				r = SSH_ERR_INVALID_FORMAT;
314295367Sdes				goto out;
315295367Sdes			}
316295367Sdes			break;
317295367Sdes		}
318295367Sdes	}
319295367Sdes	/* Success */
320295367Sdes	*argcp = argc;
321295367Sdes	*argvp = argv;
322295367Sdes	argc = 0;
323295367Sdes	argv = NULL;
324295367Sdes	r = 0;
325295367Sdes out:
326295367Sdes	if (argc != 0 && argv != NULL) {
327295367Sdes		for (i = 0; i < argc; i++)
328295367Sdes			free(argv[i]);
329295367Sdes		free(argv);
330295367Sdes	}
331295367Sdes	return r;
332295367Sdes}
333295367Sdes
334295367Sdes/*
335295367Sdes * Reassemble an argument vector into a string, quoting and escaping as
336295367Sdes * necessary. Caller must free returned string.
337295367Sdes */
338295367Sdesstatic char *
339295367Sdesassemble_argv(int argc, char **argv)
340295367Sdes{
341295367Sdes	int i, j, ws, r;
342295367Sdes	char c, *ret;
343295367Sdes	struct sshbuf *buf, *arg;
344295367Sdes
345295367Sdes	if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL)
346295367Sdes		fatal("%s: sshbuf_new failed", __func__);
347295367Sdes
348295367Sdes	for (i = 0; i < argc; i++) {
349295367Sdes		ws = 0;
350295367Sdes		sshbuf_reset(arg);
351295367Sdes		for (j = 0; argv[i][j] != '\0'; j++) {
352295367Sdes			r = 0;
353295367Sdes			c = argv[i][j];
354295367Sdes			switch (c) {
355295367Sdes			case ' ':
356295367Sdes			case '\t':
357295367Sdes				ws = 1;
358295367Sdes				r = sshbuf_put_u8(arg, c);
359295367Sdes				break;
360295367Sdes			case '\\':
361295367Sdes			case '\'':
362295367Sdes			case '"':
363295367Sdes				if ((r = sshbuf_put_u8(arg, '\\')) != 0)
364295367Sdes					break;
365295367Sdes				/* FALLTHROUGH */
366295367Sdes			default:
367295367Sdes				r = sshbuf_put_u8(arg, c);
368295367Sdes				break;
369295367Sdes			}
370295367Sdes			if (r != 0)
371295367Sdes				fatal("%s: sshbuf_put_u8: %s",
372295367Sdes				    __func__, ssh_err(r));
373295367Sdes		}
374295367Sdes		if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) ||
375295367Sdes		    (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) ||
376295367Sdes		    (r = sshbuf_putb(buf, arg)) != 0 ||
377295367Sdes		    (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0))
378295367Sdes			fatal("%s: buffer error: %s", __func__, ssh_err(r));
379295367Sdes	}
380295367Sdes	if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL)
381295367Sdes		fatal("%s: malloc failed", __func__);
382295367Sdes	memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf));
383295367Sdes	ret[sshbuf_len(buf)] = '\0';
384295367Sdes	sshbuf_free(buf);
385295367Sdes	sshbuf_free(arg);
386295367Sdes	return ret;
387295367Sdes}
388295367Sdes
389295367Sdes/*
390295367Sdes * Runs command in a subprocess. Returns pid on success and a FILE* to the
391295367Sdes * subprocess' stdout or 0 on failure.
392295367Sdes * NB. "command" is only used for logging.
393295367Sdes */
394295367Sdesstatic pid_t
395295367Sdessubprocess(const char *tag, struct passwd *pw, const char *command,
396295367Sdes    int ac, char **av, FILE **child)
397295367Sdes{
398295367Sdes	FILE *f;
399295367Sdes	struct stat st;
400295367Sdes	int devnull, p[2], i;
401295367Sdes	pid_t pid;
402295367Sdes	char *cp, errmsg[512];
403295367Sdes	u_int envsize;
404295367Sdes	char **child_env;
405295367Sdes
406295367Sdes	*child = NULL;
407295367Sdes
408295367Sdes	debug3("%s: %s command \"%s\" running as %s", __func__,
409295367Sdes	    tag, command, pw->pw_name);
410295367Sdes
411295367Sdes	/* Verify the path exists and is safe-ish to execute */
412295367Sdes	if (*av[0] != '/') {
413295367Sdes		error("%s path is not absolute", tag);
414295367Sdes		return 0;
415295367Sdes	}
416295367Sdes	temporarily_use_uid(pw);
417295367Sdes	if (stat(av[0], &st) < 0) {
418295367Sdes		error("Could not stat %s \"%s\": %s", tag,
419295367Sdes		    av[0], strerror(errno));
420295367Sdes		restore_uid();
421295367Sdes		return 0;
422295367Sdes	}
423295367Sdes	if (auth_secure_path(av[0], &st, NULL, 0,
424295367Sdes	    errmsg, sizeof(errmsg)) != 0) {
425295367Sdes		error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
426295367Sdes		restore_uid();
427295367Sdes		return 0;
428295367Sdes	}
429295367Sdes
430295367Sdes	/*
431295367Sdes	 * Run the command; stderr is left in place, stdout is the
432295367Sdes	 * authorized_keys output.
433295367Sdes	 */
434295367Sdes	if (pipe(p) != 0) {
435295367Sdes		error("%s: pipe: %s", tag, strerror(errno));
436295367Sdes		restore_uid();
437295367Sdes		return 0;
438295367Sdes	}
439295367Sdes
440295367Sdes	/*
441295367Sdes	 * Don't want to call this in the child, where it can fatal() and
442295367Sdes	 * run cleanup_exit() code.
443295367Sdes	 */
444295367Sdes	restore_uid();
445295367Sdes
446295367Sdes	switch ((pid = fork())) {
447295367Sdes	case -1: /* error */
448295367Sdes		error("%s: fork: %s", tag, strerror(errno));
449295367Sdes		close(p[0]);
450295367Sdes		close(p[1]);
451295367Sdes		return 0;
452295367Sdes	case 0: /* child */
453295367Sdes		/* Prepare a minimal environment for the child. */
454295367Sdes		envsize = 5;
455295367Sdes		child_env = xcalloc(sizeof(*child_env), envsize);
456295367Sdes		child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
457295367Sdes		child_set_env(&child_env, &envsize, "USER", pw->pw_name);
458295367Sdes		child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
459295367Sdes		child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
460295367Sdes		if ((cp = getenv("LANG")) != NULL)
461295367Sdes			child_set_env(&child_env, &envsize, "LANG", cp);
462295367Sdes
463295367Sdes		for (i = 0; i < NSIG; i++)
464295367Sdes			signal(i, SIG_DFL);
465295367Sdes
466295367Sdes		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
467295367Sdes			error("%s: open %s: %s", tag, _PATH_DEVNULL,
468295367Sdes			    strerror(errno));
469295367Sdes			_exit(1);
470295367Sdes		}
471295367Sdes		/* Keep stderr around a while longer to catch errors */
472295367Sdes		if (dup2(devnull, STDIN_FILENO) == -1 ||
473295367Sdes		    dup2(p[1], STDOUT_FILENO) == -1) {
474295367Sdes			error("%s: dup2: %s", tag, strerror(errno));
475295367Sdes			_exit(1);
476295367Sdes		}
477295367Sdes		closefrom(STDERR_FILENO + 1);
478295367Sdes
479295367Sdes		/* Don't use permanently_set_uid() here to avoid fatal() */
480295367Sdes		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
481295367Sdes			error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
482295367Sdes			    strerror(errno));
483295367Sdes			_exit(1);
484295367Sdes		}
485295367Sdes		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
486295367Sdes			error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
487295367Sdes			    strerror(errno));
488295367Sdes			_exit(1);
489295367Sdes		}
490295367Sdes		/* stdin is pointed to /dev/null at this point */
491295367Sdes		if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
492295367Sdes			error("%s: dup2: %s", tag, strerror(errno));
493295367Sdes			_exit(1);
494295367Sdes		}
495295367Sdes
496295367Sdes		execve(av[0], av, child_env);
497295367Sdes		error("%s exec \"%s\": %s", tag, command, strerror(errno));
498295367Sdes		_exit(127);
499295367Sdes	default: /* parent */
500295367Sdes		break;
501295367Sdes	}
502295367Sdes
503295367Sdes	close(p[1]);
504295367Sdes	if ((f = fdopen(p[0], "r")) == NULL) {
505295367Sdes		error("%s: fdopen: %s", tag, strerror(errno));
506295367Sdes		close(p[0]);
507295367Sdes		/* Don't leave zombie child */
508295367Sdes		kill(pid, SIGTERM);
509295367Sdes		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
510295367Sdes			;
511295367Sdes		return 0;
512295367Sdes	}
513295367Sdes	/* Success */
514295367Sdes	debug3("%s: %s pid %ld", __func__, tag, (long)pid);
515295367Sdes	*child = f;
516295367Sdes	return pid;
517295367Sdes}
518295367Sdes
519295367Sdes/* Returns 0 if pid exited cleanly, non-zero otherwise */
520295367Sdesstatic int
521295367Sdesexited_cleanly(pid_t pid, const char *tag, const char *cmd)
522295367Sdes{
523295367Sdes	int status;
524295367Sdes
525295367Sdes	while (waitpid(pid, &status, 0) == -1) {
526295367Sdes		if (errno != EINTR) {
527295367Sdes			error("%s: waitpid: %s", tag, strerror(errno));
528295367Sdes			return -1;
529295367Sdes		}
530295367Sdes	}
531295367Sdes	if (WIFSIGNALED(status)) {
532295367Sdes		error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status));
533295367Sdes		return -1;
534295367Sdes	} else if (WEXITSTATUS(status) != 0) {
535295367Sdes		error("%s %s failed, status %d", tag, cmd, WEXITSTATUS(status));
536295367Sdes		return -1;
537295367Sdes	}
538295367Sdes	return 0;
539295367Sdes}
540295367Sdes
541295367Sdesstatic int
542295367Sdesmatch_principals_option(const char *principal_list, struct sshkey_cert *cert)
543295367Sdes{
544215116Sdes	char *result;
545215116Sdes	u_int i;
546215116Sdes
547215116Sdes	/* XXX percent_expand() sequences for authorized_principals? */
548215116Sdes
549215116Sdes	for (i = 0; i < cert->nprincipals; i++) {
550215116Sdes		if ((result = match_list(cert->principals[i],
551215116Sdes		    principal_list, NULL)) != NULL) {
552215116Sdes			debug3("matched principal from key options \"%.100s\"",
553215116Sdes			    result);
554255767Sdes			free(result);
555215116Sdes			return 1;
556215116Sdes		}
557215116Sdes	}
558215116Sdes	return 0;
559215116Sdes}
560215116Sdes
561215116Sdesstatic int
562295367Sdesprocess_principals(FILE *f, char *file, struct passwd *pw,
563295367Sdes    struct sshkey_cert *cert)
564215116Sdes{
565215116Sdes	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
566215116Sdes	u_long linenum = 0;
567215116Sdes	u_int i;
568215116Sdes
569215116Sdes	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
570215116Sdes		/* Skip leading whitespace. */
571215116Sdes		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
572215116Sdes			;
573215116Sdes		/* Skip blank and comment lines. */
574215116Sdes		if ((ep = strchr(cp, '#')) != NULL)
575215116Sdes			*ep = '\0';
576215116Sdes		if (!*cp || *cp == '\n')
577215116Sdes			continue;
578215116Sdes		/* Trim trailing whitespace. */
579215116Sdes		ep = cp + strlen(cp) - 1;
580215116Sdes		while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
581215116Sdes			*ep-- = '\0';
582215116Sdes		/*
583215116Sdes		 * If the line has internal whitespace then assume it has
584215116Sdes		 * key options.
585215116Sdes		 */
586215116Sdes		line_opts = NULL;
587215116Sdes		if ((ep = strrchr(cp, ' ')) != NULL ||
588215116Sdes		    (ep = strrchr(cp, '\t')) != NULL) {
589215116Sdes			for (; *ep == ' ' || *ep == '\t'; ep++)
590221420Sdes				;
591215116Sdes			line_opts = cp;
592215116Sdes			cp = ep;
593215116Sdes		}
594215116Sdes		for (i = 0; i < cert->nprincipals; i++) {
595215116Sdes			if (strcmp(cp, cert->principals[i]) == 0) {
596295367Sdes				debug3("%s:%lu: matched principal \"%.100s\"",
597295367Sdes				    file == NULL ? "(command)" : file,
598295367Sdes				    linenum, cert->principals[i]);
599215116Sdes				if (auth_parse_options(pw, line_opts,
600215116Sdes				    file, linenum) != 1)
601215116Sdes					continue;
602215116Sdes				return 1;
603215116Sdes			}
604215116Sdes		}
605215116Sdes	}
606295367Sdes	return 0;
607295367Sdes}
608295367Sdes
609295367Sdesstatic int
610295367Sdesmatch_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
611295367Sdes{
612295367Sdes	FILE *f;
613295367Sdes	int success;
614295367Sdes
615295367Sdes	temporarily_use_uid(pw);
616295367Sdes	debug("trying authorized principals file %s", file);
617295367Sdes	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
618295367Sdes		restore_uid();
619295367Sdes		return 0;
620295367Sdes	}
621295367Sdes	success = process_principals(f, file, pw, cert);
622215116Sdes	fclose(f);
623215116Sdes	restore_uid();
624295367Sdes	return success;
625248619Sdes}
626215116Sdes
627248619Sdes/*
628295367Sdes * Checks whether principal is allowed in output of command.
629295367Sdes * returns 1 if the principal is allowed or 0 otherwise.
630295367Sdes */
631295367Sdesstatic int
632295367Sdesmatch_principals_command(struct passwd *user_pw, struct sshkey_cert *cert)
633295367Sdes{
634295367Sdes	FILE *f = NULL;
635295367Sdes	int ok, found_principal = 0;
636295367Sdes	struct passwd *pw;
637295367Sdes	int i, ac = 0, uid_swapped = 0;
638295367Sdes	pid_t pid;
639295367Sdes	char *tmp, *username = NULL, *command = NULL, **av = NULL;
640295367Sdes	void (*osigchld)(int);
641295367Sdes
642295367Sdes	if (options.authorized_principals_command == NULL)
643295367Sdes		return 0;
644295367Sdes	if (options.authorized_principals_command_user == NULL) {
645295367Sdes		error("No user for AuthorizedPrincipalsCommand specified, "
646295367Sdes		    "skipping");
647295367Sdes		return 0;
648295367Sdes	}
649295367Sdes
650295367Sdes	/*
651295367Sdes	 * NB. all returns later this function should go via "out" to
652295367Sdes	 * ensure the original SIGCHLD handler is restored properly.
653295367Sdes	 */
654295367Sdes	osigchld = signal(SIGCHLD, SIG_DFL);
655295367Sdes
656295367Sdes	/* Prepare and verify the user for the command */
657295367Sdes	username = percent_expand(options.authorized_principals_command_user,
658295367Sdes	    "u", user_pw->pw_name, (char *)NULL);
659295367Sdes	pw = getpwnam(username);
660295367Sdes	if (pw == NULL) {
661295367Sdes		error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
662295367Sdes		    username, strerror(errno));
663295367Sdes		goto out;
664295367Sdes	}
665295367Sdes
666295367Sdes	/* Turn the command into an argument vector */
667295367Sdes	if (split_argv(options.authorized_principals_command, &ac, &av) != 0) {
668295367Sdes		error("AuthorizedPrincipalsCommand \"%s\" contains "
669295367Sdes		    "invalid quotes", command);
670295367Sdes		goto out;
671295367Sdes	}
672295367Sdes	if (ac == 0) {
673295367Sdes		error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
674295367Sdes		    command);
675295367Sdes		goto out;
676295367Sdes	}
677295367Sdes	for (i = 1; i < ac; i++) {
678295367Sdes		tmp = percent_expand(av[i],
679295367Sdes		    "u", user_pw->pw_name,
680295367Sdes		    "h", user_pw->pw_dir,
681295367Sdes		    (char *)NULL);
682295367Sdes		if (tmp == NULL)
683295367Sdes			fatal("%s: percent_expand failed", __func__);
684295367Sdes		free(av[i]);
685295367Sdes		av[i] = tmp;
686295367Sdes	}
687295367Sdes	/* Prepare a printable command for logs, etc. */
688295367Sdes	command = assemble_argv(ac, av);
689295367Sdes
690295367Sdes	if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
691295367Sdes	    ac, av, &f)) == 0)
692295367Sdes		goto out;
693295367Sdes
694295367Sdes	uid_swapped = 1;
695295367Sdes	temporarily_use_uid(pw);
696295367Sdes
697295367Sdes	ok = process_principals(f, NULL, pw, cert);
698295367Sdes
699295367Sdes	if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0)
700295367Sdes		goto out;
701295367Sdes
702295367Sdes	/* Read completed successfully */
703295367Sdes	found_principal = ok;
704295367Sdes out:
705295367Sdes	if (f != NULL)
706295367Sdes		fclose(f);
707295367Sdes	signal(SIGCHLD, osigchld);
708295367Sdes	for (i = 0; i < ac; i++)
709295367Sdes		free(av[i]);
710295367Sdes	free(av);
711295367Sdes	if (uid_swapped)
712295367Sdes		restore_uid();
713295367Sdes	free(command);
714295367Sdes	free(username);
715295367Sdes	return found_principal;
716295367Sdes}
717295367Sdes/*
718248619Sdes * Checks whether key is allowed in authorized_keys-format file,
719248619Sdes * returns 1 if the key is allowed or 0 otherwise.
720248619Sdes */
72198675Sdesstatic int
722248619Sdescheck_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
72398675Sdes{
724146998Sdes	char line[SSH_MAX_PUBKEY_BYTES];
725204917Sdes	const char *reason;
72698675Sdes	int found_key = 0;
72798675Sdes	u_long linenum = 0;
72898675Sdes	Key *found;
72998675Sdes	char *fp;
73098675Sdes
73198675Sdes	found_key = 0;
73298675Sdes
733255767Sdes	found = NULL;
734146998Sdes	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
735137015Sdes		char *cp, *key_options = NULL;
736255767Sdes		if (found != NULL)
737255767Sdes			key_free(found);
738255767Sdes		found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
739204917Sdes		auth_clear_options();
740204917Sdes
74198675Sdes		/* Skip leading whitespace, empty and comment lines. */
74298675Sdes		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
74398675Sdes			;
74498675Sdes		if (!*cp || *cp == '\n' || *cp == '#')
74598675Sdes			continue;
74698675Sdes
74798675Sdes		if (key_read(found, &cp) != 1) {
74898675Sdes			/* no key?  check if there are options for this key */
74998675Sdes			int quoted = 0;
75098675Sdes			debug2("user_key_allowed: check options: '%s'", cp);
751137015Sdes			key_options = cp;
75298675Sdes			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
75398675Sdes				if (*cp == '\\' && cp[1] == '"')
75498675Sdes					cp++;	/* Skip both */
75598675Sdes				else if (*cp == '"')
75698675Sdes					quoted = !quoted;
75798675Sdes			}
75898675Sdes			/* Skip remaining whitespace. */
75998675Sdes			for (; *cp == ' ' || *cp == '\t'; cp++)
76098675Sdes				;
76198675Sdes			if (key_read(found, &cp) != 1) {
76298675Sdes				debug2("user_key_allowed: advance: '%s'", cp);
76398675Sdes				/* still no key?  advance to next line*/
76498675Sdes				continue;
76598675Sdes			}
76698675Sdes		}
767215116Sdes		if (key_is_cert(key)) {
768215116Sdes			if (!key_equal(found, key->cert->signature_key))
769215116Sdes				continue;
770215116Sdes			if (auth_parse_options(pw, key_options, file,
771215116Sdes			    linenum) != 1)
772215116Sdes				continue;
773204917Sdes			if (!key_is_cert_authority)
774204917Sdes				continue;
775295367Sdes			if ((fp = sshkey_fingerprint(found,
776295367Sdes			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
777295367Sdes				continue;
778207319Sdes			debug("matching CA found: file %s, line %lu, %s %s",
779207319Sdes			    file, linenum, key_type(found), fp);
780215116Sdes			/*
781215116Sdes			 * If the user has specified a list of principals as
782215116Sdes			 * a key option, then prefer that list to matching
783215116Sdes			 * their username in the certificate principals list.
784215116Sdes			 */
785215116Sdes			if (authorized_principals != NULL &&
786215116Sdes			    !match_principals_option(authorized_principals,
787215116Sdes			    key->cert)) {
788215116Sdes				reason = "Certificate does not contain an "
789215116Sdes				    "authorized principal";
790215116Sdes fail_reason:
791255767Sdes				free(fp);
792204917Sdes				error("%s", reason);
793204917Sdes				auth_debug_add("%s", reason);
794204917Sdes				continue;
795204917Sdes			}
796215116Sdes			if (key_cert_check_authority(key, 0, 0,
797215116Sdes			    authorized_principals == NULL ? pw->pw_name : NULL,
798215116Sdes			    &reason) != 0)
799215116Sdes				goto fail_reason;
800215116Sdes			if (auth_cert_options(key, pw) != 0) {
801255767Sdes				free(fp);
802204917Sdes				continue;
803207319Sdes			}
804296781Sdes			verbose("Accepted certificate ID \"%s\" (serial %llu) "
805207319Sdes			    "signed by %s CA %s via %s", key->cert->key_id,
806296781Sdes			    (unsigned long long)key->cert->serial,
807207319Sdes			    key_type(found), fp, file);
808255767Sdes			free(fp);
80998675Sdes			found_key = 1;
810204917Sdes			break;
811215116Sdes		} else if (key_equal(found, key)) {
812215116Sdes			if (auth_parse_options(pw, key_options, file,
813215116Sdes			    linenum) != 1)
814215116Sdes				continue;
815215116Sdes			if (key_is_cert_authority)
816215116Sdes				continue;
817295367Sdes			if ((fp = sshkey_fingerprint(found,
818295367Sdes			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
819295367Sdes				continue;
820255767Sdes			debug("matching key found: file %s, line %lu %s %s",
821255767Sdes			    file, linenum, key_type(found), fp);
822255767Sdes			free(fp);
823295367Sdes			found_key = 1;
82498675Sdes			break;
82598675Sdes		}
82698675Sdes	}
827255767Sdes	if (found != NULL)
828255767Sdes		key_free(found);
82998675Sdes	if (!found_key)
83098675Sdes		debug2("key not found");
83198675Sdes	return found_key;
83298675Sdes}
83398675Sdes
834204917Sdes/* Authenticate a certificate key against TrustedUserCAKeys */
835204917Sdesstatic int
836204917Sdesuser_cert_trusted_ca(struct passwd *pw, Key *key)
837204917Sdes{
838215116Sdes	char *ca_fp, *principals_file = NULL;
839204917Sdes	const char *reason;
840295367Sdes	int ret = 0, found_principal = 0, use_authorized_principals;
841204917Sdes
842204917Sdes	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
843204917Sdes		return 0;
844204917Sdes
845295367Sdes	if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
846295367Sdes	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
847295367Sdes		return 0;
848204917Sdes
849295367Sdes	if (sshkey_in_file(key->cert->signature_key,
850295367Sdes	    options.trusted_user_ca_keys, 1, 0) != 0) {
851204917Sdes		debug2("%s: CA %s %s is not listed in %s", __func__,
852204917Sdes		    key_type(key->cert->signature_key), ca_fp,
853204917Sdes		    options.trusted_user_ca_keys);
854204917Sdes		goto out;
855204917Sdes	}
856215116Sdes	/*
857215116Sdes	 * If AuthorizedPrincipals is in use, then compare the certificate
858215116Sdes	 * principals against the names in that file rather than matching
859215116Sdes	 * against the username.
860215116Sdes	 */
861215116Sdes	if ((principals_file = authorized_principals_file(pw)) != NULL) {
862295367Sdes		if (match_principals_file(principals_file, pw, key->cert))
863295367Sdes			found_principal = 1;
864295367Sdes	}
865295367Sdes	/* Try querying command if specified */
866295367Sdes	if (!found_principal && match_principals_command(pw, key->cert))
867295367Sdes		found_principal = 1;
868295367Sdes	/* If principals file or command is specified, then require a match */
869295367Sdes	use_authorized_principals = principals_file != NULL ||
870295367Sdes            options.authorized_principals_command != NULL;
871295367Sdes	if (!found_principal && use_authorized_principals) {
872295367Sdes		reason = "Certificate does not contain an authorized principal";
873215116Sdes fail_reason:
874295367Sdes		error("%s", reason);
875295367Sdes		auth_debug_add("%s", reason);
876295367Sdes		goto out;
877204917Sdes	}
878215116Sdes	if (key_cert_check_authority(key, 0, 1,
879295367Sdes	    use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
880215116Sdes		goto fail_reason;
881215116Sdes	if (auth_cert_options(key, pw) != 0)
882204917Sdes		goto out;
883204917Sdes
884296781Sdes	verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
885296781Sdes	    "%s CA %s via %s", key->cert->key_id,
886296781Sdes	    (unsigned long long)key->cert->serial,
887296781Sdes	    key_type(key->cert->signature_key), ca_fp,
888207319Sdes	    options.trusted_user_ca_keys);
889204917Sdes	ret = 1;
890204917Sdes
891204917Sdes out:
892255767Sdes	free(principals_file);
893255767Sdes	free(ca_fp);
894204917Sdes	return ret;
895204917Sdes}
896204917Sdes
897248619Sdes/*
898248619Sdes * Checks whether key is allowed in file.
899248619Sdes * returns 1 if the key is allowed or 0 otherwise.
900248619Sdes */
901248619Sdesstatic int
902248619Sdesuser_key_allowed2(struct passwd *pw, Key *key, char *file)
903248619Sdes{
904248619Sdes	FILE *f;
905248619Sdes	int found_key = 0;
906248619Sdes
907248619Sdes	/* Temporarily use the user's uid. */
908248619Sdes	temporarily_use_uid(pw);
909248619Sdes
910248619Sdes	debug("trying public key file %s", file);
911248619Sdes	if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
912248619Sdes		found_key = check_authkeys_file(f, file, key, pw);
913248619Sdes		fclose(f);
914248619Sdes	}
915248619Sdes
916248619Sdes	restore_uid();
917248619Sdes	return found_key;
918248619Sdes}
919248619Sdes
920248619Sdes/*
921248619Sdes * Checks whether key is allowed in output of command.
922248619Sdes * returns 1 if the key is allowed or 0 otherwise.
923248619Sdes */
924248619Sdesstatic int
925248619Sdesuser_key_command_allowed2(struct passwd *user_pw, Key *key)
926248619Sdes{
927295367Sdes	FILE *f = NULL;
928295367Sdes	int r, ok, found_key = 0;
929248619Sdes	struct passwd *pw;
930295367Sdes	int i, uid_swapped = 0, ac = 0;
931248619Sdes	pid_t pid;
932295367Sdes	char *username = NULL, *key_fp = NULL, *keytext = NULL;
933295367Sdes	char *tmp, *command = NULL, **av = NULL;
934295367Sdes	void (*osigchld)(int);
935248619Sdes
936295367Sdes	if (options.authorized_keys_command == NULL)
937248619Sdes		return 0;
938248619Sdes	if (options.authorized_keys_command_user == NULL) {
939248619Sdes		error("No user for AuthorizedKeysCommand specified, skipping");
940248619Sdes		return 0;
941248619Sdes	}
942248619Sdes
943295367Sdes	/*
944295367Sdes	 * NB. all returns later this function should go via "out" to
945295367Sdes	 * ensure the original SIGCHLD handler is restored properly.
946295367Sdes	 */
947295367Sdes	osigchld = signal(SIGCHLD, SIG_DFL);
948295367Sdes
949295367Sdes	/* Prepare and verify the user for the command */
950248619Sdes	username = percent_expand(options.authorized_keys_command_user,
951248619Sdes	    "u", user_pw->pw_name, (char *)NULL);
952248619Sdes	pw = getpwnam(username);
953248619Sdes	if (pw == NULL) {
954248619Sdes		error("AuthorizedKeysCommandUser \"%s\" not found: %s",
955248619Sdes		    username, strerror(errno));
956295367Sdes		goto out;
957248619Sdes	}
958248619Sdes
959295367Sdes	/* Prepare AuthorizedKeysCommand */
960295367Sdes	if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
961295367Sdes	    SSH_FP_DEFAULT)) == NULL) {
962295367Sdes		error("%s: sshkey_fingerprint failed", __func__);
963248619Sdes		goto out;
964248619Sdes	}
965295367Sdes	if ((r = sshkey_to_base64(key, &keytext)) != 0) {
966295367Sdes		error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
967248619Sdes		goto out;
968248619Sdes	}
969248619Sdes
970295367Sdes	/* Turn the command into an argument vector */
971295367Sdes	if (split_argv(options.authorized_keys_command, &ac, &av) != 0) {
972295367Sdes		error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
973295367Sdes		    command);
974248619Sdes		goto out;
975248619Sdes	}
976295367Sdes	if (ac == 0) {
977295367Sdes		error("AuthorizedKeysCommand \"%s\" yielded no arguments",
978295367Sdes		    command);
979295367Sdes		goto out;
980295367Sdes	}
981295367Sdes	for (i = 1; i < ac; i++) {
982295367Sdes		tmp = percent_expand(av[i],
983295367Sdes		    "u", user_pw->pw_name,
984295367Sdes		    "h", user_pw->pw_dir,
985295367Sdes		    "t", sshkey_ssh_name(key),
986295367Sdes		    "f", key_fp,
987295367Sdes		    "k", keytext,
988295367Sdes		    (char *)NULL);
989295367Sdes		if (tmp == NULL)
990295367Sdes			fatal("%s: percent_expand failed", __func__);
991295367Sdes		free(av[i]);
992295367Sdes		av[i] = tmp;
993295367Sdes	}
994295367Sdes	/* Prepare a printable command for logs, etc. */
995295367Sdes	command = assemble_argv(ac, av);
996248619Sdes
997248619Sdes	/*
998295367Sdes	 * If AuthorizedKeysCommand was run without arguments
999295367Sdes	 * then fall back to the old behaviour of passing the
1000295367Sdes	 * target username as a single argument.
1001248619Sdes	 */
1002295367Sdes	if (ac == 1) {
1003295367Sdes		av = xreallocarray(av, ac + 2, sizeof(*av));
1004295367Sdes		av[1] = xstrdup(user_pw->pw_name);
1005295367Sdes		av[2] = NULL;
1006295367Sdes		/* Fix up command too, since it is used in log messages */
1007295367Sdes		free(command);
1008295367Sdes		xasprintf(&command, "%s %s", av[0], av[1]);
1009295367Sdes	}
1010248619Sdes
1011295367Sdes	if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
1012295367Sdes	    ac, av, &f)) == 0)
1013295367Sdes		goto out;
1014248619Sdes
1015295367Sdes	uid_swapped = 1;
1016248619Sdes	temporarily_use_uid(pw);
1017248619Sdes
1018248619Sdes	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
1019248619Sdes
1020295367Sdes	if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0)
1021248619Sdes		goto out;
1022295367Sdes
1023295367Sdes	/* Read completed successfully */
1024248619Sdes	found_key = ok;
1025248619Sdes out:
1026295367Sdes	if (f != NULL)
1027295367Sdes		fclose(f);
1028295367Sdes	signal(SIGCHLD, osigchld);
1029295367Sdes	for (i = 0; i < ac; i++)
1030295367Sdes		free(av[i]);
1031295367Sdes	free(av);
1032295367Sdes	if (uid_swapped)
1033295367Sdes		restore_uid();
1034295367Sdes	free(command);
1035295367Sdes	free(username);
1036295367Sdes	free(key_fp);
1037295367Sdes	free(keytext);
1038248619Sdes	return found_key;
1039248619Sdes}
1040248619Sdes
1041248619Sdes/*
1042248619Sdes * Check whether key authenticates and authorises the user.
1043248619Sdes */
104498675Sdesint
1045295367Sdesuser_key_allowed(struct passwd *pw, Key *key, int auth_attempt)
104698675Sdes{
1047226046Sdes	u_int success, i;
104898675Sdes	char *file;
104998675Sdes
1050204917Sdes	if (auth_key_is_revoked(key))
1051204917Sdes		return 0;
1052204917Sdes	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
1053204917Sdes		return 0;
1054204917Sdes
1055204917Sdes	success = user_cert_trusted_ca(pw, key);
1056204917Sdes	if (success)
1057204917Sdes		return success;
1058204917Sdes
1059248619Sdes	success = user_key_command_allowed2(pw, key);
1060248619Sdes	if (success > 0)
1061248619Sdes		return success;
1062248619Sdes
1063226046Sdes	for (i = 0; !success && i < options.num_authkeys_files; i++) {
1064248619Sdes
1065248619Sdes		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1066248619Sdes			continue;
1067226046Sdes		file = expand_authorized_keys(
1068226046Sdes		    options.authorized_keys_files[i], pw);
1069248619Sdes
1070226046Sdes		success = user_key_allowed2(pw, key, file);
1071255767Sdes		free(file);
1072226046Sdes	}
107398675Sdes
107498675Sdes	return success;
107598675Sdes}
107698675Sdes
1077295367Sdes/* Records a public key in the list of previously-successful keys */
1078295367Sdesvoid
1079295367Sdesauth2_record_userkey(Authctxt *authctxt, struct sshkey *key)
1080295367Sdes{
1081295367Sdes	struct sshkey **tmp;
1082295367Sdes
1083295367Sdes	if (authctxt->nprev_userkeys >= INT_MAX ||
1084295367Sdes	    (tmp = reallocarray(authctxt->prev_userkeys,
1085295367Sdes	    authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL)
1086295367Sdes		fatal("%s: reallocarray failed", __func__);
1087295367Sdes	authctxt->prev_userkeys = tmp;
1088295367Sdes	authctxt->prev_userkeys[authctxt->nprev_userkeys] = key;
1089295367Sdes	authctxt->nprev_userkeys++;
1090295367Sdes}
1091295367Sdes
1092295367Sdes/* Checks whether a key has already been used successfully for authentication */
1093295367Sdesint
1094295367Sdesauth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key)
1095295367Sdes{
1096295367Sdes	u_int i;
1097295367Sdes
1098295367Sdes	for (i = 0; i < authctxt->nprev_userkeys; i++) {
1099295367Sdes		if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) {
1100295367Sdes			return 1;
1101295367Sdes		}
1102295367Sdes	}
1103295367Sdes	return 0;
1104295367Sdes}
1105295367Sdes
110698675SdesAuthmethod method_pubkey = {
110798675Sdes	"publickey",
110898675Sdes	userauth_pubkey,
110998675Sdes	&options.pubkey_authentication
111098675Sdes};
1111