1ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/* $OpenBSD: auth2-pubkey.c,v 1.53 2015/06/15 18:44:22 jsing Exp $ */
2bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/*
3bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *
5bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Redistribution and use in source and binary forms, with or without
6bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * modification, are permitted provided that the following conditions
7bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * are met:
8bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * 1. Redistributions of source code must retain the above copyright
9bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    notice, this list of conditions and the following disclaimer.
10bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * 2. Redistributions in binary form must reproduce the above copyright
11bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    notice, this list of conditions and the following disclaimer in the
12bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    documentation and/or other materials provided with the distribution.
13bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *
14bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman */
25bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
26bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "includes.h"
27bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
28bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <sys/types.h>
29bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <sys/stat.h>
30d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <sys/wait.h>
31bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
32d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <errno.h>
33bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <fcntl.h>
34d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef HAVE_PATHS_H
35d059297112922cabb0c674840589be8db821fd9aAdam Langley# include <paths.h>
36d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif
37bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <pwd.h>
38d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <signal.h>
39bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <stdio.h>
40bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <stdarg.h>
41bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <string.h>
42bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <time.h>
43bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <unistd.h>
44d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <limits.h>
45bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
46bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "xmalloc.h"
47bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "ssh.h"
48bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "ssh2.h"
49bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "packet.h"
50bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "buffer.h"
51bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "log.h"
52d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "misc.h"
53bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "servconf.h"
54bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "compat.h"
55bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "key.h"
56bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "hostfile.h"
57bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "auth.h"
58bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "pathnames.h"
59bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "uidswap.h"
60bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "auth-options.h"
61bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "canohost.h"
62bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#ifdef GSSAPI
63bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "ssh-gss.h"
64bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
65bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "monitor_wrap.h"
66bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "authfile.h"
67bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "match.h"
68ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman#include "ssherr.h"
69ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman#include "channels.h" /* XXX for session.h */
70ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman#include "session.h" /* XXX for child_set_env(); refactor? */
71bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
72bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* import */
73bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanextern ServerOptions options;
74bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanextern u_char *session_id2;
75bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanextern u_int session_id2_len;
76bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
77bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic int
78bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanuserauth_pubkey(Authctxt *authctxt)
79bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
80bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	Buffer b;
81bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	Key *key = NULL;
82d059297112922cabb0c674840589be8db821fd9aAdam Langley	char *pkalg, *userstyle;
83bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_char *pkblob, *sig;
84bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int alen, blen, slen;
85bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	int have_sig, pktype;
86bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	int authenticated = 0;
87bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
88bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (!authctxt->valid) {
89bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug2("userauth_pubkey: disabled because of invalid user");
90bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return 0;
91bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
92bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	have_sig = packet_get_char();
93bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (datafellows & SSH_BUG_PKAUTH) {
94bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug2("userauth_pubkey: SSH_BUG_PKAUTH");
95bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* no explicit pkalg given */
96bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		pkblob = packet_get_string(&blen);
97bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_init(&b);
98bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_append(&b, pkblob, blen);
99bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* so we have to extract the pkalg from the pkblob */
100bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		pkalg = buffer_get_string(&b, &alen);
101bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_free(&b);
102bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	} else {
103bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		pkalg = packet_get_string(&alen);
104bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		pkblob = packet_get_string(&blen);
105bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
106bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	pktype = key_type_from_name(pkalg);
107bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (pktype == KEY_UNSPEC) {
108bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* this is perfectly legal */
109bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		logit("userauth_pubkey: unsupported public key algorithm: %s",
110bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    pkalg);
111bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		goto done;
112bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
113bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	key = key_from_blob(pkblob, blen);
114bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (key == NULL) {
115bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		error("userauth_pubkey: cannot decode key: %s", pkalg);
116bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		goto done;
117bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
118bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (key->type != pktype) {
119bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		error("userauth_pubkey: type mismatch for decoded key "
120bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    "(received %d, expected %d)", key->type, pktype);
121bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		goto done;
122bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
123d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (key_type_plain(key->type) == KEY_RSA &&
124d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (datafellows & SSH_BUG_RSASIGMD5) != 0) {
125d059297112922cabb0c674840589be8db821fd9aAdam Langley		logit("Refusing RSA key because client uses unsafe "
126d059297112922cabb0c674840589be8db821fd9aAdam Langley		    "signature scheme");
127d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto done;
128d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
129d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (auth2_userkey_already_used(authctxt, key)) {
130d059297112922cabb0c674840589be8db821fd9aAdam Langley		logit("refusing previously-used %s key", key_type(key));
131d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto done;
132d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
133ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (match_pattern_list(sshkey_ssh_name(key),
134ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    options.pubkey_key_types, 0) != 1) {
135d059297112922cabb0c674840589be8db821fd9aAdam Langley		logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
136d059297112922cabb0c674840589be8db821fd9aAdam Langley		    __func__, sshkey_ssh_name(key));
137d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto done;
138d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
139d059297112922cabb0c674840589be8db821fd9aAdam Langley
140bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (have_sig) {
141bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		sig = packet_get_string(&slen);
142bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		packet_check_eom();
143bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_init(&b);
144bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (datafellows & SSH_OLD_SESSIONID) {
145bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			buffer_append(&b, session_id2, session_id2_len);
146bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		} else {
147bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			buffer_put_string(&b, session_id2, session_id2_len);
148bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
149bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* reconstruct packet */
150bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
151d059297112922cabb0c674840589be8db821fd9aAdam Langley		xasprintf(&userstyle, "%s%s%s", authctxt->user,
152d059297112922cabb0c674840589be8db821fd9aAdam Langley		    authctxt->style ? ":" : "",
153d059297112922cabb0c674840589be8db821fd9aAdam Langley		    authctxt->style ? authctxt->style : "");
154d059297112922cabb0c674840589be8db821fd9aAdam Langley		buffer_put_cstring(&b, userstyle);
155d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(userstyle);
156bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_put_cstring(&b,
157bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    datafellows & SSH_BUG_PKSERVICE ?
158bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    "ssh-userauth" :
159bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    authctxt->service);
160bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (datafellows & SSH_BUG_PKAUTH) {
161bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			buffer_put_char(&b, have_sig);
162bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		} else {
163bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			buffer_put_cstring(&b, "publickey");
164bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			buffer_put_char(&b, have_sig);
165bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			buffer_put_cstring(&b, pkalg);
166bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
167bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_put_string(&b, pkblob, blen);
168bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#ifdef DEBUG_PK
169bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_dump(&b);
170bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
171d059297112922cabb0c674840589be8db821fd9aAdam Langley		pubkey_auth_info(authctxt, key, NULL);
172d059297112922cabb0c674840589be8db821fd9aAdam Langley
173bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* test for correct signature */
174bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		authenticated = 0;
175ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
176bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
177d059297112922cabb0c674840589be8db821fd9aAdam Langley		    buffer_len(&b))) == 1) {
178bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			authenticated = 1;
179d059297112922cabb0c674840589be8db821fd9aAdam Langley			/* Record the successful key to prevent reuse */
180d059297112922cabb0c674840589be8db821fd9aAdam Langley			auth2_record_userkey(authctxt, key);
181d059297112922cabb0c674840589be8db821fd9aAdam Langley			key = NULL; /* Don't free below */
182d059297112922cabb0c674840589be8db821fd9aAdam Langley		}
183bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		buffer_free(&b);
184d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(sig);
185bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	} else {
186bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug("test whether pkalg/pkblob are acceptable");
187bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		packet_check_eom();
188bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
189bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* XXX fake reply and always send PK_OK ? */
190bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/*
191bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * XXX this allows testing whether a user is allowed
192bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * to login: if you happen to have a valid pubkey this
193bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * message is sent. the message is NEVER sent at all
194bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * if a user is not allowed to login. is this an
195bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * issue? -markus
196bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 */
197ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) {
198bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			packet_start(SSH2_MSG_USERAUTH_PK_OK);
199bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			packet_put_string(pkalg, alen);
200bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			packet_put_string(pkblob, blen);
201bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			packet_send();
202bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			packet_write_wait();
203bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			authctxt->postponed = 1;
204bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
205bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
206bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (authenticated != 1)
207bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		auth_clear_options();
208bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmandone:
209bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
210bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (key != NULL)
211bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		key_free(key);
212d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(pkalg);
213d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(pkblob);
214bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return authenticated;
215bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
216bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
217d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid
218d059297112922cabb0c674840589be8db821fd9aAdam Langleypubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
219d059297112922cabb0c674840589be8db821fd9aAdam Langley{
220d059297112922cabb0c674840589be8db821fd9aAdam Langley	char *fp, *extra;
221d059297112922cabb0c674840589be8db821fd9aAdam Langley	va_list ap;
222d059297112922cabb0c674840589be8db821fd9aAdam Langley	int i;
223d059297112922cabb0c674840589be8db821fd9aAdam Langley
224d059297112922cabb0c674840589be8db821fd9aAdam Langley	extra = NULL;
225d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (fmt != NULL) {
226d059297112922cabb0c674840589be8db821fd9aAdam Langley		va_start(ap, fmt);
227d059297112922cabb0c674840589be8db821fd9aAdam Langley		i = vasprintf(&extra, fmt, ap);
228d059297112922cabb0c674840589be8db821fd9aAdam Langley		va_end(ap);
229d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (i < 0 || extra == NULL)
230d059297112922cabb0c674840589be8db821fd9aAdam Langley			fatal("%s: vasprintf failed", __func__);
231d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
232d059297112922cabb0c674840589be8db821fd9aAdam Langley
233d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (key_is_cert(key)) {
234d059297112922cabb0c674840589be8db821fd9aAdam Langley		fp = sshkey_fingerprint(key->cert->signature_key,
235d059297112922cabb0c674840589be8db821fd9aAdam Langley		    options.fingerprint_hash, SSH_FP_DEFAULT);
236d059297112922cabb0c674840589be8db821fd9aAdam Langley		auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s",
237d059297112922cabb0c674840589be8db821fd9aAdam Langley		    key_type(key), key->cert->key_id,
238d059297112922cabb0c674840589be8db821fd9aAdam Langley		    (unsigned long long)key->cert->serial,
239d059297112922cabb0c674840589be8db821fd9aAdam Langley		    key_type(key->cert->signature_key),
240d059297112922cabb0c674840589be8db821fd9aAdam Langley		    fp == NULL ? "(null)" : fp,
241d059297112922cabb0c674840589be8db821fd9aAdam Langley		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
242d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(fp);
243d059297112922cabb0c674840589be8db821fd9aAdam Langley	} else {
244d059297112922cabb0c674840589be8db821fd9aAdam Langley		fp = sshkey_fingerprint(key, options.fingerprint_hash,
245d059297112922cabb0c674840589be8db821fd9aAdam Langley		    SSH_FP_DEFAULT);
246d059297112922cabb0c674840589be8db821fd9aAdam Langley		auth_info(authctxt, "%s %s%s%s", key_type(key),
247d059297112922cabb0c674840589be8db821fd9aAdam Langley		    fp == NULL ? "(null)" : fp,
248d059297112922cabb0c674840589be8db821fd9aAdam Langley		    extra == NULL ? "" : ", ", extra == NULL ? "" : extra);
249d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(fp);
250d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
251d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(extra);
252d059297112922cabb0c674840589be8db821fd9aAdam Langley}
253d059297112922cabb0c674840589be8db821fd9aAdam Langley
254ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/*
255ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * Splits 's' into an argument vector. Handles quoted string and basic
256ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * escape characters (\\, \", \'). Caller must free the argument vector
257ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * and its members.
258ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman */
259ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanstatic int
260ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmansplit_argv(const char *s, int *argcp, char ***argvp)
261ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman{
262ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int r = SSH_ERR_INTERNAL_ERROR;
263ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int argc = 0, quote, i, j;
264ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char *arg, **argv = xcalloc(1, sizeof(*argv));
265ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
266ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	*argvp = NULL;
267ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	*argcp = 0;
268ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
269ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	for (i = 0; s[i] != '\0'; i++) {
270ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Skip leading whitespace */
271ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (s[i] == ' ' || s[i] == '\t')
272ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			continue;
273ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
274ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Start of a token */
275ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		quote = 0;
276ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (s[i] == '\\' &&
277ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\'))
278ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			i++;
279ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		else if (s[i] == '\'' || s[i] == '"')
280ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			quote = s[i++];
281ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
282ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		argv = xreallocarray(argv, (argc + 2), sizeof(*argv));
283ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1);
284ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		argv[argc] = NULL;
285ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
286ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Copy the token in, removing escapes */
287ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		for (j = 0; s[i] != '\0'; i++) {
288ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			if (s[i] == '\\') {
289ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				if (s[i + 1] == '\'' ||
290ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				    s[i + 1] == '\"' ||
291ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				    s[i + 1] == '\\') {
292ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman					i++; /* Skip '\' */
293ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman					arg[j++] = s[i];
294ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				} else {
295ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman					/* Unrecognised escape */
296ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman					arg[j++] = s[i];
297ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				}
298ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			} else if (quote == 0 && (s[i] == ' ' || s[i] == '\t'))
299ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				break; /* done */
300ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			else if (quote != 0 && s[i] == quote)
301ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				break; /* done */
302ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			else
303ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				arg[j++] = s[i];
304ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
305ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (s[i] == '\0') {
306ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			if (quote != 0) {
307ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				/* Ran out of string looking for close quote */
308ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				r = SSH_ERR_INVALID_FORMAT;
309ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				goto out;
310ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			}
311ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			break;
312ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
313ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
314ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Success */
315ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	*argcp = argc;
316ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	*argvp = argv;
317ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	argc = 0;
318ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	argv = NULL;
319ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	r = 0;
320ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman out:
321ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (argc != 0 && argv != NULL) {
322ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		for (i = 0; i < argc; i++)
323ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			free(argv[i]);
324ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		free(argv);
325ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
326ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return r;
327ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman}
328ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
329ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/*
330ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * Reassemble an argument vector into a string, quoting and escaping as
331ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * necessary. Caller must free returned string.
332ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman */
333ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanstatic char *
334ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanassemble_argv(int argc, char **argv)
335ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman{
336ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int i, j, ws, r;
337ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char c, *ret;
338ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	struct sshbuf *buf, *arg;
339ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
340ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL)
341ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		fatal("%s: sshbuf_new failed", __func__);
342ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
343ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	for (i = 0; i < argc; i++) {
344ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		ws = 0;
345ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		sshbuf_reset(arg);
346ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		for (j = 0; argv[i][j] != '\0'; j++) {
347ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			r = 0;
348ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			c = argv[i][j];
349ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			switch (c) {
350ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			case ' ':
351ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			case '\t':
352ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				ws = 1;
353ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				r = sshbuf_put_u8(arg, c);
354ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				break;
355ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			case '\\':
356ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			case '\'':
357ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			case '"':
358ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				if ((r = sshbuf_put_u8(arg, '\\')) != 0)
359ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman					break;
360ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				/* FALLTHROUGH */
361ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			default:
362ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				r = sshbuf_put_u8(arg, c);
363ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				break;
364ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			}
365ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			if (r != 0)
366ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				fatal("%s: sshbuf_put_u8: %s",
367ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				    __func__, ssh_err(r));
368ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
369ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) ||
370ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) ||
371ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    (r = sshbuf_putb(buf, arg)) != 0 ||
372ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0))
373ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			fatal("%s: buffer error: %s", __func__, ssh_err(r));
374ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
375ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL)
376ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		fatal("%s: malloc failed", __func__);
377ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf));
378ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	ret[sshbuf_len(buf)] = '\0';
379ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	sshbuf_free(buf);
380ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	sshbuf_free(arg);
381ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return ret;
382ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman}
383ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
384ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/*
385ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * Runs command in a subprocess. Returns pid on success and a FILE* to the
386ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * subprocess' stdout or 0 on failure.
387ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * NB. "command" is only used for logging.
388ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman */
389ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanstatic pid_t
390ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmansubprocess(const char *tag, struct passwd *pw, const char *command,
391ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman    int ac, char **av, FILE **child)
392ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman{
393ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	FILE *f;
394ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	struct stat st;
395ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int devnull, p[2], i;
396ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	pid_t pid;
397ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char *cp, errmsg[512];
398ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	u_int envsize;
399ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char **child_env;
400ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
401ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	*child = NULL;
402ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
403ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	debug3("%s: %s command \"%s\" running as %s", __func__,
404ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    tag, command, pw->pw_name);
405ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
406ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Verify the path exists and is safe-ish to execute */
407ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (*av[0] != '/') {
408ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s path is not absolute", tag);
409ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
410ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
411ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	temporarily_use_uid(pw);
412ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (stat(av[0], &st) < 0) {
413ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("Could not stat %s \"%s\": %s", tag,
414ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    av[0], strerror(errno));
415ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		restore_uid();
416ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
417ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
418ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (auth_secure_path(av[0], &st, NULL, 0,
419ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    errmsg, sizeof(errmsg)) != 0) {
420ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
421ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		restore_uid();
422ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
423ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
424ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
425ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/*
426ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * Run the command; stderr is left in place, stdout is the
427ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * authorized_keys output.
428ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 */
429ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (pipe(p) != 0) {
430ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s: pipe: %s", tag, strerror(errno));
431ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		restore_uid();
432ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
433ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
434ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
435ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/*
436ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * Don't want to call this in the child, where it can fatal() and
437ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * run cleanup_exit() code.
438ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 */
439ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	restore_uid();
440ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
441ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	switch ((pid = fork())) {
442ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	case -1: /* error */
443ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s: fork: %s", tag, strerror(errno));
444ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		close(p[0]);
445ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		close(p[1]);
446ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
447ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	case 0: /* child */
448ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Prepare a minimal environment for the child. */
449ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		envsize = 5;
450ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		child_env = xcalloc(sizeof(*child_env), envsize);
451ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
452ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		child_set_env(&child_env, &envsize, "USER", pw->pw_name);
453ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
454ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
455ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if ((cp = getenv("LANG")) != NULL)
456ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			child_set_env(&child_env, &envsize, "LANG", cp);
457ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
458ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		for (i = 0; i < NSIG; i++)
459ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			signal(i, SIG_DFL);
460ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
461ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
462ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			error("%s: open %s: %s", tag, _PATH_DEVNULL,
463ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			    strerror(errno));
464ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			_exit(1);
465ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
466ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Keep stderr around a while longer to catch errors */
467ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (dup2(devnull, STDIN_FILENO) == -1 ||
468ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    dup2(p[1], STDOUT_FILENO) == -1) {
469ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			error("%s: dup2: %s", tag, strerror(errno));
470ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			_exit(1);
471ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
472ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		closefrom(STDERR_FILENO + 1);
473ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
474ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Don't use permanently_set_uid() here to avoid fatal() */
475ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
476ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
477ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			    strerror(errno));
478ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			_exit(1);
479ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
480ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
481ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
482ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			    strerror(errno));
483ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			_exit(1);
484ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
485ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* stdin is pointed to /dev/null at this point */
486ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
487ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			error("%s: dup2: %s", tag, strerror(errno));
488ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			_exit(1);
489ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
490ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
491ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		execve(av[0], av, child_env);
492ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s exec \"%s\": %s", tag, command, strerror(errno));
493ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		_exit(127);
494ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	default: /* parent */
495ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		break;
496ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
497ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
498ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	close(p[1]);
499ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((f = fdopen(p[0], "r")) == NULL) {
500ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s: fdopen: %s", tag, strerror(errno));
501ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		close(p[0]);
502ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Don't leave zombie child */
503ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		kill(pid, SIGTERM);
504ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
505ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			;
506ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
507ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
508ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Success */
509ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	debug3("%s: %s pid %ld", __func__, tag, (long)pid);
510ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	*child = f;
511ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return pid;
512ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman}
513ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
514ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/* Returns 0 if pid exited cleanly, non-zero otherwise */
515ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanstatic int
516ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanexited_cleanly(pid_t pid, const char *tag, const char *cmd)
517ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman{
518ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int status;
519ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
520ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	while (waitpid(pid, &status, 0) == -1) {
521ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (errno != EINTR) {
522ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			error("%s: waitpid: %s", tag, strerror(errno));
523ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			return -1;
524ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		}
525ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
526ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (WIFSIGNALED(status)) {
527ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status));
528ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return -1;
529ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	} else if (WEXITSTATUS(status) != 0) {
530ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s %s failed, status %d", tag, cmd, WEXITSTATUS(status));
531ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return -1;
532ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
533ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return 0;
534ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman}
535ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
536bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic int
537d059297112922cabb0c674840589be8db821fd9aAdam Langleymatch_principals_option(const char *principal_list, struct sshkey_cert *cert)
538bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
539bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *result;
540bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int i;
541bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
542bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* XXX percent_expand() sequences for authorized_principals? */
543bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
544bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; i < cert->nprincipals; i++) {
545bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if ((result = match_list(cert->principals[i],
546bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    principal_list, NULL)) != NULL) {
547bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			debug3("matched principal from key options \"%.100s\"",
548bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    result);
549d059297112922cabb0c674840589be8db821fd9aAdam Langley			free(result);
550bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return 1;
551bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
552bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
553bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return 0;
554bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
555bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
556bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic int
557ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanprocess_principals(FILE *f, char *file, struct passwd *pw,
558ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman    struct sshkey_cert *cert)
559bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
560bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
561bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_long linenum = 0;
562bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int i;
563bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
564bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
565bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* Skip leading whitespace. */
566bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
567bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			;
568bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* Skip blank and comment lines. */
569bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if ((ep = strchr(cp, '#')) != NULL)
570bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			*ep = '\0';
571bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (!*cp || *cp == '\n')
572bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			continue;
573bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* Trim trailing whitespace. */
574bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		ep = cp + strlen(cp) - 1;
575bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
576bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			*ep-- = '\0';
577bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/*
578bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * If the line has internal whitespace then assume it has
579bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 * key options.
580bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		 */
581bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		line_opts = NULL;
582bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if ((ep = strrchr(cp, ' ')) != NULL ||
583bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    (ep = strrchr(cp, '\t')) != NULL) {
584bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			for (; *ep == ' ' || *ep == '\t'; ep++)
585bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				;
586bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			line_opts = cp;
587bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			cp = ep;
588bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
589bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		for (i = 0; i < cert->nprincipals; i++) {
590bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (strcmp(cp, cert->principals[i]) == 0) {
591ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				debug3("%s:%lu: matched principal \"%.100s\"",
592ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				    file == NULL ? "(command)" : file,
593ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman				    linenum, cert->principals[i]);
594bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				if (auth_parse_options(pw, line_opts,
595bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				    file, linenum) != 1)
596bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman					continue;
597bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				return 1;
598bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
599bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
600bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
601ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return 0;
602ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman}
603ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
604ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanstatic int
605ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanmatch_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
606ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman{
607ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	FILE *f;
608ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int success;
609ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
610ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	temporarily_use_uid(pw);
611ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	debug("trying authorized principals file %s", file);
612ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
613ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		restore_uid();
614ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
615ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
616ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	success = process_principals(f, file, pw, cert);
617bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	fclose(f);
618bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	restore_uid();
619ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return success;
620d059297112922cabb0c674840589be8db821fd9aAdam Langley}
621bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
622d059297112922cabb0c674840589be8db821fd9aAdam Langley/*
623ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * Checks whether principal is allowed in output of command.
624ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman * returns 1 if the principal is allowed or 0 otherwise.
625ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman */
626ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanstatic int
627ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanmatch_principals_command(struct passwd *user_pw, struct sshkey_cert *cert)
628ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman{
629ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	FILE *f = NULL;
630ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int ok, found_principal = 0;
631ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	struct passwd *pw;
632ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int i, ac = 0, uid_swapped = 0;
633ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	pid_t pid;
634ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char *tmp, *username = NULL, *command = NULL, **av = NULL;
635ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	void (*osigchld)(int);
636ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
637ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (options.authorized_principals_command == NULL)
638ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
639ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (options.authorized_principals_command_user == NULL) {
640ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("No user for AuthorizedPrincipalsCommand specified, "
641ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "skipping");
642ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		return 0;
643ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
644ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
645ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/*
646ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * NB. all returns later this function should go via "out" to
647ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * ensure the original SIGCHLD handler is restored properly.
648ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 */
649ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	osigchld = signal(SIGCHLD, SIG_DFL);
650ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
651ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Prepare and verify the user for the command */
652ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	username = percent_expand(options.authorized_principals_command_user,
653ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    "u", user_pw->pw_name, (char *)NULL);
654ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	pw = getpwnam(username);
655ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (pw == NULL) {
656ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
657ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    username, strerror(errno));
658ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
659ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
660ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
661ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Turn the command into an argument vector */
662ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (split_argv(options.authorized_principals_command, &ac, &av) != 0) {
663ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("AuthorizedPrincipalsCommand \"%s\" contains "
664ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "invalid quotes", command);
665ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
666ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
667ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (ac == 0) {
668ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
669ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    command);
670ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
671ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
672ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	for (i = 1; i < ac; i++) {
673ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		tmp = percent_expand(av[i],
674ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "u", user_pw->pw_name,
675ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "h", user_pw->pw_dir,
676ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    (char *)NULL);
677ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (tmp == NULL)
678ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			fatal("%s: percent_expand failed", __func__);
679ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		free(av[i]);
680ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		av[i] = tmp;
681ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
682ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Prepare a printable command for logs, etc. */
683ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	command = assemble_argv(ac, av);
684ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
685ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
686ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    ac, av, &f)) == 0)
687ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
688ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
689ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	uid_swapped = 1;
690ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	temporarily_use_uid(pw);
691ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
692ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	ok = process_principals(f, NULL, pw, cert);
693ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
694ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0)
695ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
696ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
697ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Read completed successfully */
698ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	found_principal = ok;
699ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman out:
700ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (f != NULL)
701ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		fclose(f);
702ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	signal(SIGCHLD, osigchld);
703ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	for (i = 0; i < ac; i++)
704ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		free(av[i]);
705ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(av);
706ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (uid_swapped)
707ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		restore_uid();
708ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(command);
709ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(username);
710ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	return found_principal;
711ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman}
712ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/*
713d059297112922cabb0c674840589be8db821fd9aAdam Langley * Checks whether key is allowed in authorized_keys-format file,
714d059297112922cabb0c674840589be8db821fd9aAdam Langley * returns 1 if the key is allowed or 0 otherwise.
715d059297112922cabb0c674840589be8db821fd9aAdam Langley */
716bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic int
717d059297112922cabb0c674840589be8db821fd9aAdam Langleycheck_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
718bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
719bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char line[SSH_MAX_PUBKEY_BYTES];
720bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	const char *reason;
721bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	int found_key = 0;
722bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_long linenum = 0;
723bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	Key *found;
724bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *fp;
725bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
726bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	found_key = 0;
727bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
728d059297112922cabb0c674840589be8db821fd9aAdam Langley	found = NULL;
729bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
730bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		char *cp, *key_options = NULL;
731d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (found != NULL)
732d059297112922cabb0c674840589be8db821fd9aAdam Langley			key_free(found);
733d059297112922cabb0c674840589be8db821fd9aAdam Langley		found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
734bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		auth_clear_options();
735bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
736bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		/* Skip leading whitespace, empty and comment lines. */
737bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
738bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			;
739bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (!*cp || *cp == '\n' || *cp == '#')
740bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			continue;
741bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
742bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (key_read(found, &cp) != 1) {
743bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			/* no key?  check if there are options for this key */
744bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			int quoted = 0;
745bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			debug2("user_key_allowed: check options: '%s'", cp);
746bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			key_options = cp;
747bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
748bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				if (*cp == '\\' && cp[1] == '"')
749bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman					cp++;	/* Skip both */
750bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				else if (*cp == '"')
751bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman					quoted = !quoted;
752bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
753bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			/* Skip remaining whitespace. */
754bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			for (; *cp == ' ' || *cp == '\t'; cp++)
755bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				;
756bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (key_read(found, &cp) != 1) {
757bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				debug2("user_key_allowed: advance: '%s'", cp);
758bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				/* still no key?  advance to next line*/
759bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
760bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
761bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
762bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (key_is_cert(key)) {
763bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (!key_equal(found, key->cert->signature_key))
764bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
765bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (auth_parse_options(pw, key_options, file,
766bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    linenum) != 1)
767bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
768bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (!key_is_cert_authority)
769bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
770d059297112922cabb0c674840589be8db821fd9aAdam Langley			if ((fp = sshkey_fingerprint(found,
771d059297112922cabb0c674840589be8db821fd9aAdam Langley			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
772d059297112922cabb0c674840589be8db821fd9aAdam Langley				continue;
773bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			debug("matching CA found: file %s, line %lu, %s %s",
774bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    file, linenum, key_type(found), fp);
775bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			/*
776bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			 * If the user has specified a list of principals as
777bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			 * a key option, then prefer that list to matching
778bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			 * their username in the certificate principals list.
779bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			 */
780bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (authorized_principals != NULL &&
781bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    !match_principals_option(authorized_principals,
782bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    key->cert)) {
783bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				reason = "Certificate does not contain an "
784bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				    "authorized principal";
785bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman fail_reason:
786d059297112922cabb0c674840589be8db821fd9aAdam Langley				free(fp);
787bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				error("%s", reason);
788bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				auth_debug_add("%s", reason);
789bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
790bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
791bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (key_cert_check_authority(key, 0, 0,
792bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    authorized_principals == NULL ? pw->pw_name : NULL,
793bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    &reason) != 0)
794bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				goto fail_reason;
795bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (auth_cert_options(key, pw) != 0) {
796d059297112922cabb0c674840589be8db821fd9aAdam Langley				free(fp);
797bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
798bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			}
799bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			verbose("Accepted certificate ID \"%s\" "
800bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    "signed by %s CA %s via %s", key->cert->key_id,
801bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    key_type(found), fp, file);
802d059297112922cabb0c674840589be8db821fd9aAdam Langley			free(fp);
803bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			found_key = 1;
804bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			break;
805bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		} else if (key_equal(found, key)) {
806bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (auth_parse_options(pw, key_options, file,
807bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    linenum) != 1)
808bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
809bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			if (key_is_cert_authority)
810bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman				continue;
811d059297112922cabb0c674840589be8db821fd9aAdam Langley			if ((fp = sshkey_fingerprint(found,
812d059297112922cabb0c674840589be8db821fd9aAdam Langley			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
813d059297112922cabb0c674840589be8db821fd9aAdam Langley				continue;
814d059297112922cabb0c674840589be8db821fd9aAdam Langley			debug("matching key found: file %s, line %lu %s %s",
815d059297112922cabb0c674840589be8db821fd9aAdam Langley			    file, linenum, key_type(found), fp);
816d059297112922cabb0c674840589be8db821fd9aAdam Langley			free(fp);
817bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			found_key = 1;
818bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			break;
819bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
820bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
821d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (found != NULL)
822d059297112922cabb0c674840589be8db821fd9aAdam Langley		key_free(found);
823bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (!found_key)
824bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug2("key not found");
825bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return found_key;
826bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
827bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
828bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* Authenticate a certificate key against TrustedUserCAKeys */
829bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic int
830bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanuser_cert_trusted_ca(struct passwd *pw, Key *key)
831bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
832bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *ca_fp, *principals_file = NULL;
833bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	const char *reason;
834ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int ret = 0, found_principal = 0, use_authorized_principals;
835bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
836bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
837bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return 0;
838bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
839d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
840d059297112922cabb0c674840589be8db821fd9aAdam Langley	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
841d059297112922cabb0c674840589be8db821fd9aAdam Langley		return 0;
842bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
843d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (sshkey_in_file(key->cert->signature_key,
844d059297112922cabb0c674840589be8db821fd9aAdam Langley	    options.trusted_user_ca_keys, 1, 0) != 0) {
845bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug2("%s: CA %s %s is not listed in %s", __func__,
846bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    key_type(key->cert->signature_key), ca_fp,
847bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    options.trusted_user_ca_keys);
848bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		goto out;
849bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
850bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/*
851bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * If AuthorizedPrincipals is in use, then compare the certificate
852bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * principals against the names in that file rather than matching
853bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * against the username.
854bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 */
855bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if ((principals_file = authorized_principals_file(pw)) != NULL) {
856ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (match_principals_file(principals_file, pw, key->cert))
857ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			found_principal = 1;
858ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
859ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Try querying command if specified */
860ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (!found_principal && match_principals_command(pw, key->cert))
861ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		found_principal = 1;
862ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* If principals file or command is specified, then require a match */
863ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	use_authorized_principals = principals_file != NULL ||
864ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman            options.authorized_principals_command != NULL;
865ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (!found_principal && use_authorized_principals) {
866ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		reason = "Certificate does not contain an authorized principal";
867bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman fail_reason:
868ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s", reason);
869ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		auth_debug_add("%s", reason);
870ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
871bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
872bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (key_cert_check_authority(key, 0, 1,
873ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
874bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		goto fail_reason;
875bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (auth_cert_options(key, pw) != 0)
876bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		goto out;
877bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
878bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
879bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	    key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
880bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	    options.trusted_user_ca_keys);
881bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	ret = 1;
882bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
883bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman out:
884d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(principals_file);
885d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(ca_fp);
886bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return ret;
887bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
888bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
889d059297112922cabb0c674840589be8db821fd9aAdam Langley/*
890d059297112922cabb0c674840589be8db821fd9aAdam Langley * Checks whether key is allowed in file.
891d059297112922cabb0c674840589be8db821fd9aAdam Langley * returns 1 if the key is allowed or 0 otherwise.
892d059297112922cabb0c674840589be8db821fd9aAdam Langley */
893d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
894d059297112922cabb0c674840589be8db821fd9aAdam Langleyuser_key_allowed2(struct passwd *pw, Key *key, char *file)
895d059297112922cabb0c674840589be8db821fd9aAdam Langley{
896d059297112922cabb0c674840589be8db821fd9aAdam Langley	FILE *f;
897d059297112922cabb0c674840589be8db821fd9aAdam Langley	int found_key = 0;
898d059297112922cabb0c674840589be8db821fd9aAdam Langley
899d059297112922cabb0c674840589be8db821fd9aAdam Langley	/* Temporarily use the user's uid. */
900d059297112922cabb0c674840589be8db821fd9aAdam Langley	temporarily_use_uid(pw);
901d059297112922cabb0c674840589be8db821fd9aAdam Langley
902d059297112922cabb0c674840589be8db821fd9aAdam Langley	debug("trying public key file %s", file);
903d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
904d059297112922cabb0c674840589be8db821fd9aAdam Langley		found_key = check_authkeys_file(f, file, key, pw);
905d059297112922cabb0c674840589be8db821fd9aAdam Langley		fclose(f);
906d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
907d059297112922cabb0c674840589be8db821fd9aAdam Langley
908d059297112922cabb0c674840589be8db821fd9aAdam Langley	restore_uid();
909d059297112922cabb0c674840589be8db821fd9aAdam Langley	return found_key;
910d059297112922cabb0c674840589be8db821fd9aAdam Langley}
911d059297112922cabb0c674840589be8db821fd9aAdam Langley
912d059297112922cabb0c674840589be8db821fd9aAdam Langley/*
913d059297112922cabb0c674840589be8db821fd9aAdam Langley * Checks whether key is allowed in output of command.
914d059297112922cabb0c674840589be8db821fd9aAdam Langley * returns 1 if the key is allowed or 0 otherwise.
915d059297112922cabb0c674840589be8db821fd9aAdam Langley */
916d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
917d059297112922cabb0c674840589be8db821fd9aAdam Langleyuser_key_command_allowed2(struct passwd *user_pw, Key *key)
918d059297112922cabb0c674840589be8db821fd9aAdam Langley{
919ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	FILE *f = NULL;
920ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int r, ok, found_key = 0;
921d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct passwd *pw;
922ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	int i, uid_swapped = 0, ac = 0;
923d059297112922cabb0c674840589be8db821fd9aAdam Langley	pid_t pid;
924ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char *username = NULL, *key_fp = NULL, *keytext = NULL;
925ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	char *tmp, *command = NULL, **av = NULL;
926ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	void (*osigchld)(int);
927d059297112922cabb0c674840589be8db821fd9aAdam Langley
928ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (options.authorized_keys_command == NULL)
929d059297112922cabb0c674840589be8db821fd9aAdam Langley		return 0;
930d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (options.authorized_keys_command_user == NULL) {
931d059297112922cabb0c674840589be8db821fd9aAdam Langley		error("No user for AuthorizedKeysCommand specified, skipping");
932d059297112922cabb0c674840589be8db821fd9aAdam Langley		return 0;
933d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
934d059297112922cabb0c674840589be8db821fd9aAdam Langley
935ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/*
936ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * NB. all returns later this function should go via "out" to
937ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * ensure the original SIGCHLD handler is restored properly.
938ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 */
939ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	osigchld = signal(SIGCHLD, SIG_DFL);
940ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
941ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Prepare and verify the user for the command */
942d059297112922cabb0c674840589be8db821fd9aAdam Langley	username = percent_expand(options.authorized_keys_command_user,
943d059297112922cabb0c674840589be8db821fd9aAdam Langley	    "u", user_pw->pw_name, (char *)NULL);
944d059297112922cabb0c674840589be8db821fd9aAdam Langley	pw = getpwnam(username);
945d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (pw == NULL) {
946d059297112922cabb0c674840589be8db821fd9aAdam Langley		error("AuthorizedKeysCommandUser \"%s\" not found: %s",
947d059297112922cabb0c674840589be8db821fd9aAdam Langley		    username, strerror(errno));
948ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
949d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
950d059297112922cabb0c674840589be8db821fd9aAdam Langley
951ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Prepare AuthorizedKeysCommand */
952ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
953ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    SSH_FP_DEFAULT)) == NULL) {
954ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s: sshkey_fingerprint failed", __func__);
955d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
956d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
957ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((r = sshkey_to_base64(key, &keytext)) != 0) {
958ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
959d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
960d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
961d059297112922cabb0c674840589be8db821fd9aAdam Langley
962ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Turn the command into an argument vector */
963ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (split_argv(options.authorized_keys_command, &ac, &av) != 0) {
964ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
965ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    command);
966d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
967d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
968ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (ac == 0) {
969ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		error("AuthorizedKeysCommand \"%s\" yielded no arguments",
970ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    command);
971ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
972ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
973ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	for (i = 1; i < ac; i++) {
974ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		tmp = percent_expand(av[i],
975ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "u", user_pw->pw_name,
976ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "h", user_pw->pw_dir,
977ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "t", sshkey_ssh_name(key),
978ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "f", key_fp,
979ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    "k", keytext,
980ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		    (char *)NULL);
981ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		if (tmp == NULL)
982ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman			fatal("%s: percent_expand failed", __func__);
983ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		free(av[i]);
984ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		av[i] = tmp;
985ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	}
986ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Prepare a printable command for logs, etc. */
987ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	command = assemble_argv(ac, av);
988d059297112922cabb0c674840589be8db821fd9aAdam Langley
989d059297112922cabb0c674840589be8db821fd9aAdam Langley	/*
990ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * If AuthorizedKeysCommand was run without arguments
991ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * then fall back to the old behaviour of passing the
992ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	 * target username as a single argument.
993d059297112922cabb0c674840589be8db821fd9aAdam Langley	 */
994ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (ac == 1) {
995ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		av = xreallocarray(av, ac + 2, sizeof(*av));
996ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		av[1] = xstrdup(user_pw->pw_name);
997ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		av[2] = NULL;
998ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		/* Fix up command too, since it is used in log messages */
999ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		free(command);
1000ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		xasprintf(&command, "%s %s", av[0], av[1]);
1001d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
1002d059297112922cabb0c674840589be8db821fd9aAdam Langley
1003ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
1004ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	    ac, av, &f)) == 0)
1005ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		goto out;
1006ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
1007ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	uid_swapped = 1;
1008d059297112922cabb0c674840589be8db821fd9aAdam Langley	temporarily_use_uid(pw);
1009d059297112922cabb0c674840589be8db821fd9aAdam Langley
1010d059297112922cabb0c674840589be8db821fd9aAdam Langley	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
1011d059297112922cabb0c674840589be8db821fd9aAdam Langley
1012ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0)
1013d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
1014ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman
1015ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	/* Read completed successfully */
1016d059297112922cabb0c674840589be8db821fd9aAdam Langley	found_key = ok;
1017d059297112922cabb0c674840589be8db821fd9aAdam Langley out:
1018ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (f != NULL)
1019ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		fclose(f);
1020ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	signal(SIGCHLD, osigchld);
1021ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	for (i = 0; i < ac; i++)
1022ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		free(av[i]);
1023ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(av);
1024ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	if (uid_swapped)
1025ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman		restore_uid();
1026ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(command);
1027ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(username);
1028ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(key_fp);
1029ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman	free(keytext);
1030d059297112922cabb0c674840589be8db821fd9aAdam Langley	return found_key;
1031d059297112922cabb0c674840589be8db821fd9aAdam Langley}
1032d059297112922cabb0c674840589be8db821fd9aAdam Langley
1033d059297112922cabb0c674840589be8db821fd9aAdam Langley/*
1034d059297112922cabb0c674840589be8db821fd9aAdam Langley * Check whether key authenticates and authorises the user.
1035d059297112922cabb0c674840589be8db821fd9aAdam Langley */
1036bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanint
1037ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartmanuser_key_allowed(struct passwd *pw, Key *key, int auth_attempt)
1038bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
1039bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int success, i;
1040bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *file;
1041bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
1042bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (auth_key_is_revoked(key))
1043bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return 0;
1044bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
1045bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return 0;
1046bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
1047bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	success = user_cert_trusted_ca(pw, key);
1048bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (success)
1049bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return success;
1050bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
1051d059297112922cabb0c674840589be8db821fd9aAdam Langley	success = user_key_command_allowed2(pw, key);
1052d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (success > 0)
1053d059297112922cabb0c674840589be8db821fd9aAdam Langley		return success;
1054d059297112922cabb0c674840589be8db821fd9aAdam Langley
1055bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; !success && i < options.num_authkeys_files; i++) {
1056d059297112922cabb0c674840589be8db821fd9aAdam Langley
1057d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1058d059297112922cabb0c674840589be8db821fd9aAdam Langley			continue;
1059bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		file = expand_authorized_keys(
1060bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    options.authorized_keys_files[i], pw);
1061d059297112922cabb0c674840589be8db821fd9aAdam Langley
1062bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		success = user_key_allowed2(pw, key, file);
1063d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(file);
1064bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
1065bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
1066bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return success;
1067bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
1068bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
1069d059297112922cabb0c674840589be8db821fd9aAdam Langley/* Records a public key in the list of previously-successful keys */
1070d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid
1071d059297112922cabb0c674840589be8db821fd9aAdam Langleyauth2_record_userkey(Authctxt *authctxt, struct sshkey *key)
1072d059297112922cabb0c674840589be8db821fd9aAdam Langley{
1073d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct sshkey **tmp;
1074d059297112922cabb0c674840589be8db821fd9aAdam Langley
1075d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (authctxt->nprev_userkeys >= INT_MAX ||
1076d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (tmp = reallocarray(authctxt->prev_userkeys,
1077d059297112922cabb0c674840589be8db821fd9aAdam Langley	    authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL)
1078d059297112922cabb0c674840589be8db821fd9aAdam Langley		fatal("%s: reallocarray failed", __func__);
1079d059297112922cabb0c674840589be8db821fd9aAdam Langley	authctxt->prev_userkeys = tmp;
1080d059297112922cabb0c674840589be8db821fd9aAdam Langley	authctxt->prev_userkeys[authctxt->nprev_userkeys] = key;
1081d059297112922cabb0c674840589be8db821fd9aAdam Langley	authctxt->nprev_userkeys++;
1082d059297112922cabb0c674840589be8db821fd9aAdam Langley}
1083d059297112922cabb0c674840589be8db821fd9aAdam Langley
1084d059297112922cabb0c674840589be8db821fd9aAdam Langley/* Checks whether a key has already been used successfully for authentication */
1085d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
1086d059297112922cabb0c674840589be8db821fd9aAdam Langleyauth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key)
1087d059297112922cabb0c674840589be8db821fd9aAdam Langley{
1088d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int i;
1089d059297112922cabb0c674840589be8db821fd9aAdam Langley
1090d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (i = 0; i < authctxt->nprev_userkeys; i++) {
1091d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) {
1092d059297112922cabb0c674840589be8db821fd9aAdam Langley			return 1;
1093d059297112922cabb0c674840589be8db821fd9aAdam Langley		}
1094d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
1095d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
1096d059297112922cabb0c674840589be8db821fd9aAdam Langley}
1097d059297112922cabb0c674840589be8db821fd9aAdam Langley
1098bd77cf78387b72b7b3ea870459077672bf75c3b5Greg HartmanAuthmethod method_pubkey = {
1099bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	"publickey",
1100bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	userauth_pubkey,
1101bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	&options.pubkey_authentication
1102bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman};
1103