xref: /freebsd/crypto/openssh/auth.c (revision 27ceebbc2402e4c98203c7eef9696f4bd3d326f8)
12f513db7SEd Maste /* $OpenBSD: auth.c,v 1.133 2018/09/12 01:19:12 djm Exp $ */
2a04a10f8SKris Kennaway /*
3a04a10f8SKris Kennaway  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4e8aafc91SKris Kennaway  *
5c2d3a559SKris Kennaway  * Redistribution and use in source and binary forms, with or without
6c2d3a559SKris Kennaway  * modification, are permitted provided that the following conditions
7c2d3a559SKris Kennaway  * are met:
8c2d3a559SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
9c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
10c2d3a559SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
11c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
12c2d3a559SKris Kennaway  *    documentation and/or other materials provided with the distribution.
13c2d3a559SKris Kennaway  *
14c2d3a559SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15c2d3a559SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16c2d3a559SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17c2d3a559SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18c2d3a559SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19c2d3a559SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20c2d3a559SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21c2d3a559SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22c2d3a559SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23c2d3a559SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24a04a10f8SKris Kennaway  */
25a04a10f8SKris Kennaway 
26a04a10f8SKris Kennaway #include "includes.h"
27333ee039SDag-Erling Smørgrav __RCSID("$FreeBSD$");
28a04a10f8SKris Kennaway 
29333ee039SDag-Erling Smørgrav #include <sys/types.h>
30333ee039SDag-Erling Smørgrav #include <sys/stat.h>
31076ad2f8SDag-Erling Smørgrav #include <sys/socket.h>
3247dd1d1bSDag-Erling Smørgrav #include <sys/wait.h>
33333ee039SDag-Erling Smørgrav 
34333ee039SDag-Erling Smørgrav #include <netinet/in.h>
35333ee039SDag-Erling Smørgrav 
36333ee039SDag-Erling Smørgrav #include <errno.h>
37d4af9e69SDag-Erling Smørgrav #include <fcntl.h>
38333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H
39333ee039SDag-Erling Smørgrav # include <paths.h>
40333ee039SDag-Erling Smørgrav #endif
41333ee039SDag-Erling Smørgrav #include <pwd.h>
42989dd127SDag-Erling Smørgrav #ifdef HAVE_LOGIN_H
43989dd127SDag-Erling Smørgrav #include <login.h>
44989dd127SDag-Erling Smørgrav #endif
451ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
46989dd127SDag-Erling Smørgrav #include <shadow.h>
471ec0d754SDag-Erling Smørgrav #endif
48333ee039SDag-Erling Smørgrav #include <stdarg.h>
49333ee039SDag-Erling Smørgrav #include <stdio.h>
50333ee039SDag-Erling Smørgrav #include <string.h>
51d4af9e69SDag-Erling Smørgrav #include <unistd.h>
52bc5531deSDag-Erling Smørgrav #include <limits.h>
53076ad2f8SDag-Erling Smørgrav #include <netdb.h>
54af12a3e7SDag-Erling Smørgrav 
55a04a10f8SKris Kennaway #include "xmalloc.h"
56a04a10f8SKris Kennaway #include "match.h"
57ca3176e7SBrian Feldman #include "groupaccess.h"
58ca3176e7SBrian Feldman #include "log.h"
59190cef3dSDag-Erling Smørgrav #include "sshbuf.h"
60a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
61ca3176e7SBrian Feldman #include "servconf.h"
62190cef3dSDag-Erling Smørgrav #include "sshkey.h"
63333ee039SDag-Erling Smørgrav #include "hostfile.h"
64a04a10f8SKris Kennaway #include "auth.h"
65ca3176e7SBrian Feldman #include "auth-options.h"
66ca3176e7SBrian Feldman #include "canohost.h"
67af12a3e7SDag-Erling Smørgrav #include "uidswap.h"
6880628bacSDag-Erling Smørgrav #include "packet.h"
69aa49c926SDag-Erling Smørgrav #include "loginrec.h"
70333ee039SDag-Erling Smørgrav #ifdef GSSAPI
71333ee039SDag-Erling Smørgrav #include "ssh-gss.h"
72333ee039SDag-Erling Smørgrav #endif
73b15c8340SDag-Erling Smørgrav #include "authfile.h"
74aa49c926SDag-Erling Smørgrav #include "monitor_wrap.h"
75bc5531deSDag-Erling Smørgrav #include "authfile.h"
76bc5531deSDag-Erling Smørgrav #include "ssherr.h"
77e4a9863fSDag-Erling Smørgrav #include "compat.h"
7847dd1d1bSDag-Erling Smørgrav #include "channels.h"
79b2af61ecSKurt Lidl #include "blacklist_client.h"
80a04a10f8SKris Kennaway 
81a04a10f8SKris Kennaway /* import */
82a04a10f8SKris Kennaway extern ServerOptions options;
83333ee039SDag-Erling Smørgrav extern int use_privsep;
84190cef3dSDag-Erling Smørgrav extern struct sshbuf *loginmsg;
85333ee039SDag-Erling Smørgrav extern struct passwd *privsep_pw;
8647dd1d1bSDag-Erling Smørgrav extern struct sshauthopt *auth_opts;
87a04a10f8SKris Kennaway 
8880628bacSDag-Erling Smørgrav /* Debugging messages */
89190cef3dSDag-Erling Smørgrav static struct sshbuf *auth_debug;
9080628bacSDag-Erling Smørgrav 
91a04a10f8SKris Kennaway /*
92ca3176e7SBrian Feldman  * Check if the user is allowed to log in via ssh. If user is listed
93ca3176e7SBrian Feldman  * in DenyUsers or one of user's groups is listed in DenyGroups, false
94ca3176e7SBrian Feldman  * will be returned. If AllowUsers isn't empty and user isn't listed
95ca3176e7SBrian Feldman  * there, or if AllowGroups isn't empty and one of user's groups isn't
96ca3176e7SBrian Feldman  * listed there, false will be returned.
97a04a10f8SKris Kennaway  * If the user's shell is not executable, false will be returned.
98a04a10f8SKris Kennaway  * Otherwise true is returned.
99a04a10f8SKris Kennaway  */
100a04a10f8SKris Kennaway int
101a04a10f8SKris Kennaway allowed_user(struct passwd * pw)
102a04a10f8SKris Kennaway {
103076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
104a04a10f8SKris Kennaway 	struct stat st;
105cf2b5f3bSDag-Erling Smørgrav 	const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL;
106d4ecd108SDag-Erling Smørgrav 	u_int i;
107ca86bcf2SDag-Erling Smørgrav 	int r;
1081ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
109cf2b5f3bSDag-Erling Smørgrav 	struct spwd *spw = NULL;
110e73e9afaSDag-Erling Smørgrav #endif
111a04a10f8SKris Kennaway 
112a04a10f8SKris Kennaway 	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
113ca3176e7SBrian Feldman 	if (!pw || !pw->pw_name)
114a04a10f8SKris Kennaway 		return 0;
115a04a10f8SKris Kennaway 
1161ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
117cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
118cf2b5f3bSDag-Erling Smørgrav 		spw = getspnam(pw->pw_name);
119cf2b5f3bSDag-Erling Smørgrav #ifdef HAS_SHADOW_EXPIRE
1201ec0d754SDag-Erling Smørgrav 	if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw))
121989dd127SDag-Erling Smørgrav 		return 0;
122cf2b5f3bSDag-Erling Smørgrav #endif /* HAS_SHADOW_EXPIRE */
1231ec0d754SDag-Erling Smørgrav #endif /* USE_SHADOW */
124cf2b5f3bSDag-Erling Smørgrav 
125cf2b5f3bSDag-Erling Smørgrav 	/* grab passwd field for locked account check */
126d4af9e69SDag-Erling Smørgrav 	passwd = pw->pw_passwd;
1271ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
128cf2b5f3bSDag-Erling Smørgrav 	if (spw != NULL)
129d4af9e69SDag-Erling Smørgrav #ifdef USE_LIBIAF
130d4ecd108SDag-Erling Smørgrav 		passwd = get_iaf_password(pw);
131d4ecd108SDag-Erling Smørgrav #else
132cf2b5f3bSDag-Erling Smørgrav 		passwd = spw->sp_pwdp;
133d4af9e69SDag-Erling Smørgrav #endif /* USE_LIBIAF */
134989dd127SDag-Erling Smørgrav #endif
135989dd127SDag-Erling Smørgrav 
136cf2b5f3bSDag-Erling Smørgrav 	/* check for locked account */
137cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam && passwd && *passwd) {
138cf2b5f3bSDag-Erling Smørgrav 		int locked = 0;
139cf2b5f3bSDag-Erling Smørgrav 
140cf2b5f3bSDag-Erling Smørgrav #ifdef LOCKED_PASSWD_STRING
141cf2b5f3bSDag-Erling Smørgrav 		if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0)
142cf2b5f3bSDag-Erling Smørgrav 			 locked = 1;
143cf2b5f3bSDag-Erling Smørgrav #endif
144cf2b5f3bSDag-Erling Smørgrav #ifdef LOCKED_PASSWD_PREFIX
145cf2b5f3bSDag-Erling Smørgrav 		if (strncmp(passwd, LOCKED_PASSWD_PREFIX,
146cf2b5f3bSDag-Erling Smørgrav 		    strlen(LOCKED_PASSWD_PREFIX)) == 0)
147cf2b5f3bSDag-Erling Smørgrav 			 locked = 1;
148cf2b5f3bSDag-Erling Smørgrav #endif
149cf2b5f3bSDag-Erling Smørgrav #ifdef LOCKED_PASSWD_SUBSTR
150cf2b5f3bSDag-Erling Smørgrav 		if (strstr(passwd, LOCKED_PASSWD_SUBSTR))
151cf2b5f3bSDag-Erling Smørgrav 			locked = 1;
152cf2b5f3bSDag-Erling Smørgrav #endif
153d4af9e69SDag-Erling Smørgrav #ifdef USE_LIBIAF
154e2f6069cSDag-Erling Smørgrav 		free((void *) passwd);
155d4af9e69SDag-Erling Smørgrav #endif /* USE_LIBIAF */
156cf2b5f3bSDag-Erling Smørgrav 		if (locked) {
157cf2b5f3bSDag-Erling Smørgrav 			logit("User %.100s not allowed because account is locked",
158cf2b5f3bSDag-Erling Smørgrav 			    pw->pw_name);
159cf2b5f3bSDag-Erling Smørgrav 			return 0;
160cf2b5f3bSDag-Erling Smørgrav 		}
161cf2b5f3bSDag-Erling Smørgrav 	}
162cf2b5f3bSDag-Erling Smørgrav 
163c322fe35SKris Kennaway 	/*
164b15c8340SDag-Erling Smørgrav 	 * Deny if shell does not exist or is not executable unless we
165b15c8340SDag-Erling Smørgrav 	 * are chrooting.
166c322fe35SKris Kennaway 	 */
167b15c8340SDag-Erling Smørgrav 	if (options.chroot_directory == NULL ||
168b15c8340SDag-Erling Smørgrav 	    strcasecmp(options.chroot_directory, "none") == 0) {
169b15c8340SDag-Erling Smørgrav 		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
170b15c8340SDag-Erling Smørgrav 		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */
171c322fe35SKris Kennaway 
172af12a3e7SDag-Erling Smørgrav 		if (stat(shell, &st) != 0) {
173b15c8340SDag-Erling Smørgrav 			logit("User %.100s not allowed because shell %.100s "
174b15c8340SDag-Erling Smørgrav 			    "does not exist", pw->pw_name, shell);
175e4a9863fSDag-Erling Smørgrav 			free(shell);
176a04a10f8SKris Kennaway 			return 0;
177af12a3e7SDag-Erling Smørgrav 		}
17880628bacSDag-Erling Smørgrav 		if (S_ISREG(st.st_mode) == 0 ||
17980628bacSDag-Erling Smørgrav 		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
180b15c8340SDag-Erling Smørgrav 			logit("User %.100s not allowed because shell %.100s "
181b15c8340SDag-Erling Smørgrav 			    "is not executable", pw->pw_name, shell);
182e4a9863fSDag-Erling Smørgrav 			free(shell);
183a04a10f8SKris Kennaway 			return 0;
184af12a3e7SDag-Erling Smørgrav 		}
185e4a9863fSDag-Erling Smørgrav 		free(shell);
186b15c8340SDag-Erling Smørgrav 	}
187af12a3e7SDag-Erling Smørgrav 
188aa49c926SDag-Erling Smørgrav 	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
189aa49c926SDag-Erling Smørgrav 	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
190076ad2f8SDag-Erling Smørgrav 		hostname = auth_get_canonical_hostname(ssh, options.use_dns);
191076ad2f8SDag-Erling Smørgrav 		ipaddr = ssh_remote_ipaddr(ssh);
192af12a3e7SDag-Erling Smørgrav 	}
193a04a10f8SKris Kennaway 
194a04a10f8SKris Kennaway 	/* Return false if user is listed in DenyUsers */
195a04a10f8SKris Kennaway 	if (options.num_deny_users > 0) {
196ca86bcf2SDag-Erling Smørgrav 		for (i = 0; i < options.num_deny_users; i++) {
197ca86bcf2SDag-Erling Smørgrav 			r = match_user(pw->pw_name, hostname, ipaddr,
198ca86bcf2SDag-Erling Smørgrav 			    options.deny_users[i]);
199ca86bcf2SDag-Erling Smørgrav 			if (r < 0) {
200ca86bcf2SDag-Erling Smørgrav 				fatal("Invalid DenyUsers pattern \"%.100s\"",
201ca86bcf2SDag-Erling Smørgrav 				    options.deny_users[i]);
202ca86bcf2SDag-Erling Smørgrav 			} else if (r != 0) {
203aa49c926SDag-Erling Smørgrav 				logit("User %.100s from %.100s not allowed "
204aa49c926SDag-Erling Smørgrav 				    "because listed in DenyUsers",
205aa49c926SDag-Erling Smørgrav 				    pw->pw_name, hostname);
206a04a10f8SKris Kennaway 				return 0;
207a04a10f8SKris Kennaway 			}
208af12a3e7SDag-Erling Smørgrav 		}
209ca86bcf2SDag-Erling Smørgrav 	}
210a04a10f8SKris Kennaway 	/* Return false if AllowUsers isn't empty and user isn't listed there */
211a04a10f8SKris Kennaway 	if (options.num_allow_users > 0) {
212ca86bcf2SDag-Erling Smørgrav 		for (i = 0; i < options.num_allow_users; i++) {
213ca86bcf2SDag-Erling Smørgrav 			r = match_user(pw->pw_name, hostname, ipaddr,
214ca86bcf2SDag-Erling Smørgrav 			    options.allow_users[i]);
215ca86bcf2SDag-Erling Smørgrav 			if (r < 0) {
216ca86bcf2SDag-Erling Smørgrav 				fatal("Invalid AllowUsers pattern \"%.100s\"",
217ca86bcf2SDag-Erling Smørgrav 				    options.allow_users[i]);
218ca86bcf2SDag-Erling Smørgrav 			} else if (r == 1)
219a04a10f8SKris Kennaway 				break;
220ca86bcf2SDag-Erling Smørgrav 		}
221a04a10f8SKris Kennaway 		/* i < options.num_allow_users iff we break for loop */
222af12a3e7SDag-Erling Smørgrav 		if (i >= options.num_allow_users) {
223aa49c926SDag-Erling Smørgrav 			logit("User %.100s from %.100s not allowed because "
224aa49c926SDag-Erling Smørgrav 			    "not listed in AllowUsers", pw->pw_name, hostname);
225a04a10f8SKris Kennaway 			return 0;
226a04a10f8SKris Kennaway 		}
227af12a3e7SDag-Erling Smørgrav 	}
228a04a10f8SKris Kennaway 	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
229ca3176e7SBrian Feldman 		/* Get the user's group access list (primary and supplementary) */
230af12a3e7SDag-Erling Smørgrav 		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
231aa49c926SDag-Erling Smørgrav 			logit("User %.100s from %.100s not allowed because "
232aa49c926SDag-Erling Smørgrav 			    "not in any group", pw->pw_name, hostname);
233a04a10f8SKris Kennaway 			return 0;
234af12a3e7SDag-Erling Smørgrav 		}
235a04a10f8SKris Kennaway 
236ca3176e7SBrian Feldman 		/* Return false if one of user's groups is listed in DenyGroups */
237ca3176e7SBrian Feldman 		if (options.num_deny_groups > 0)
238ca3176e7SBrian Feldman 			if (ga_match(options.deny_groups,
239ca3176e7SBrian Feldman 			    options.num_deny_groups)) {
240ca3176e7SBrian Feldman 				ga_free();
241aa49c926SDag-Erling Smørgrav 				logit("User %.100s from %.100s not allowed "
242aa49c926SDag-Erling Smørgrav 				    "because a group is listed in DenyGroups",
243aa49c926SDag-Erling Smørgrav 				    pw->pw_name, hostname);
244a04a10f8SKris Kennaway 				return 0;
245a04a10f8SKris Kennaway 			}
246a04a10f8SKris Kennaway 		/*
247ca3176e7SBrian Feldman 		 * Return false if AllowGroups isn't empty and one of user's groups
248a04a10f8SKris Kennaway 		 * isn't listed there
249a04a10f8SKris Kennaway 		 */
250ca3176e7SBrian Feldman 		if (options.num_allow_groups > 0)
251ca3176e7SBrian Feldman 			if (!ga_match(options.allow_groups,
252ca3176e7SBrian Feldman 			    options.num_allow_groups)) {
253ca3176e7SBrian Feldman 				ga_free();
254aa49c926SDag-Erling Smørgrav 				logit("User %.100s from %.100s not allowed "
255aa49c926SDag-Erling Smørgrav 				    "because none of user's groups are listed "
256aa49c926SDag-Erling Smørgrav 				    "in AllowGroups", pw->pw_name, hostname);
257a04a10f8SKris Kennaway 				return 0;
258a04a10f8SKris Kennaway 			}
259ca3176e7SBrian Feldman 		ga_free();
260a04a10f8SKris Kennaway 	}
261989dd127SDag-Erling Smørgrav 
26221e764dfSDag-Erling Smørgrav #ifdef CUSTOM_SYS_AUTH_ALLOWED_USER
263aa49c926SDag-Erling Smørgrav 	if (!sys_auth_allowed_user(pw, &loginmsg))
264989dd127SDag-Erling Smørgrav 		return 0;
26521e764dfSDag-Erling Smørgrav #endif
266989dd127SDag-Erling Smørgrav 
267a04a10f8SKris Kennaway 	/* We found no reason not to let this user try to log on... */
268a04a10f8SKris Kennaway 	return 1;
269a04a10f8SKris Kennaway }
270ca3176e7SBrian Feldman 
2714f52dfbbSDag-Erling Smørgrav /*
2724f52dfbbSDag-Erling Smørgrav  * Formats any key left in authctxt->auth_method_key for inclusion in
2734f52dfbbSDag-Erling Smørgrav  * auth_log()'s message. Also includes authxtct->auth_method_info if present.
2744f52dfbbSDag-Erling Smørgrav  */
2754f52dfbbSDag-Erling Smørgrav static char *
2764f52dfbbSDag-Erling Smørgrav format_method_key(Authctxt *authctxt)
277e4a9863fSDag-Erling Smørgrav {
2784f52dfbbSDag-Erling Smørgrav 	const struct sshkey *key = authctxt->auth_method_key;
2794f52dfbbSDag-Erling Smørgrav 	const char *methinfo = authctxt->auth_method_info;
2802f513db7SEd Maste 	char *fp, *cafp, *ret = NULL;
281e4a9863fSDag-Erling Smørgrav 
2824f52dfbbSDag-Erling Smørgrav 	if (key == NULL)
2834f52dfbbSDag-Erling Smørgrav 		return NULL;
284e4a9863fSDag-Erling Smørgrav 
285190cef3dSDag-Erling Smørgrav 	if (sshkey_is_cert(key)) {
2862f513db7SEd Maste 		fp = sshkey_fingerprint(key,
2874f52dfbbSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT);
2882f513db7SEd Maste 		cafp = sshkey_fingerprint(key->cert->signature_key,
2892f513db7SEd Maste 		    options.fingerprint_hash, SSH_FP_DEFAULT);
2902f513db7SEd Maste 		xasprintf(&ret, "%s %s ID %s (serial %llu) CA %s %s%s%s",
2912f513db7SEd Maste 		    sshkey_type(key), fp == NULL ? "(null)" : fp,
2922f513db7SEd Maste 		    key->cert->key_id,
2934f52dfbbSDag-Erling Smørgrav 		    (unsigned long long)key->cert->serial,
2944f52dfbbSDag-Erling Smørgrav 		    sshkey_type(key->cert->signature_key),
2952f513db7SEd Maste 		    cafp == NULL ? "(null)" : cafp,
2964f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : ", ",
2974f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : methinfo);
2984f52dfbbSDag-Erling Smørgrav 		free(fp);
2992f513db7SEd Maste 		free(cafp);
3004f52dfbbSDag-Erling Smørgrav 	} else {
3014f52dfbbSDag-Erling Smørgrav 		fp = sshkey_fingerprint(key, options.fingerprint_hash,
3024f52dfbbSDag-Erling Smørgrav 		    SSH_FP_DEFAULT);
3034f52dfbbSDag-Erling Smørgrav 		xasprintf(&ret, "%s %s%s%s", sshkey_type(key),
3044f52dfbbSDag-Erling Smørgrav 		    fp == NULL ? "(null)" : fp,
3054f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : ", ",
3064f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : methinfo);
3074f52dfbbSDag-Erling Smørgrav 		free(fp);
3084f52dfbbSDag-Erling Smørgrav 	}
3094f52dfbbSDag-Erling Smørgrav 	return ret;
310e4a9863fSDag-Erling Smørgrav }
311e4a9863fSDag-Erling Smørgrav 
312e4a9863fSDag-Erling Smørgrav void
3136888a9beSDag-Erling Smørgrav auth_log(Authctxt *authctxt, int authenticated, int partial,
314e4a9863fSDag-Erling Smørgrav     const char *method, const char *submethod)
315ca3176e7SBrian Feldman {
316076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
3172f513db7SEd Maste 	int level = SYSLOG_LEVEL_VERBOSE;
3184f52dfbbSDag-Erling Smørgrav 	const char *authmsg;
3194f52dfbbSDag-Erling Smørgrav 	char *extra = NULL;
320ca3176e7SBrian Feldman 
321333ee039SDag-Erling Smørgrav 	if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
322333ee039SDag-Erling Smørgrav 		return;
323333ee039SDag-Erling Smørgrav 
324ca3176e7SBrian Feldman 	/* Raise logging level */
325ca3176e7SBrian Feldman 	if (authenticated == 1 ||
326ca3176e7SBrian Feldman 	    !authctxt->valid ||
32721e764dfSDag-Erling Smørgrav 	    authctxt->failures >= options.max_authtries / 2 ||
328ca3176e7SBrian Feldman 	    strcmp(method, "password") == 0)
3292f513db7SEd Maste 		level = SYSLOG_LEVEL_INFO;
330ca3176e7SBrian Feldman 
331ca3176e7SBrian Feldman 	if (authctxt->postponed)
332ca3176e7SBrian Feldman 		authmsg = "Postponed";
3336888a9beSDag-Erling Smørgrav 	else if (partial)
3346888a9beSDag-Erling Smørgrav 		authmsg = "Partial";
335b2af61ecSKurt Lidl 	else {
336ca3176e7SBrian Feldman 		authmsg = authenticated ? "Accepted" : "Failed";
3375057f656SKurt Lidl 		if (authenticated)
338342b8b88SKurt Lidl 			BLACKLIST_NOTIFY(BLACKLIST_AUTH_OK, "ssh");
339b2af61ecSKurt Lidl 	}
340ca3176e7SBrian Feldman 
3414f52dfbbSDag-Erling Smørgrav 	if ((extra = format_method_key(authctxt)) == NULL) {
3424f52dfbbSDag-Erling Smørgrav 		if (authctxt->auth_method_info != NULL)
3434f52dfbbSDag-Erling Smørgrav 			extra = xstrdup(authctxt->auth_method_info);
3444f52dfbbSDag-Erling Smørgrav 	}
3454f52dfbbSDag-Erling Smørgrav 
3462f513db7SEd Maste 	do_log2(level, "%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s",
347ca3176e7SBrian Feldman 	    authmsg,
348ca3176e7SBrian Feldman 	    method,
3496888a9beSDag-Erling Smørgrav 	    submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
35021e764dfSDag-Erling Smørgrav 	    authctxt->valid ? "" : "invalid user ",
351af12a3e7SDag-Erling Smørgrav 	    authctxt->user,
352076ad2f8SDag-Erling Smørgrav 	    ssh_remote_ipaddr(ssh),
353076ad2f8SDag-Erling Smørgrav 	    ssh_remote_port(ssh),
3544f52dfbbSDag-Erling Smørgrav 	    extra != NULL ? ": " : "",
3554f52dfbbSDag-Erling Smørgrav 	    extra != NULL ? extra : "");
3564f52dfbbSDag-Erling Smørgrav 
3574f52dfbbSDag-Erling Smørgrav 	free(extra);
358f388f5efSDag-Erling Smørgrav 
359cf2b5f3bSDag-Erling Smørgrav #ifdef CUSTOM_FAILED_LOGIN
360aa49c926SDag-Erling Smørgrav 	if (authenticated == 0 && !authctxt->postponed &&
361aa49c926SDag-Erling Smørgrav 	    (strcmp(method, "password") == 0 ||
362aa49c926SDag-Erling Smørgrav 	    strncmp(method, "keyboard-interactive", 20) == 0 ||
363aa49c926SDag-Erling Smørgrav 	    strcmp(method, "challenge-response") == 0))
364aa49c926SDag-Erling Smørgrav 		record_failed_login(authctxt->user,
365076ad2f8SDag-Erling Smørgrav 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh");
366333ee039SDag-Erling Smørgrav # ifdef WITH_AIXAUTHENTICATE
367333ee039SDag-Erling Smørgrav 	if (authenticated)
368333ee039SDag-Erling Smørgrav 		sys_auth_record_login(authctxt->user,
369076ad2f8SDag-Erling Smørgrav 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh",
370076ad2f8SDag-Erling Smørgrav 		    &loginmsg);
371333ee039SDag-Erling Smørgrav # endif
372aa49c926SDag-Erling Smørgrav #endif
373aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS
374333ee039SDag-Erling Smørgrav 	if (authenticated == 0 && !authctxt->postponed)
375333ee039SDag-Erling Smørgrav 		audit_event(audit_classify_auth(method));
376cf2b5f3bSDag-Erling Smørgrav #endif
377ca3176e7SBrian Feldman }
378ca3176e7SBrian Feldman 
379a0ee8cc6SDag-Erling Smørgrav 
380a0ee8cc6SDag-Erling Smørgrav void
381a0ee8cc6SDag-Erling Smørgrav auth_maxtries_exceeded(Authctxt *authctxt)
382a0ee8cc6SDag-Erling Smørgrav {
383076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
384076ad2f8SDag-Erling Smørgrav 
385bc5531deSDag-Erling Smørgrav 	error("maximum authentication attempts exceeded for "
386ca86bcf2SDag-Erling Smørgrav 	    "%s%.100s from %.200s port %d ssh2",
387a0ee8cc6SDag-Erling Smørgrav 	    authctxt->valid ? "" : "invalid user ",
388a0ee8cc6SDag-Erling Smørgrav 	    authctxt->user,
389076ad2f8SDag-Erling Smørgrav 	    ssh_remote_ipaddr(ssh),
390ca86bcf2SDag-Erling Smørgrav 	    ssh_remote_port(ssh));
391bc5531deSDag-Erling Smørgrav 	packet_disconnect("Too many authentication failures");
392a0ee8cc6SDag-Erling Smørgrav 	/* NOTREACHED */
393a0ee8cc6SDag-Erling Smørgrav }
394a0ee8cc6SDag-Erling Smørgrav 
395ca3176e7SBrian Feldman /*
396ca3176e7SBrian Feldman  * Check whether root logins are disallowed.
397ca3176e7SBrian Feldman  */
398ca3176e7SBrian Feldman int
39947dd1d1bSDag-Erling Smørgrav auth_root_allowed(struct ssh *ssh, const char *method)
400ca3176e7SBrian Feldman {
401ca3176e7SBrian Feldman 	switch (options.permit_root_login) {
402ca3176e7SBrian Feldman 	case PERMIT_YES:
403ca3176e7SBrian Feldman 		return 1;
404ca3176e7SBrian Feldman 	case PERMIT_NO_PASSWD:
405eccfee6eSDag-Erling Smørgrav 		if (strcmp(method, "publickey") == 0 ||
406eccfee6eSDag-Erling Smørgrav 		    strcmp(method, "hostbased") == 0 ||
407fc1ba28aSDag-Erling Smørgrav 		    strcmp(method, "gssapi-with-mic") == 0)
408ca3176e7SBrian Feldman 			return 1;
409ca3176e7SBrian Feldman 		break;
410ca3176e7SBrian Feldman 	case PERMIT_FORCED_ONLY:
41147dd1d1bSDag-Erling Smørgrav 		if (auth_opts->force_command != NULL) {
412cf2b5f3bSDag-Erling Smørgrav 			logit("Root login accepted for forced command.");
413ca3176e7SBrian Feldman 			return 1;
414ca3176e7SBrian Feldman 		}
415ca3176e7SBrian Feldman 		break;
416ca3176e7SBrian Feldman 	}
417076ad2f8SDag-Erling Smørgrav 	logit("ROOT LOGIN REFUSED FROM %.200s port %d",
418076ad2f8SDag-Erling Smørgrav 	    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
419ca3176e7SBrian Feldman 	return 0;
420ca3176e7SBrian Feldman }
421af12a3e7SDag-Erling Smørgrav 
422af12a3e7SDag-Erling Smørgrav 
423af12a3e7SDag-Erling Smørgrav /*
424af12a3e7SDag-Erling Smørgrav  * Given a template and a passwd structure, build a filename
425af12a3e7SDag-Erling Smørgrav  * by substituting % tokenised options. Currently, %% becomes '%',
426af12a3e7SDag-Erling Smørgrav  * %h becomes the home directory and %u the username.
427af12a3e7SDag-Erling Smørgrav  *
428af12a3e7SDag-Erling Smørgrav  * This returns a buffer allocated by xmalloc.
429af12a3e7SDag-Erling Smørgrav  */
430e146993eSDag-Erling Smørgrav char *
431d4ecd108SDag-Erling Smørgrav expand_authorized_keys(const char *filename, struct passwd *pw)
432af12a3e7SDag-Erling Smørgrav {
433190cef3dSDag-Erling Smørgrav 	char *file, uidstr[32], ret[PATH_MAX];
434333ee039SDag-Erling Smørgrav 	int i;
435af12a3e7SDag-Erling Smørgrav 
436190cef3dSDag-Erling Smørgrav 	snprintf(uidstr, sizeof(uidstr), "%llu",
437190cef3dSDag-Erling Smørgrav 	    (unsigned long long)pw->pw_uid);
438d4ecd108SDag-Erling Smørgrav 	file = percent_expand(filename, "h", pw->pw_dir,
439190cef3dSDag-Erling Smørgrav 	    "u", pw->pw_name, "U", uidstr, (char *)NULL);
440af12a3e7SDag-Erling Smørgrav 
441af12a3e7SDag-Erling Smørgrav 	/*
442af12a3e7SDag-Erling Smørgrav 	 * Ensure that filename starts anchored. If not, be backward
443af12a3e7SDag-Erling Smørgrav 	 * compatible and prepend the '%h/'
444af12a3e7SDag-Erling Smørgrav 	 */
445d4ecd108SDag-Erling Smørgrav 	if (*file == '/')
446d4ecd108SDag-Erling Smørgrav 		return (file);
447af12a3e7SDag-Erling Smørgrav 
448333ee039SDag-Erling Smørgrav 	i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file);
449333ee039SDag-Erling Smørgrav 	if (i < 0 || (size_t)i >= sizeof(ret))
450d4ecd108SDag-Erling Smørgrav 		fatal("expand_authorized_keys: path too long");
451e4a9863fSDag-Erling Smørgrav 	free(file);
452333ee039SDag-Erling Smørgrav 	return (xstrdup(ret));
453af12a3e7SDag-Erling Smørgrav }
454af12a3e7SDag-Erling Smørgrav 
455af12a3e7SDag-Erling Smørgrav char *
456e2f6069cSDag-Erling Smørgrav authorized_principals_file(struct passwd *pw)
457e2f6069cSDag-Erling Smørgrav {
458557f75e5SDag-Erling Smørgrav 	if (options.authorized_principals_file == NULL)
459e2f6069cSDag-Erling Smørgrav 		return NULL;
460e2f6069cSDag-Erling Smørgrav 	return expand_authorized_keys(options.authorized_principals_file, pw);
461e2f6069cSDag-Erling Smørgrav }
462e2f6069cSDag-Erling Smørgrav 
463af12a3e7SDag-Erling Smørgrav /* return ok if key exists in sysfile or userfile */
464af12a3e7SDag-Erling Smørgrav HostStatus
4654f52dfbbSDag-Erling Smørgrav check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
466af12a3e7SDag-Erling Smørgrav     const char *sysfile, const char *userfile)
467af12a3e7SDag-Erling Smørgrav {
468af12a3e7SDag-Erling Smørgrav 	char *user_hostfile;
469af12a3e7SDag-Erling Smørgrav 	struct stat st;
470af12a3e7SDag-Erling Smørgrav 	HostStatus host_status;
4714a421b63SDag-Erling Smørgrav 	struct hostkeys *hostkeys;
4724a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *found;
473af12a3e7SDag-Erling Smørgrav 
4744a421b63SDag-Erling Smørgrav 	hostkeys = init_hostkeys();
4754a421b63SDag-Erling Smørgrav 	load_hostkeys(hostkeys, host, sysfile);
4764a421b63SDag-Erling Smørgrav 	if (userfile != NULL) {
477af12a3e7SDag-Erling Smørgrav 		user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
478af12a3e7SDag-Erling Smørgrav 		if (options.strict_modes &&
479af12a3e7SDag-Erling Smørgrav 		    (stat(user_hostfile, &st) == 0) &&
480af12a3e7SDag-Erling Smørgrav 		    ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
481af12a3e7SDag-Erling Smørgrav 		    (st.st_mode & 022) != 0)) {
482cf2b5f3bSDag-Erling Smørgrav 			logit("Authentication refused for %.100s: "
483af12a3e7SDag-Erling Smørgrav 			    "bad owner or modes for %.200s",
484af12a3e7SDag-Erling Smørgrav 			    pw->pw_name, user_hostfile);
485e2f6069cSDag-Erling Smørgrav 			auth_debug_add("Ignored %.200s: bad ownership or modes",
486e2f6069cSDag-Erling Smørgrav 			    user_hostfile);
487af12a3e7SDag-Erling Smørgrav 		} else {
488af12a3e7SDag-Erling Smørgrav 			temporarily_use_uid(pw);
4894a421b63SDag-Erling Smørgrav 			load_hostkeys(hostkeys, host, user_hostfile);
490af12a3e7SDag-Erling Smørgrav 			restore_uid();
491af12a3e7SDag-Erling Smørgrav 		}
492e4a9863fSDag-Erling Smørgrav 		free(user_hostfile);
493af12a3e7SDag-Erling Smørgrav 	}
4944a421b63SDag-Erling Smørgrav 	host_status = check_key_in_hostkeys(hostkeys, key, &found);
4954a421b63SDag-Erling Smørgrav 	if (host_status == HOST_REVOKED)
4964a421b63SDag-Erling Smørgrav 		error("WARNING: revoked key for %s attempted authentication",
4974a421b63SDag-Erling Smørgrav 		    found->host);
4984a421b63SDag-Erling Smørgrav 	else if (host_status == HOST_OK)
4994a421b63SDag-Erling Smørgrav 		debug("%s: key for %s found at %s:%ld", __func__,
5004a421b63SDag-Erling Smørgrav 		    found->host, found->file, found->line);
5014a421b63SDag-Erling Smørgrav 	else
5024a421b63SDag-Erling Smørgrav 		debug("%s: key for host %s not found", __func__, host);
503af12a3e7SDag-Erling Smørgrav 
5044a421b63SDag-Erling Smørgrav 	free_hostkeys(hostkeys);
5054a421b63SDag-Erling Smørgrav 
506af12a3e7SDag-Erling Smørgrav 	return host_status;
507af12a3e7SDag-Erling Smørgrav }
508af12a3e7SDag-Erling Smørgrav 
509e2f6069cSDag-Erling Smørgrav static FILE *
510e2f6069cSDag-Erling Smørgrav auth_openfile(const char *file, struct passwd *pw, int strict_modes,
511e2f6069cSDag-Erling Smørgrav     int log_missing, char *file_type)
512d4af9e69SDag-Erling Smørgrav {
513d4af9e69SDag-Erling Smørgrav 	char line[1024];
514d4af9e69SDag-Erling Smørgrav 	struct stat st;
515d4af9e69SDag-Erling Smørgrav 	int fd;
516d4af9e69SDag-Erling Smørgrav 	FILE *f;
517d4af9e69SDag-Erling Smørgrav 
518b15c8340SDag-Erling Smørgrav 	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
519e2f6069cSDag-Erling Smørgrav 		if (log_missing || errno != ENOENT)
520e2f6069cSDag-Erling Smørgrav 			debug("Could not open %s '%s': %s", file_type, file,
521b15c8340SDag-Erling Smørgrav 			   strerror(errno));
522d4af9e69SDag-Erling Smørgrav 		return NULL;
523b15c8340SDag-Erling Smørgrav 	}
524d4af9e69SDag-Erling Smørgrav 
525d4af9e69SDag-Erling Smørgrav 	if (fstat(fd, &st) < 0) {
526d4af9e69SDag-Erling Smørgrav 		close(fd);
527d4af9e69SDag-Erling Smørgrav 		return NULL;
528d4af9e69SDag-Erling Smørgrav 	}
529d4af9e69SDag-Erling Smørgrav 	if (!S_ISREG(st.st_mode)) {
530e2f6069cSDag-Erling Smørgrav 		logit("User %s %s %s is not a regular file",
531e2f6069cSDag-Erling Smørgrav 		    pw->pw_name, file_type, file);
532d4af9e69SDag-Erling Smørgrav 		close(fd);
533d4af9e69SDag-Erling Smørgrav 		return NULL;
534d4af9e69SDag-Erling Smørgrav 	}
535d4af9e69SDag-Erling Smørgrav 	unset_nonblock(fd);
536d4af9e69SDag-Erling Smørgrav 	if ((f = fdopen(fd, "r")) == NULL) {
537d4af9e69SDag-Erling Smørgrav 		close(fd);
538d4af9e69SDag-Erling Smørgrav 		return NULL;
539d4af9e69SDag-Erling Smørgrav 	}
5404a421b63SDag-Erling Smørgrav 	if (strict_modes &&
5414f52dfbbSDag-Erling Smørgrav 	    safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) {
542d4af9e69SDag-Erling Smørgrav 		fclose(f);
543d4af9e69SDag-Erling Smørgrav 		logit("Authentication refused: %s", line);
544e2f6069cSDag-Erling Smørgrav 		auth_debug_add("Ignored %s: %s", file_type, line);
545d4af9e69SDag-Erling Smørgrav 		return NULL;
546d4af9e69SDag-Erling Smørgrav 	}
547d4af9e69SDag-Erling Smørgrav 
548d4af9e69SDag-Erling Smørgrav 	return f;
549d4af9e69SDag-Erling Smørgrav }
550d4af9e69SDag-Erling Smørgrav 
551e2f6069cSDag-Erling Smørgrav 
552e2f6069cSDag-Erling Smørgrav FILE *
553e2f6069cSDag-Erling Smørgrav auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
554e2f6069cSDag-Erling Smørgrav {
555e2f6069cSDag-Erling Smørgrav 	return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
556e2f6069cSDag-Erling Smørgrav }
557e2f6069cSDag-Erling Smørgrav 
558e2f6069cSDag-Erling Smørgrav FILE *
559e2f6069cSDag-Erling Smørgrav auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
560e2f6069cSDag-Erling Smørgrav {
561e2f6069cSDag-Erling Smørgrav 	return auth_openfile(file, pw, strict_modes, 0,
562e2f6069cSDag-Erling Smørgrav 	    "authorized principals");
563e2f6069cSDag-Erling Smørgrav }
564e2f6069cSDag-Erling Smørgrav 
56580628bacSDag-Erling Smørgrav struct passwd *
56680628bacSDag-Erling Smørgrav getpwnamallow(const char *user)
56780628bacSDag-Erling Smørgrav {
568076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
56980628bacSDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
57080628bacSDag-Erling Smørgrav 	extern login_cap_t *lc;
571*27ceebbcSEd Maste #ifdef HAVE_AUTH_HOSTOK
572*27ceebbcSEd Maste 	const char *from_host, *from_ip;
573*27ceebbcSEd Maste #endif
57480628bacSDag-Erling Smørgrav #ifdef BSD_AUTH
57580628bacSDag-Erling Smørgrav 	auth_session_t *as;
57680628bacSDag-Erling Smørgrav #endif
57780628bacSDag-Erling Smørgrav #endif
57880628bacSDag-Erling Smørgrav 	struct passwd *pw;
579462c32cbSDag-Erling Smørgrav 	struct connection_info *ci = get_connection_info(1, options.use_dns);
58080628bacSDag-Erling Smørgrav 
581462c32cbSDag-Erling Smørgrav 	ci->user = user;
582462c32cbSDag-Erling Smørgrav 	parse_server_match_config(&options, ci);
5834f52dfbbSDag-Erling Smørgrav 	log_change_level(options.log_level);
5844f52dfbbSDag-Erling Smørgrav 	process_permitopen(ssh, &options);
585333ee039SDag-Erling Smørgrav 
586b15c8340SDag-Erling Smørgrav #if defined(_AIX) && defined(HAVE_SETAUTHDB)
587b15c8340SDag-Erling Smørgrav 	aix_setauthdb(user);
588b15c8340SDag-Erling Smørgrav #endif
589b15c8340SDag-Erling Smørgrav 
59080628bacSDag-Erling Smørgrav 	pw = getpwnam(user);
591b15c8340SDag-Erling Smørgrav 
592b15c8340SDag-Erling Smørgrav #if defined(_AIX) && defined(HAVE_SETAUTHDB)
593b15c8340SDag-Erling Smørgrav 	aix_restoreauthdb();
594b15c8340SDag-Erling Smørgrav #endif
595b15c8340SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
596b15c8340SDag-Erling Smørgrav 	/*
597b15c8340SDag-Erling Smørgrav 	 * Windows usernames are case-insensitive.  To avoid later problems
598b15c8340SDag-Erling Smørgrav 	 * when trying to match the username, the user is only allowed to
599b15c8340SDag-Erling Smørgrav 	 * login if the username is given in the same case as stored in the
600b15c8340SDag-Erling Smørgrav 	 * user database.
601b15c8340SDag-Erling Smørgrav 	 */
602b15c8340SDag-Erling Smørgrav 	if (pw != NULL && strcmp(user, pw->pw_name) != 0) {
603b15c8340SDag-Erling Smørgrav 		logit("Login name %.100s does not match stored username %.100s",
604b15c8340SDag-Erling Smørgrav 		    user, pw->pw_name);
605b15c8340SDag-Erling Smørgrav 		pw = NULL;
606b15c8340SDag-Erling Smørgrav 	}
607b15c8340SDag-Erling Smørgrav #endif
608f388f5efSDag-Erling Smørgrav 	if (pw == NULL) {
609342b8b88SKurt Lidl 		BLACKLIST_NOTIFY(BLACKLIST_BAD_USER, user);
610076ad2f8SDag-Erling Smørgrav 		logit("Invalid user %.100s from %.100s port %d",
611076ad2f8SDag-Erling Smørgrav 		    user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
612cf2b5f3bSDag-Erling Smørgrav #ifdef CUSTOM_FAILED_LOGIN
613aa49c926SDag-Erling Smørgrav 		record_failed_login(user,
614076ad2f8SDag-Erling Smørgrav 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh");
615e73e9afaSDag-Erling Smørgrav #endif
616aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS
617aa49c926SDag-Erling Smørgrav 		audit_event(SSH_INVALID_USER);
618aa49c926SDag-Erling Smørgrav #endif /* SSH_AUDIT_EVENTS */
619f388f5efSDag-Erling Smørgrav 		return (NULL);
620f388f5efSDag-Erling Smørgrav 	}
621f388f5efSDag-Erling Smørgrav 	if (!allowed_user(pw))
62280628bacSDag-Erling Smørgrav 		return (NULL);
62380628bacSDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
624f38aa77fSTony Finch 	if ((lc = login_getpwclass(pw)) == NULL) {
62580628bacSDag-Erling Smørgrav 		debug("unable to get login class: %s", user);
62680628bacSDag-Erling Smørgrav 		return (NULL);
62780628bacSDag-Erling Smørgrav 	}
628*27ceebbcSEd Maste #ifdef HAVE_AUTH_HOSTOK
629*27ceebbcSEd Maste 	from_host = auth_get_canonical_hostname(ssh, options.use_dns);
630*27ceebbcSEd Maste 	from_ip = ssh_remote_ipaddr(ssh);
631*27ceebbcSEd Maste 	if (!auth_hostok(lc, from_host, from_ip)) {
632*27ceebbcSEd Maste 		debug("Denied connection for %.200s from %.200s [%.200s].",
633*27ceebbcSEd Maste 		    pw->pw_name, from_host, from_ip);
634*27ceebbcSEd Maste 		return (NULL);
635*27ceebbcSEd Maste 	}
636*27ceebbcSEd Maste #endif /* HAVE_AUTH_HOSTOK */
637*27ceebbcSEd Maste #ifdef HAVE_AUTH_TIMEOK
638*27ceebbcSEd Maste 	if (!auth_timeok(lc, time(NULL))) {
639*27ceebbcSEd Maste 		debug("LOGIN %.200s REFUSED (TIME)", pw->pw_name);
640*27ceebbcSEd Maste 		return (NULL);
641*27ceebbcSEd Maste 	}
642*27ceebbcSEd Maste #endif /* HAVE_AUTH_TIMEOK */
64380628bacSDag-Erling Smørgrav #ifdef BSD_AUTH
64480628bacSDag-Erling Smørgrav 	if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
64580628bacSDag-Erling Smørgrav 	    auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
64680628bacSDag-Erling Smørgrav 		debug("Approval failure for %s", user);
64780628bacSDag-Erling Smørgrav 		pw = NULL;
64880628bacSDag-Erling Smørgrav 	}
64980628bacSDag-Erling Smørgrav 	if (as != NULL)
65080628bacSDag-Erling Smørgrav 		auth_close(as);
65180628bacSDag-Erling Smørgrav #endif
65280628bacSDag-Erling Smørgrav #endif
65380628bacSDag-Erling Smørgrav 	if (pw != NULL)
65480628bacSDag-Erling Smørgrav 		return (pwcopy(pw));
65580628bacSDag-Erling Smørgrav 	return (NULL);
65680628bacSDag-Erling Smørgrav }
65780628bacSDag-Erling Smørgrav 
658b15c8340SDag-Erling Smørgrav /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
659b15c8340SDag-Erling Smørgrav int
6604f52dfbbSDag-Erling Smørgrav auth_key_is_revoked(struct sshkey *key)
661b15c8340SDag-Erling Smørgrav {
662bc5531deSDag-Erling Smørgrav 	char *fp = NULL;
663bc5531deSDag-Erling Smørgrav 	int r;
664b15c8340SDag-Erling Smørgrav 
665b15c8340SDag-Erling Smørgrav 	if (options.revoked_keys_file == NULL)
666b15c8340SDag-Erling Smørgrav 		return 0;
667bc5531deSDag-Erling Smørgrav 	if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
668bc5531deSDag-Erling Smørgrav 	    SSH_FP_DEFAULT)) == NULL) {
669bc5531deSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
670bc5531deSDag-Erling Smørgrav 		error("%s: fingerprint key: %s", __func__, ssh_err(r));
671bc5531deSDag-Erling Smørgrav 		goto out;
672bc5531deSDag-Erling Smørgrav 	}
673bc5531deSDag-Erling Smørgrav 
674bc5531deSDag-Erling Smørgrav 	r = sshkey_check_revoked(key, options.revoked_keys_file);
675bc5531deSDag-Erling Smørgrav 	switch (r) {
6766888a9beSDag-Erling Smørgrav 	case 0:
677bc5531deSDag-Erling Smørgrav 		break; /* not revoked */
678bc5531deSDag-Erling Smørgrav 	case SSH_ERR_KEY_REVOKED:
679bc5531deSDag-Erling Smørgrav 		error("Authentication key %s %s revoked by file %s",
680bc5531deSDag-Erling Smørgrav 		    sshkey_type(key), fp, options.revoked_keys_file);
681bc5531deSDag-Erling Smørgrav 		goto out;
6826888a9beSDag-Erling Smørgrav 	default:
683bc5531deSDag-Erling Smørgrav 		error("Error checking authentication key %s %s in "
684bc5531deSDag-Erling Smørgrav 		    "revoked keys file %s: %s", sshkey_type(key), fp,
685bc5531deSDag-Erling Smørgrav 		    options.revoked_keys_file, ssh_err(r));
686bc5531deSDag-Erling Smørgrav 		goto out;
6876888a9beSDag-Erling Smørgrav 	}
688bc5531deSDag-Erling Smørgrav 
689bc5531deSDag-Erling Smørgrav 	/* Success */
690bc5531deSDag-Erling Smørgrav 	r = 0;
691bc5531deSDag-Erling Smørgrav 
692bc5531deSDag-Erling Smørgrav  out:
693bc5531deSDag-Erling Smørgrav 	free(fp);
694bc5531deSDag-Erling Smørgrav 	return r == 0 ? 0 : 1;
695b15c8340SDag-Erling Smørgrav }
696b15c8340SDag-Erling Smørgrav 
69780628bacSDag-Erling Smørgrav void
69880628bacSDag-Erling Smørgrav auth_debug_add(const char *fmt,...)
69980628bacSDag-Erling Smørgrav {
70080628bacSDag-Erling Smørgrav 	char buf[1024];
70180628bacSDag-Erling Smørgrav 	va_list args;
702190cef3dSDag-Erling Smørgrav 	int r;
70380628bacSDag-Erling Smørgrav 
704190cef3dSDag-Erling Smørgrav 	if (auth_debug == NULL)
70580628bacSDag-Erling Smørgrav 		return;
70680628bacSDag-Erling Smørgrav 
70780628bacSDag-Erling Smørgrav 	va_start(args, fmt);
70880628bacSDag-Erling Smørgrav 	vsnprintf(buf, sizeof(buf), fmt, args);
70980628bacSDag-Erling Smørgrav 	va_end(args);
710190cef3dSDag-Erling Smørgrav 	if ((r = sshbuf_put_cstring(auth_debug, buf)) != 0)
711190cef3dSDag-Erling Smørgrav 		fatal("%s: sshbuf_put_cstring: %s", __func__, ssh_err(r));
71280628bacSDag-Erling Smørgrav }
71380628bacSDag-Erling Smørgrav 
71480628bacSDag-Erling Smørgrav void
71580628bacSDag-Erling Smørgrav auth_debug_send(void)
71680628bacSDag-Erling Smørgrav {
717190cef3dSDag-Erling Smørgrav 	struct ssh *ssh = active_state;		/* XXX */
71880628bacSDag-Erling Smørgrav 	char *msg;
719190cef3dSDag-Erling Smørgrav 	int r;
72080628bacSDag-Erling Smørgrav 
721190cef3dSDag-Erling Smørgrav 	if (auth_debug == NULL)
72280628bacSDag-Erling Smørgrav 		return;
723190cef3dSDag-Erling Smørgrav 	while (sshbuf_len(auth_debug) != 0) {
724190cef3dSDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(auth_debug, &msg, NULL)) != 0)
725190cef3dSDag-Erling Smørgrav 			fatal("%s: sshbuf_get_cstring: %s",
726190cef3dSDag-Erling Smørgrav 			    __func__, ssh_err(r));
727190cef3dSDag-Erling Smørgrav 		ssh_packet_send_debug(ssh, "%s", msg);
728e4a9863fSDag-Erling Smørgrav 		free(msg);
72980628bacSDag-Erling Smørgrav 	}
73080628bacSDag-Erling Smørgrav }
73180628bacSDag-Erling Smørgrav 
73280628bacSDag-Erling Smørgrav void
73380628bacSDag-Erling Smørgrav auth_debug_reset(void)
73480628bacSDag-Erling Smørgrav {
735190cef3dSDag-Erling Smørgrav 	if (auth_debug != NULL)
736190cef3dSDag-Erling Smørgrav 		sshbuf_reset(auth_debug);
737190cef3dSDag-Erling Smørgrav 	else if ((auth_debug = sshbuf_new()) == NULL)
738190cef3dSDag-Erling Smørgrav 		fatal("%s: sshbuf_new failed", __func__);
73980628bacSDag-Erling Smørgrav }
740cf2b5f3bSDag-Erling Smørgrav 
741cf2b5f3bSDag-Erling Smørgrav struct passwd *
742cf2b5f3bSDag-Erling Smørgrav fakepw(void)
743cf2b5f3bSDag-Erling Smørgrav {
744cf2b5f3bSDag-Erling Smørgrav 	static struct passwd fake;
745cf2b5f3bSDag-Erling Smørgrav 
746cf2b5f3bSDag-Erling Smørgrav 	memset(&fake, 0, sizeof(fake));
747cf2b5f3bSDag-Erling Smørgrav 	fake.pw_name = "NOUSER";
748cf2b5f3bSDag-Erling Smørgrav 	fake.pw_passwd =
749cf2b5f3bSDag-Erling Smørgrav 	    "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK";
750e4a9863fSDag-Erling Smørgrav #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
751cf2b5f3bSDag-Erling Smørgrav 	fake.pw_gecos = "NOUSER";
752e4a9863fSDag-Erling Smørgrav #endif
753d4af9e69SDag-Erling Smørgrav 	fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid;
754d4af9e69SDag-Erling Smørgrav 	fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid;
755e4a9863fSDag-Erling Smørgrav #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
756cf2b5f3bSDag-Erling Smørgrav 	fake.pw_class = "";
757cf2b5f3bSDag-Erling Smørgrav #endif
758cf2b5f3bSDag-Erling Smørgrav 	fake.pw_dir = "/nonexist";
759cf2b5f3bSDag-Erling Smørgrav 	fake.pw_shell = "/nonexist";
760cf2b5f3bSDag-Erling Smørgrav 
761cf2b5f3bSDag-Erling Smørgrav 	return (&fake);
762cf2b5f3bSDag-Erling Smørgrav }
763076ad2f8SDag-Erling Smørgrav 
764076ad2f8SDag-Erling Smørgrav /*
765076ad2f8SDag-Erling Smørgrav  * Returns the remote DNS hostname as a string. The returned string must not
766076ad2f8SDag-Erling Smørgrav  * be freed. NB. this will usually trigger a DNS query the first time it is
767076ad2f8SDag-Erling Smørgrav  * called.
768076ad2f8SDag-Erling Smørgrav  * This function does additional checks on the hostname to mitigate some
769076ad2f8SDag-Erling Smørgrav  * attacks on legacy rhosts-style authentication.
770076ad2f8SDag-Erling Smørgrav  * XXX is RhostsRSAAuthentication vulnerable to these?
771076ad2f8SDag-Erling Smørgrav  * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
772076ad2f8SDag-Erling Smørgrav  */
773076ad2f8SDag-Erling Smørgrav 
774076ad2f8SDag-Erling Smørgrav static char *
775076ad2f8SDag-Erling Smørgrav remote_hostname(struct ssh *ssh)
776076ad2f8SDag-Erling Smørgrav {
777076ad2f8SDag-Erling Smørgrav 	struct sockaddr_storage from;
778076ad2f8SDag-Erling Smørgrav 	socklen_t fromlen;
779076ad2f8SDag-Erling Smørgrav 	struct addrinfo hints, *ai, *aitop;
780076ad2f8SDag-Erling Smørgrav 	char name[NI_MAXHOST], ntop2[NI_MAXHOST];
781076ad2f8SDag-Erling Smørgrav 	const char *ntop = ssh_remote_ipaddr(ssh);
782076ad2f8SDag-Erling Smørgrav 
783076ad2f8SDag-Erling Smørgrav 	/* Get IP address of client. */
784076ad2f8SDag-Erling Smørgrav 	fromlen = sizeof(from);
785076ad2f8SDag-Erling Smørgrav 	memset(&from, 0, sizeof(from));
786076ad2f8SDag-Erling Smørgrav 	if (getpeername(ssh_packet_get_connection_in(ssh),
787076ad2f8SDag-Erling Smørgrav 	    (struct sockaddr *)&from, &fromlen) < 0) {
788076ad2f8SDag-Erling Smørgrav 		debug("getpeername failed: %.100s", strerror(errno));
789076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
790076ad2f8SDag-Erling Smørgrav 	}
791076ad2f8SDag-Erling Smørgrav 
792076ad2f8SDag-Erling Smørgrav 	ipv64_normalise_mapped(&from, &fromlen);
793076ad2f8SDag-Erling Smørgrav 	if (from.ss_family == AF_INET6)
794076ad2f8SDag-Erling Smørgrav 		fromlen = sizeof(struct sockaddr_in6);
795076ad2f8SDag-Erling Smørgrav 
796076ad2f8SDag-Erling Smørgrav 	debug3("Trying to reverse map address %.100s.", ntop);
797076ad2f8SDag-Erling Smørgrav 	/* Map the IP address to a host name. */
798076ad2f8SDag-Erling Smørgrav 	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
799076ad2f8SDag-Erling Smørgrav 	    NULL, 0, NI_NAMEREQD) != 0) {
800076ad2f8SDag-Erling Smørgrav 		/* Host name not found.  Use ip address. */
801076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
802076ad2f8SDag-Erling Smørgrav 	}
803076ad2f8SDag-Erling Smørgrav 
804076ad2f8SDag-Erling Smørgrav 	/*
805076ad2f8SDag-Erling Smørgrav 	 * if reverse lookup result looks like a numeric hostname,
806076ad2f8SDag-Erling Smørgrav 	 * someone is trying to trick us by PTR record like following:
807076ad2f8SDag-Erling Smørgrav 	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
808076ad2f8SDag-Erling Smørgrav 	 */
809076ad2f8SDag-Erling Smørgrav 	memset(&hints, 0, sizeof(hints));
810076ad2f8SDag-Erling Smørgrav 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
811076ad2f8SDag-Erling Smørgrav 	hints.ai_flags = AI_NUMERICHOST;
812076ad2f8SDag-Erling Smørgrav 	if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
813076ad2f8SDag-Erling Smørgrav 		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
814076ad2f8SDag-Erling Smørgrav 		    name, ntop);
815076ad2f8SDag-Erling Smørgrav 		freeaddrinfo(ai);
816076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
817076ad2f8SDag-Erling Smørgrav 	}
818076ad2f8SDag-Erling Smørgrav 
819076ad2f8SDag-Erling Smørgrav 	/* Names are stored in lowercase. */
820076ad2f8SDag-Erling Smørgrav 	lowercase(name);
821076ad2f8SDag-Erling Smørgrav 
822076ad2f8SDag-Erling Smørgrav 	/*
823076ad2f8SDag-Erling Smørgrav 	 * Map it back to an IP address and check that the given
824076ad2f8SDag-Erling Smørgrav 	 * address actually is an address of this host.  This is
825076ad2f8SDag-Erling Smørgrav 	 * necessary because anyone with access to a name server can
826076ad2f8SDag-Erling Smørgrav 	 * define arbitrary names for an IP address. Mapping from
827076ad2f8SDag-Erling Smørgrav 	 * name to IP address can be trusted better (but can still be
828076ad2f8SDag-Erling Smørgrav 	 * fooled if the intruder has access to the name server of
829076ad2f8SDag-Erling Smørgrav 	 * the domain).
830076ad2f8SDag-Erling Smørgrav 	 */
831076ad2f8SDag-Erling Smørgrav 	memset(&hints, 0, sizeof(hints));
832076ad2f8SDag-Erling Smørgrav 	hints.ai_family = from.ss_family;
833076ad2f8SDag-Erling Smørgrav 	hints.ai_socktype = SOCK_STREAM;
834076ad2f8SDag-Erling Smørgrav 	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
835076ad2f8SDag-Erling Smørgrav 		logit("reverse mapping checking getaddrinfo for %.700s "
836076ad2f8SDag-Erling Smørgrav 		    "[%s] failed.", name, ntop);
837076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
838076ad2f8SDag-Erling Smørgrav 	}
839076ad2f8SDag-Erling Smørgrav 	/* Look for the address from the list of addresses. */
840076ad2f8SDag-Erling Smørgrav 	for (ai = aitop; ai; ai = ai->ai_next) {
841076ad2f8SDag-Erling Smørgrav 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
842076ad2f8SDag-Erling Smørgrav 		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
843076ad2f8SDag-Erling Smørgrav 		    (strcmp(ntop, ntop2) == 0))
844076ad2f8SDag-Erling Smørgrav 				break;
845076ad2f8SDag-Erling Smørgrav 	}
846076ad2f8SDag-Erling Smørgrav 	freeaddrinfo(aitop);
847076ad2f8SDag-Erling Smørgrav 	/* If we reached the end of the list, the address was not there. */
848076ad2f8SDag-Erling Smørgrav 	if (ai == NULL) {
849076ad2f8SDag-Erling Smørgrav 		/* Address not found for the host name. */
850076ad2f8SDag-Erling Smørgrav 		logit("Address %.100s maps to %.600s, but this does not "
851076ad2f8SDag-Erling Smørgrav 		    "map back to the address.", ntop, name);
852076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
853076ad2f8SDag-Erling Smørgrav 	}
854076ad2f8SDag-Erling Smørgrav 	return strdup(name);
855076ad2f8SDag-Erling Smørgrav }
856076ad2f8SDag-Erling Smørgrav 
857076ad2f8SDag-Erling Smørgrav /*
858076ad2f8SDag-Erling Smørgrav  * Return the canonical name of the host in the other side of the current
859076ad2f8SDag-Erling Smørgrav  * connection.  The host name is cached, so it is efficient to call this
860076ad2f8SDag-Erling Smørgrav  * several times.
861076ad2f8SDag-Erling Smørgrav  */
862076ad2f8SDag-Erling Smørgrav 
863076ad2f8SDag-Erling Smørgrav const char *
864076ad2f8SDag-Erling Smørgrav auth_get_canonical_hostname(struct ssh *ssh, int use_dns)
865076ad2f8SDag-Erling Smørgrav {
866076ad2f8SDag-Erling Smørgrav 	static char *dnsname;
867076ad2f8SDag-Erling Smørgrav 
868076ad2f8SDag-Erling Smørgrav 	if (!use_dns)
869076ad2f8SDag-Erling Smørgrav 		return ssh_remote_ipaddr(ssh);
870076ad2f8SDag-Erling Smørgrav 	else if (dnsname != NULL)
871076ad2f8SDag-Erling Smørgrav 		return dnsname;
872076ad2f8SDag-Erling Smørgrav 	else {
873076ad2f8SDag-Erling Smørgrav 		dnsname = remote_hostname(ssh);
874076ad2f8SDag-Erling Smørgrav 		return dnsname;
875076ad2f8SDag-Erling Smørgrav 	}
876076ad2f8SDag-Erling Smørgrav }
87747dd1d1bSDag-Erling Smørgrav 
87847dd1d1bSDag-Erling Smørgrav /*
879190cef3dSDag-Erling Smørgrav  * Runs command in a subprocess with a minimal environment.
88047dd1d1bSDag-Erling Smørgrav  * Returns pid on success, 0 on failure.
88147dd1d1bSDag-Erling Smørgrav  * The child stdout and stderr maybe captured, left attached or sent to
88247dd1d1bSDag-Erling Smørgrav  * /dev/null depending on the contents of flags.
88347dd1d1bSDag-Erling Smørgrav  * "tag" is prepended to log messages.
88447dd1d1bSDag-Erling Smørgrav  * NB. "command" is only used for logging; the actual command executed is
88547dd1d1bSDag-Erling Smørgrav  * av[0].
88647dd1d1bSDag-Erling Smørgrav  */
88747dd1d1bSDag-Erling Smørgrav pid_t
88847dd1d1bSDag-Erling Smørgrav subprocess(const char *tag, struct passwd *pw, const char *command,
88947dd1d1bSDag-Erling Smørgrav     int ac, char **av, FILE **child, u_int flags)
89047dd1d1bSDag-Erling Smørgrav {
89147dd1d1bSDag-Erling Smørgrav 	FILE *f = NULL;
89247dd1d1bSDag-Erling Smørgrav 	struct stat st;
89347dd1d1bSDag-Erling Smørgrav 	int fd, devnull, p[2], i;
89447dd1d1bSDag-Erling Smørgrav 	pid_t pid;
89547dd1d1bSDag-Erling Smørgrav 	char *cp, errmsg[512];
89647dd1d1bSDag-Erling Smørgrav 	u_int envsize;
89747dd1d1bSDag-Erling Smørgrav 	char **child_env;
89847dd1d1bSDag-Erling Smørgrav 
89947dd1d1bSDag-Erling Smørgrav 	if (child != NULL)
90047dd1d1bSDag-Erling Smørgrav 		*child = NULL;
90147dd1d1bSDag-Erling Smørgrav 
90247dd1d1bSDag-Erling Smørgrav 	debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__,
90347dd1d1bSDag-Erling Smørgrav 	    tag, command, pw->pw_name, flags);
90447dd1d1bSDag-Erling Smørgrav 
90547dd1d1bSDag-Erling Smørgrav 	/* Check consistency */
90647dd1d1bSDag-Erling Smørgrav 	if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 &&
90747dd1d1bSDag-Erling Smørgrav 	    (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) {
90847dd1d1bSDag-Erling Smørgrav 		error("%s: inconsistent flags", __func__);
90947dd1d1bSDag-Erling Smørgrav 		return 0;
91047dd1d1bSDag-Erling Smørgrav 	}
91147dd1d1bSDag-Erling Smørgrav 	if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) {
91247dd1d1bSDag-Erling Smørgrav 		error("%s: inconsistent flags/output", __func__);
91347dd1d1bSDag-Erling Smørgrav 		return 0;
91447dd1d1bSDag-Erling Smørgrav 	}
91547dd1d1bSDag-Erling Smørgrav 
91647dd1d1bSDag-Erling Smørgrav 	/*
91747dd1d1bSDag-Erling Smørgrav 	 * If executing an explicit binary, then verify the it exists
91847dd1d1bSDag-Erling Smørgrav 	 * and appears safe-ish to execute
91947dd1d1bSDag-Erling Smørgrav 	 */
92047dd1d1bSDag-Erling Smørgrav 	if (*av[0] != '/') {
92147dd1d1bSDag-Erling Smørgrav 		error("%s path is not absolute", tag);
92247dd1d1bSDag-Erling Smørgrav 		return 0;
92347dd1d1bSDag-Erling Smørgrav 	}
92447dd1d1bSDag-Erling Smørgrav 	temporarily_use_uid(pw);
92547dd1d1bSDag-Erling Smørgrav 	if (stat(av[0], &st) < 0) {
92647dd1d1bSDag-Erling Smørgrav 		error("Could not stat %s \"%s\": %s", tag,
92747dd1d1bSDag-Erling Smørgrav 		    av[0], strerror(errno));
92847dd1d1bSDag-Erling Smørgrav 		restore_uid();
92947dd1d1bSDag-Erling Smørgrav 		return 0;
93047dd1d1bSDag-Erling Smørgrav 	}
93147dd1d1bSDag-Erling Smørgrav 	if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) {
93247dd1d1bSDag-Erling Smørgrav 		error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
93347dd1d1bSDag-Erling Smørgrav 		restore_uid();
93447dd1d1bSDag-Erling Smørgrav 		return 0;
93547dd1d1bSDag-Erling Smørgrav 	}
93647dd1d1bSDag-Erling Smørgrav 	/* Prepare to keep the child's stdout if requested */
93747dd1d1bSDag-Erling Smørgrav 	if (pipe(p) != 0) {
93847dd1d1bSDag-Erling Smørgrav 		error("%s: pipe: %s", tag, strerror(errno));
93947dd1d1bSDag-Erling Smørgrav 		restore_uid();
94047dd1d1bSDag-Erling Smørgrav 		return 0;
94147dd1d1bSDag-Erling Smørgrav 	}
94247dd1d1bSDag-Erling Smørgrav 	restore_uid();
94347dd1d1bSDag-Erling Smørgrav 
94447dd1d1bSDag-Erling Smørgrav 	switch ((pid = fork())) {
94547dd1d1bSDag-Erling Smørgrav 	case -1: /* error */
94647dd1d1bSDag-Erling Smørgrav 		error("%s: fork: %s", tag, strerror(errno));
94747dd1d1bSDag-Erling Smørgrav 		close(p[0]);
94847dd1d1bSDag-Erling Smørgrav 		close(p[1]);
94947dd1d1bSDag-Erling Smørgrav 		return 0;
95047dd1d1bSDag-Erling Smørgrav 	case 0: /* child */
95147dd1d1bSDag-Erling Smørgrav 		/* Prepare a minimal environment for the child. */
95247dd1d1bSDag-Erling Smørgrav 		envsize = 5;
95347dd1d1bSDag-Erling Smørgrav 		child_env = xcalloc(sizeof(*child_env), envsize);
95447dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
95547dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "USER", pw->pw_name);
95647dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
95747dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
95847dd1d1bSDag-Erling Smørgrav 		if ((cp = getenv("LANG")) != NULL)
95947dd1d1bSDag-Erling Smørgrav 			child_set_env(&child_env, &envsize, "LANG", cp);
96047dd1d1bSDag-Erling Smørgrav 
96147dd1d1bSDag-Erling Smørgrav 		for (i = 0; i < NSIG; i++)
96247dd1d1bSDag-Erling Smørgrav 			signal(i, SIG_DFL);
96347dd1d1bSDag-Erling Smørgrav 
96447dd1d1bSDag-Erling Smørgrav 		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
96547dd1d1bSDag-Erling Smørgrav 			error("%s: open %s: %s", tag, _PATH_DEVNULL,
96647dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
96747dd1d1bSDag-Erling Smørgrav 			_exit(1);
96847dd1d1bSDag-Erling Smørgrav 		}
96947dd1d1bSDag-Erling Smørgrav 		if (dup2(devnull, STDIN_FILENO) == -1) {
97047dd1d1bSDag-Erling Smørgrav 			error("%s: dup2: %s", tag, strerror(errno));
97147dd1d1bSDag-Erling Smørgrav 			_exit(1);
97247dd1d1bSDag-Erling Smørgrav 		}
97347dd1d1bSDag-Erling Smørgrav 
97447dd1d1bSDag-Erling Smørgrav 		/* Set up stdout as requested; leave stderr in place for now. */
97547dd1d1bSDag-Erling Smørgrav 		fd = -1;
97647dd1d1bSDag-Erling Smørgrav 		if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0)
97747dd1d1bSDag-Erling Smørgrav 			fd = p[1];
97847dd1d1bSDag-Erling Smørgrav 		else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0)
97947dd1d1bSDag-Erling Smørgrav 			fd = devnull;
98047dd1d1bSDag-Erling Smørgrav 		if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) {
98147dd1d1bSDag-Erling Smørgrav 			error("%s: dup2: %s", tag, strerror(errno));
98247dd1d1bSDag-Erling Smørgrav 			_exit(1);
98347dd1d1bSDag-Erling Smørgrav 		}
98447dd1d1bSDag-Erling Smørgrav 		closefrom(STDERR_FILENO + 1);
98547dd1d1bSDag-Erling Smørgrav 
98647dd1d1bSDag-Erling Smørgrav 		/* Don't use permanently_set_uid() here to avoid fatal() */
98747dd1d1bSDag-Erling Smørgrav 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
98847dd1d1bSDag-Erling Smørgrav 			error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
98947dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
99047dd1d1bSDag-Erling Smørgrav 			_exit(1);
99147dd1d1bSDag-Erling Smørgrav 		}
99247dd1d1bSDag-Erling Smørgrav 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
99347dd1d1bSDag-Erling Smørgrav 			error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
99447dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
99547dd1d1bSDag-Erling Smørgrav 			_exit(1);
99647dd1d1bSDag-Erling Smørgrav 		}
99747dd1d1bSDag-Erling Smørgrav 		/* stdin is pointed to /dev/null at this point */
99847dd1d1bSDag-Erling Smørgrav 		if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 &&
99947dd1d1bSDag-Erling Smørgrav 		    dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
100047dd1d1bSDag-Erling Smørgrav 			error("%s: dup2: %s", tag, strerror(errno));
100147dd1d1bSDag-Erling Smørgrav 			_exit(1);
100247dd1d1bSDag-Erling Smørgrav 		}
100347dd1d1bSDag-Erling Smørgrav 
100447dd1d1bSDag-Erling Smørgrav 		execve(av[0], av, child_env);
100547dd1d1bSDag-Erling Smørgrav 		error("%s exec \"%s\": %s", tag, command, strerror(errno));
100647dd1d1bSDag-Erling Smørgrav 		_exit(127);
100747dd1d1bSDag-Erling Smørgrav 	default: /* parent */
100847dd1d1bSDag-Erling Smørgrav 		break;
100947dd1d1bSDag-Erling Smørgrav 	}
101047dd1d1bSDag-Erling Smørgrav 
101147dd1d1bSDag-Erling Smørgrav 	close(p[1]);
101247dd1d1bSDag-Erling Smørgrav 	if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0)
101347dd1d1bSDag-Erling Smørgrav 		close(p[0]);
101447dd1d1bSDag-Erling Smørgrav 	else if ((f = fdopen(p[0], "r")) == NULL) {
101547dd1d1bSDag-Erling Smørgrav 		error("%s: fdopen: %s", tag, strerror(errno));
101647dd1d1bSDag-Erling Smørgrav 		close(p[0]);
101747dd1d1bSDag-Erling Smørgrav 		/* Don't leave zombie child */
101847dd1d1bSDag-Erling Smørgrav 		kill(pid, SIGTERM);
101947dd1d1bSDag-Erling Smørgrav 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
102047dd1d1bSDag-Erling Smørgrav 			;
102147dd1d1bSDag-Erling Smørgrav 		return 0;
102247dd1d1bSDag-Erling Smørgrav 	}
102347dd1d1bSDag-Erling Smørgrav 	/* Success */
102447dd1d1bSDag-Erling Smørgrav 	debug3("%s: %s pid %ld", __func__, tag, (long)pid);
102547dd1d1bSDag-Erling Smørgrav 	if (child != NULL)
102647dd1d1bSDag-Erling Smørgrav 		*child = f;
102747dd1d1bSDag-Erling Smørgrav 	return pid;
102847dd1d1bSDag-Erling Smørgrav }
102947dd1d1bSDag-Erling Smørgrav 
103047dd1d1bSDag-Erling Smørgrav /* These functions link key/cert options to the auth framework */
103147dd1d1bSDag-Erling Smørgrav 
103247dd1d1bSDag-Erling Smørgrav /* Log sshauthopt options locally and (optionally) for remote transmission */
103347dd1d1bSDag-Erling Smørgrav void
103447dd1d1bSDag-Erling Smørgrav auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
103547dd1d1bSDag-Erling Smørgrav {
103647dd1d1bSDag-Erling Smørgrav 	int do_env = options.permit_user_env && opts->nenv > 0;
103747dd1d1bSDag-Erling Smørgrav 	int do_permitopen = opts->npermitopen > 0 &&
103847dd1d1bSDag-Erling Smørgrav 	    (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0;
1039190cef3dSDag-Erling Smørgrav 	int do_permitlisten = opts->npermitlisten > 0 &&
1040190cef3dSDag-Erling Smørgrav 	    (options.allow_tcp_forwarding & FORWARD_REMOTE) != 0;
104147dd1d1bSDag-Erling Smørgrav 	size_t i;
104247dd1d1bSDag-Erling Smørgrav 	char msg[1024], buf[64];
104347dd1d1bSDag-Erling Smørgrav 
104447dd1d1bSDag-Erling Smørgrav 	snprintf(buf, sizeof(buf), "%d", opts->force_tun_device);
104547dd1d1bSDag-Erling Smørgrav 	/* Try to keep this alphabetically sorted */
1046190cef3dSDag-Erling Smørgrav 	snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s",
104747dd1d1bSDag-Erling Smørgrav 	    opts->permit_agent_forwarding_flag ? " agent-forwarding" : "",
104847dd1d1bSDag-Erling Smørgrav 	    opts->force_command == NULL ? "" : " command",
104947dd1d1bSDag-Erling Smørgrav 	    do_env ?  " environment" : "",
105047dd1d1bSDag-Erling Smørgrav 	    opts->valid_before == 0 ? "" : "expires",
105147dd1d1bSDag-Erling Smørgrav 	    do_permitopen ?  " permitopen" : "",
1052190cef3dSDag-Erling Smørgrav 	    do_permitlisten ?  " permitlisten" : "",
105347dd1d1bSDag-Erling Smørgrav 	    opts->permit_port_forwarding_flag ? " port-forwarding" : "",
105447dd1d1bSDag-Erling Smørgrav 	    opts->cert_principals == NULL ? "" : " principals",
105547dd1d1bSDag-Erling Smørgrav 	    opts->permit_pty_flag ? " pty" : "",
105647dd1d1bSDag-Erling Smørgrav 	    opts->force_tun_device == -1 ? "" : " tun=",
105747dd1d1bSDag-Erling Smørgrav 	    opts->force_tun_device == -1 ? "" : buf,
105847dd1d1bSDag-Erling Smørgrav 	    opts->permit_user_rc ? " user-rc" : "",
105947dd1d1bSDag-Erling Smørgrav 	    opts->permit_x11_forwarding_flag ? " x11-forwarding" : "");
106047dd1d1bSDag-Erling Smørgrav 
106147dd1d1bSDag-Erling Smørgrav 	debug("%s: %s", loc, msg);
106247dd1d1bSDag-Erling Smørgrav 	if (do_remote)
106347dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: %s", loc, msg);
106447dd1d1bSDag-Erling Smørgrav 
106547dd1d1bSDag-Erling Smørgrav 	if (options.permit_user_env) {
106647dd1d1bSDag-Erling Smørgrav 		for (i = 0; i < opts->nenv; i++) {
106747dd1d1bSDag-Erling Smørgrav 			debug("%s: environment: %s", loc, opts->env[i]);
106847dd1d1bSDag-Erling Smørgrav 			if (do_remote) {
106947dd1d1bSDag-Erling Smørgrav 				auth_debug_add("%s: environment: %s",
107047dd1d1bSDag-Erling Smørgrav 				    loc, opts->env[i]);
107147dd1d1bSDag-Erling Smørgrav 			}
107247dd1d1bSDag-Erling Smørgrav 		}
107347dd1d1bSDag-Erling Smørgrav 	}
107447dd1d1bSDag-Erling Smørgrav 
107547dd1d1bSDag-Erling Smørgrav 	/* Go into a little more details for the local logs. */
107647dd1d1bSDag-Erling Smørgrav 	if (opts->valid_before != 0) {
107747dd1d1bSDag-Erling Smørgrav 		format_absolute_time(opts->valid_before, buf, sizeof(buf));
107847dd1d1bSDag-Erling Smørgrav 		debug("%s: expires at %s", loc, buf);
107947dd1d1bSDag-Erling Smørgrav 	}
108047dd1d1bSDag-Erling Smørgrav 	if (opts->cert_principals != NULL) {
108147dd1d1bSDag-Erling Smørgrav 		debug("%s: authorized principals: \"%s\"",
108247dd1d1bSDag-Erling Smørgrav 		    loc, opts->cert_principals);
108347dd1d1bSDag-Erling Smørgrav 	}
108447dd1d1bSDag-Erling Smørgrav 	if (opts->force_command != NULL)
108547dd1d1bSDag-Erling Smørgrav 		debug("%s: forced command: \"%s\"", loc, opts->force_command);
1086190cef3dSDag-Erling Smørgrav 	if (do_permitopen) {
108747dd1d1bSDag-Erling Smørgrav 		for (i = 0; i < opts->npermitopen; i++) {
108847dd1d1bSDag-Erling Smørgrav 			debug("%s: permitted open: %s",
108947dd1d1bSDag-Erling Smørgrav 			    loc, opts->permitopen[i]);
109047dd1d1bSDag-Erling Smørgrav 		}
109147dd1d1bSDag-Erling Smørgrav 	}
1092190cef3dSDag-Erling Smørgrav 	if (do_permitlisten) {
1093190cef3dSDag-Erling Smørgrav 		for (i = 0; i < opts->npermitlisten; i++) {
1094190cef3dSDag-Erling Smørgrav 			debug("%s: permitted listen: %s",
1095190cef3dSDag-Erling Smørgrav 			    loc, opts->permitlisten[i]);
1096190cef3dSDag-Erling Smørgrav 		}
1097190cef3dSDag-Erling Smørgrav 	}
109847dd1d1bSDag-Erling Smørgrav }
109947dd1d1bSDag-Erling Smørgrav 
110047dd1d1bSDag-Erling Smørgrav /* Activate a new set of key/cert options; merging with what is there. */
110147dd1d1bSDag-Erling Smørgrav int
110247dd1d1bSDag-Erling Smørgrav auth_activate_options(struct ssh *ssh, struct sshauthopt *opts)
110347dd1d1bSDag-Erling Smørgrav {
110447dd1d1bSDag-Erling Smørgrav 	struct sshauthopt *old = auth_opts;
110547dd1d1bSDag-Erling Smørgrav 	const char *emsg = NULL;
110647dd1d1bSDag-Erling Smørgrav 
110747dd1d1bSDag-Erling Smørgrav 	debug("%s: setting new authentication options", __func__);
110847dd1d1bSDag-Erling Smørgrav 	if ((auth_opts = sshauthopt_merge(old, opts, &emsg)) == NULL) {
110947dd1d1bSDag-Erling Smørgrav 		error("Inconsistent authentication options: %s", emsg);
111047dd1d1bSDag-Erling Smørgrav 		return -1;
111147dd1d1bSDag-Erling Smørgrav 	}
111247dd1d1bSDag-Erling Smørgrav 	return 0;
111347dd1d1bSDag-Erling Smørgrav }
111447dd1d1bSDag-Erling Smørgrav 
111547dd1d1bSDag-Erling Smørgrav /* Disable forwarding, etc for the session */
111647dd1d1bSDag-Erling Smørgrav void
111747dd1d1bSDag-Erling Smørgrav auth_restrict_session(struct ssh *ssh)
111847dd1d1bSDag-Erling Smørgrav {
111947dd1d1bSDag-Erling Smørgrav 	struct sshauthopt *restricted;
112047dd1d1bSDag-Erling Smørgrav 
112147dd1d1bSDag-Erling Smørgrav 	debug("%s: restricting session", __func__);
112247dd1d1bSDag-Erling Smørgrav 
112347dd1d1bSDag-Erling Smørgrav 	/* A blank sshauthopt defaults to permitting nothing */
112447dd1d1bSDag-Erling Smørgrav 	restricted = sshauthopt_new();
1125190cef3dSDag-Erling Smørgrav 	restricted->permit_pty_flag = 1;
112647dd1d1bSDag-Erling Smørgrav 	restricted->restricted = 1;
112747dd1d1bSDag-Erling Smørgrav 
112847dd1d1bSDag-Erling Smørgrav 	if (auth_activate_options(ssh, restricted) != 0)
112947dd1d1bSDag-Erling Smørgrav 		fatal("%s: failed to restrict session", __func__);
113047dd1d1bSDag-Erling Smørgrav 	sshauthopt_free(restricted);
113147dd1d1bSDag-Erling Smørgrav }
113247dd1d1bSDag-Erling Smørgrav 
113347dd1d1bSDag-Erling Smørgrav int
113447dd1d1bSDag-Erling Smørgrav auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw,
113547dd1d1bSDag-Erling Smørgrav     struct sshauthopt *opts, int allow_cert_authority, const char *loc)
113647dd1d1bSDag-Erling Smørgrav {
113747dd1d1bSDag-Erling Smørgrav 	const char *remote_ip = ssh_remote_ipaddr(ssh);
113847dd1d1bSDag-Erling Smørgrav 	const char *remote_host = auth_get_canonical_hostname(ssh,
113947dd1d1bSDag-Erling Smørgrav 	    options.use_dns);
114047dd1d1bSDag-Erling Smørgrav 	time_t now = time(NULL);
114147dd1d1bSDag-Erling Smørgrav 	char buf[64];
114247dd1d1bSDag-Erling Smørgrav 
114347dd1d1bSDag-Erling Smørgrav 	/*
114447dd1d1bSDag-Erling Smørgrav 	 * Check keys/principals file expiry time.
114547dd1d1bSDag-Erling Smørgrav 	 * NB. validity interval in certificate is handled elsewhere.
114647dd1d1bSDag-Erling Smørgrav 	 */
114747dd1d1bSDag-Erling Smørgrav 	if (opts->valid_before && now > 0 &&
114847dd1d1bSDag-Erling Smørgrav 	    opts->valid_before < (uint64_t)now) {
114947dd1d1bSDag-Erling Smørgrav 		format_absolute_time(opts->valid_before, buf, sizeof(buf));
115047dd1d1bSDag-Erling Smørgrav 		debug("%s: entry expired at %s", loc, buf);
115147dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: entry expired at %s", loc, buf);
115247dd1d1bSDag-Erling Smørgrav 		return -1;
115347dd1d1bSDag-Erling Smørgrav 	}
115447dd1d1bSDag-Erling Smørgrav 	/* Consistency checks */
115547dd1d1bSDag-Erling Smørgrav 	if (opts->cert_principals != NULL && !opts->cert_authority) {
115647dd1d1bSDag-Erling Smørgrav 		debug("%s: principals on non-CA key", loc);
115747dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: principals on non-CA key", loc);
115847dd1d1bSDag-Erling Smørgrav 		/* deny access */
115947dd1d1bSDag-Erling Smørgrav 		return -1;
116047dd1d1bSDag-Erling Smørgrav 	}
116147dd1d1bSDag-Erling Smørgrav 	/* cert-authority flag isn't valid in authorized_principals files */
116247dd1d1bSDag-Erling Smørgrav 	if (!allow_cert_authority && opts->cert_authority) {
116347dd1d1bSDag-Erling Smørgrav 		debug("%s: cert-authority flag invalid here", loc);
116447dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: cert-authority flag invalid here", loc);
116547dd1d1bSDag-Erling Smørgrav 		/* deny access */
116647dd1d1bSDag-Erling Smørgrav 		return -1;
116747dd1d1bSDag-Erling Smørgrav 	}
116847dd1d1bSDag-Erling Smørgrav 
116947dd1d1bSDag-Erling Smørgrav 	/* Perform from= checks */
117047dd1d1bSDag-Erling Smørgrav 	if (opts->required_from_host_keys != NULL) {
117147dd1d1bSDag-Erling Smørgrav 		switch (match_host_and_ip(remote_host, remote_ip,
117247dd1d1bSDag-Erling Smørgrav 		    opts->required_from_host_keys )) {
117347dd1d1bSDag-Erling Smørgrav 		case 1:
117447dd1d1bSDag-Erling Smørgrav 			/* Host name matches. */
117547dd1d1bSDag-Erling Smørgrav 			break;
117647dd1d1bSDag-Erling Smørgrav 		case -1:
117747dd1d1bSDag-Erling Smørgrav 		default:
117847dd1d1bSDag-Erling Smørgrav 			debug("%s: invalid from criteria", loc);
117947dd1d1bSDag-Erling Smørgrav 			auth_debug_add("%s: invalid from criteria", loc);
118047dd1d1bSDag-Erling Smørgrav 			/* FALLTHROUGH */
118147dd1d1bSDag-Erling Smørgrav 		case 0:
118247dd1d1bSDag-Erling Smørgrav 			logit("%s: Authentication tried for %.100s with "
118347dd1d1bSDag-Erling Smørgrav 			    "correct key but not from a permitted "
118447dd1d1bSDag-Erling Smørgrav 			    "host (host=%.200s, ip=%.200s, required=%.200s).",
118547dd1d1bSDag-Erling Smørgrav 			    loc, pw->pw_name, remote_host, remote_ip,
118647dd1d1bSDag-Erling Smørgrav 			    opts->required_from_host_keys);
118747dd1d1bSDag-Erling Smørgrav 			auth_debug_add("%s: Your host '%.200s' is not "
118847dd1d1bSDag-Erling Smørgrav 			    "permitted to use this key for login.",
118947dd1d1bSDag-Erling Smørgrav 			    loc, remote_host);
119047dd1d1bSDag-Erling Smørgrav 			/* deny access */
119147dd1d1bSDag-Erling Smørgrav 			return -1;
119247dd1d1bSDag-Erling Smørgrav 		}
119347dd1d1bSDag-Erling Smørgrav 	}
119447dd1d1bSDag-Erling Smørgrav 	/* Check source-address restriction from certificate */
119547dd1d1bSDag-Erling Smørgrav 	if (opts->required_from_host_cert != NULL) {
119647dd1d1bSDag-Erling Smørgrav 		switch (addr_match_cidr_list(remote_ip,
119747dd1d1bSDag-Erling Smørgrav 		    opts->required_from_host_cert)) {
119847dd1d1bSDag-Erling Smørgrav 		case 1:
119947dd1d1bSDag-Erling Smørgrav 			/* accepted */
120047dd1d1bSDag-Erling Smørgrav 			break;
120147dd1d1bSDag-Erling Smørgrav 		case -1:
120247dd1d1bSDag-Erling Smørgrav 		default:
120347dd1d1bSDag-Erling Smørgrav 			/* invalid */
120447dd1d1bSDag-Erling Smørgrav 			error("%s: Certificate source-address invalid",
120547dd1d1bSDag-Erling Smørgrav 			    loc);
120647dd1d1bSDag-Erling Smørgrav 			/* FALLTHROUGH */
120747dd1d1bSDag-Erling Smørgrav 		case 0:
120847dd1d1bSDag-Erling Smørgrav 			logit("%s: Authentication tried for %.100s with valid "
120947dd1d1bSDag-Erling Smørgrav 			    "certificate but not from a permitted source "
121047dd1d1bSDag-Erling Smørgrav 			    "address (%.200s).", loc, pw->pw_name, remote_ip);
121147dd1d1bSDag-Erling Smørgrav 			auth_debug_add("%s: Your address '%.200s' is not "
121247dd1d1bSDag-Erling Smørgrav 			    "permitted to use this certificate for login.",
121347dd1d1bSDag-Erling Smørgrav 			    loc, remote_ip);
121447dd1d1bSDag-Erling Smørgrav 			return -1;
121547dd1d1bSDag-Erling Smørgrav 		}
121647dd1d1bSDag-Erling Smørgrav 	}
121747dd1d1bSDag-Erling Smørgrav 	/*
121847dd1d1bSDag-Erling Smørgrav 	 *
121947dd1d1bSDag-Erling Smørgrav 	 * XXX this is spammy. We should report remotely only for keys
122047dd1d1bSDag-Erling Smørgrav 	 *     that are successful in actual auth attempts, and not PK_OK
122147dd1d1bSDag-Erling Smørgrav 	 *     tests.
122247dd1d1bSDag-Erling Smørgrav 	 */
122347dd1d1bSDag-Erling Smørgrav 	auth_log_authopts(loc, opts, 1);
122447dd1d1bSDag-Erling Smørgrav 
122547dd1d1bSDag-Erling Smørgrav 	return 0;
122647dd1d1bSDag-Erling Smørgrav }
1227