xref: /freebsd/crypto/openssh/auth.c (revision 47dd1d1b619cc035b82b49a91a25544309ff95ae)
1*47dd1d1bSDag-Erling Smørgrav /* $OpenBSD: auth.c,v 1.127 2018/03/12 00:52:01 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>
32*47dd1d1bSDag-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"
59333ee039SDag-Erling Smørgrav #include "buffer.h"
60a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
61ca3176e7SBrian Feldman #include "servconf.h"
62333ee039SDag-Erling Smørgrav #include "key.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"
78*47dd1d1bSDag-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;
84cf2b5f3bSDag-Erling Smørgrav extern Buffer loginmsg;
85333ee039SDag-Erling Smørgrav extern struct passwd *privsep_pw;
86*47dd1d1bSDag-Erling Smørgrav extern struct sshauthopt *auth_opts;
87a04a10f8SKris Kennaway 
8880628bacSDag-Erling Smørgrav /* Debugging messages */
8980628bacSDag-Erling Smørgrav Buffer auth_debug;
9080628bacSDag-Erling Smørgrav int auth_debug_init;
9180628bacSDag-Erling Smørgrav 
92a04a10f8SKris Kennaway /*
93ca3176e7SBrian Feldman  * Check if the user is allowed to log in via ssh. If user is listed
94ca3176e7SBrian Feldman  * in DenyUsers or one of user's groups is listed in DenyGroups, false
95ca3176e7SBrian Feldman  * will be returned. If AllowUsers isn't empty and user isn't listed
96ca3176e7SBrian Feldman  * there, or if AllowGroups isn't empty and one of user's groups isn't
97ca3176e7SBrian Feldman  * listed there, false will be returned.
98a04a10f8SKris Kennaway  * If the user's shell is not executable, false will be returned.
99a04a10f8SKris Kennaway  * Otherwise true is returned.
100a04a10f8SKris Kennaway  */
101a04a10f8SKris Kennaway int
102a04a10f8SKris Kennaway allowed_user(struct passwd * pw)
103a04a10f8SKris Kennaway {
104076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
105a04a10f8SKris Kennaway 	struct stat st;
106cf2b5f3bSDag-Erling Smørgrav 	const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL;
107d4ecd108SDag-Erling Smørgrav 	u_int i;
108ca86bcf2SDag-Erling Smørgrav 	int r;
1091ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
110cf2b5f3bSDag-Erling Smørgrav 	struct spwd *spw = NULL;
111e73e9afaSDag-Erling Smørgrav #endif
112a04a10f8SKris Kennaway 
113a04a10f8SKris Kennaway 	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
114ca3176e7SBrian Feldman 	if (!pw || !pw->pw_name)
115a04a10f8SKris Kennaway 		return 0;
116a04a10f8SKris Kennaway 
1171ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
118cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam)
119cf2b5f3bSDag-Erling Smørgrav 		spw = getspnam(pw->pw_name);
120cf2b5f3bSDag-Erling Smørgrav #ifdef HAS_SHADOW_EXPIRE
1211ec0d754SDag-Erling Smørgrav 	if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw))
122989dd127SDag-Erling Smørgrav 		return 0;
123cf2b5f3bSDag-Erling Smørgrav #endif /* HAS_SHADOW_EXPIRE */
1241ec0d754SDag-Erling Smørgrav #endif /* USE_SHADOW */
125cf2b5f3bSDag-Erling Smørgrav 
126cf2b5f3bSDag-Erling Smørgrav 	/* grab passwd field for locked account check */
127d4af9e69SDag-Erling Smørgrav 	passwd = pw->pw_passwd;
1281ec0d754SDag-Erling Smørgrav #ifdef USE_SHADOW
129cf2b5f3bSDag-Erling Smørgrav 	if (spw != NULL)
130d4af9e69SDag-Erling Smørgrav #ifdef USE_LIBIAF
131d4ecd108SDag-Erling Smørgrav 		passwd = get_iaf_password(pw);
132d4ecd108SDag-Erling Smørgrav #else
133cf2b5f3bSDag-Erling Smørgrav 		passwd = spw->sp_pwdp;
134d4af9e69SDag-Erling Smørgrav #endif /* USE_LIBIAF */
135989dd127SDag-Erling Smørgrav #endif
136989dd127SDag-Erling Smørgrav 
137cf2b5f3bSDag-Erling Smørgrav 	/* check for locked account */
138cf2b5f3bSDag-Erling Smørgrav 	if (!options.use_pam && passwd && *passwd) {
139cf2b5f3bSDag-Erling Smørgrav 		int locked = 0;
140cf2b5f3bSDag-Erling Smørgrav 
141cf2b5f3bSDag-Erling Smørgrav #ifdef LOCKED_PASSWD_STRING
142cf2b5f3bSDag-Erling Smørgrav 		if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0)
143cf2b5f3bSDag-Erling Smørgrav 			 locked = 1;
144cf2b5f3bSDag-Erling Smørgrav #endif
145cf2b5f3bSDag-Erling Smørgrav #ifdef LOCKED_PASSWD_PREFIX
146cf2b5f3bSDag-Erling Smørgrav 		if (strncmp(passwd, LOCKED_PASSWD_PREFIX,
147cf2b5f3bSDag-Erling Smørgrav 		    strlen(LOCKED_PASSWD_PREFIX)) == 0)
148cf2b5f3bSDag-Erling Smørgrav 			 locked = 1;
149cf2b5f3bSDag-Erling Smørgrav #endif
150cf2b5f3bSDag-Erling Smørgrav #ifdef LOCKED_PASSWD_SUBSTR
151cf2b5f3bSDag-Erling Smørgrav 		if (strstr(passwd, LOCKED_PASSWD_SUBSTR))
152cf2b5f3bSDag-Erling Smørgrav 			locked = 1;
153cf2b5f3bSDag-Erling Smørgrav #endif
154d4af9e69SDag-Erling Smørgrav #ifdef USE_LIBIAF
155e2f6069cSDag-Erling Smørgrav 		free((void *) passwd);
156d4af9e69SDag-Erling Smørgrav #endif /* USE_LIBIAF */
157cf2b5f3bSDag-Erling Smørgrav 		if (locked) {
158cf2b5f3bSDag-Erling Smørgrav 			logit("User %.100s not allowed because account is locked",
159cf2b5f3bSDag-Erling Smørgrav 			    pw->pw_name);
160cf2b5f3bSDag-Erling Smørgrav 			return 0;
161cf2b5f3bSDag-Erling Smørgrav 		}
162cf2b5f3bSDag-Erling Smørgrav 	}
163cf2b5f3bSDag-Erling Smørgrav 
164c322fe35SKris Kennaway 	/*
165b15c8340SDag-Erling Smørgrav 	 * Deny if shell does not exist or is not executable unless we
166b15c8340SDag-Erling Smørgrav 	 * are chrooting.
167c322fe35SKris Kennaway 	 */
168b15c8340SDag-Erling Smørgrav 	if (options.chroot_directory == NULL ||
169b15c8340SDag-Erling Smørgrav 	    strcasecmp(options.chroot_directory, "none") == 0) {
170b15c8340SDag-Erling Smørgrav 		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
171b15c8340SDag-Erling Smørgrav 		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */
172c322fe35SKris Kennaway 
173af12a3e7SDag-Erling Smørgrav 		if (stat(shell, &st) != 0) {
174b15c8340SDag-Erling Smørgrav 			logit("User %.100s not allowed because shell %.100s "
175b15c8340SDag-Erling Smørgrav 			    "does not exist", pw->pw_name, shell);
176e4a9863fSDag-Erling Smørgrav 			free(shell);
177a04a10f8SKris Kennaway 			return 0;
178af12a3e7SDag-Erling Smørgrav 		}
17980628bacSDag-Erling Smørgrav 		if (S_ISREG(st.st_mode) == 0 ||
18080628bacSDag-Erling Smørgrav 		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
181b15c8340SDag-Erling Smørgrav 			logit("User %.100s not allowed because shell %.100s "
182b15c8340SDag-Erling Smørgrav 			    "is not executable", pw->pw_name, shell);
183e4a9863fSDag-Erling Smørgrav 			free(shell);
184a04a10f8SKris Kennaway 			return 0;
185af12a3e7SDag-Erling Smørgrav 		}
186e4a9863fSDag-Erling Smørgrav 		free(shell);
187b15c8340SDag-Erling Smørgrav 	}
188af12a3e7SDag-Erling Smørgrav 
189aa49c926SDag-Erling Smørgrav 	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
190aa49c926SDag-Erling Smørgrav 	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
191076ad2f8SDag-Erling Smørgrav 		hostname = auth_get_canonical_hostname(ssh, options.use_dns);
192076ad2f8SDag-Erling Smørgrav 		ipaddr = ssh_remote_ipaddr(ssh);
193af12a3e7SDag-Erling Smørgrav 	}
194a04a10f8SKris Kennaway 
195a04a10f8SKris Kennaway 	/* Return false if user is listed in DenyUsers */
196a04a10f8SKris Kennaway 	if (options.num_deny_users > 0) {
197ca86bcf2SDag-Erling Smørgrav 		for (i = 0; i < options.num_deny_users; i++) {
198ca86bcf2SDag-Erling Smørgrav 			r = match_user(pw->pw_name, hostname, ipaddr,
199ca86bcf2SDag-Erling Smørgrav 			    options.deny_users[i]);
200ca86bcf2SDag-Erling Smørgrav 			if (r < 0) {
201ca86bcf2SDag-Erling Smørgrav 				fatal("Invalid DenyUsers pattern \"%.100s\"",
202ca86bcf2SDag-Erling Smørgrav 				    options.deny_users[i]);
203ca86bcf2SDag-Erling Smørgrav 			} else if (r != 0) {
204aa49c926SDag-Erling Smørgrav 				logit("User %.100s from %.100s not allowed "
205aa49c926SDag-Erling Smørgrav 				    "because listed in DenyUsers",
206aa49c926SDag-Erling Smørgrav 				    pw->pw_name, hostname);
207a04a10f8SKris Kennaway 				return 0;
208a04a10f8SKris Kennaway 			}
209af12a3e7SDag-Erling Smørgrav 		}
210ca86bcf2SDag-Erling Smørgrav 	}
211a04a10f8SKris Kennaway 	/* Return false if AllowUsers isn't empty and user isn't listed there */
212a04a10f8SKris Kennaway 	if (options.num_allow_users > 0) {
213ca86bcf2SDag-Erling Smørgrav 		for (i = 0; i < options.num_allow_users; i++) {
214ca86bcf2SDag-Erling Smørgrav 			r = match_user(pw->pw_name, hostname, ipaddr,
215ca86bcf2SDag-Erling Smørgrav 			    options.allow_users[i]);
216ca86bcf2SDag-Erling Smørgrav 			if (r < 0) {
217ca86bcf2SDag-Erling Smørgrav 				fatal("Invalid AllowUsers pattern \"%.100s\"",
218ca86bcf2SDag-Erling Smørgrav 				    options.allow_users[i]);
219ca86bcf2SDag-Erling Smørgrav 			} else if (r == 1)
220a04a10f8SKris Kennaway 				break;
221ca86bcf2SDag-Erling Smørgrav 		}
222a04a10f8SKris Kennaway 		/* i < options.num_allow_users iff we break for loop */
223af12a3e7SDag-Erling Smørgrav 		if (i >= options.num_allow_users) {
224aa49c926SDag-Erling Smørgrav 			logit("User %.100s from %.100s not allowed because "
225aa49c926SDag-Erling Smørgrav 			    "not listed in AllowUsers", pw->pw_name, hostname);
226a04a10f8SKris Kennaway 			return 0;
227a04a10f8SKris Kennaway 		}
228af12a3e7SDag-Erling Smørgrav 	}
229a04a10f8SKris Kennaway 	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
230ca3176e7SBrian Feldman 		/* Get the user's group access list (primary and supplementary) */
231af12a3e7SDag-Erling Smørgrav 		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
232aa49c926SDag-Erling Smørgrav 			logit("User %.100s from %.100s not allowed because "
233aa49c926SDag-Erling Smørgrav 			    "not in any group", pw->pw_name, hostname);
234a04a10f8SKris Kennaway 			return 0;
235af12a3e7SDag-Erling Smørgrav 		}
236a04a10f8SKris Kennaway 
237ca3176e7SBrian Feldman 		/* Return false if one of user's groups is listed in DenyGroups */
238ca3176e7SBrian Feldman 		if (options.num_deny_groups > 0)
239ca3176e7SBrian Feldman 			if (ga_match(options.deny_groups,
240ca3176e7SBrian Feldman 			    options.num_deny_groups)) {
241ca3176e7SBrian Feldman 				ga_free();
242aa49c926SDag-Erling Smørgrav 				logit("User %.100s from %.100s not allowed "
243aa49c926SDag-Erling Smørgrav 				    "because a group is listed in DenyGroups",
244aa49c926SDag-Erling Smørgrav 				    pw->pw_name, hostname);
245a04a10f8SKris Kennaway 				return 0;
246a04a10f8SKris Kennaway 			}
247a04a10f8SKris Kennaway 		/*
248ca3176e7SBrian Feldman 		 * Return false if AllowGroups isn't empty and one of user's groups
249a04a10f8SKris Kennaway 		 * isn't listed there
250a04a10f8SKris Kennaway 		 */
251ca3176e7SBrian Feldman 		if (options.num_allow_groups > 0)
252ca3176e7SBrian Feldman 			if (!ga_match(options.allow_groups,
253ca3176e7SBrian Feldman 			    options.num_allow_groups)) {
254ca3176e7SBrian Feldman 				ga_free();
255aa49c926SDag-Erling Smørgrav 				logit("User %.100s from %.100s not allowed "
256aa49c926SDag-Erling Smørgrav 				    "because none of user's groups are listed "
257aa49c926SDag-Erling Smørgrav 				    "in AllowGroups", pw->pw_name, hostname);
258a04a10f8SKris Kennaway 				return 0;
259a04a10f8SKris Kennaway 			}
260ca3176e7SBrian Feldman 		ga_free();
261a04a10f8SKris Kennaway 	}
262989dd127SDag-Erling Smørgrav 
26321e764dfSDag-Erling Smørgrav #ifdef CUSTOM_SYS_AUTH_ALLOWED_USER
264aa49c926SDag-Erling Smørgrav 	if (!sys_auth_allowed_user(pw, &loginmsg))
265989dd127SDag-Erling Smørgrav 		return 0;
26621e764dfSDag-Erling Smørgrav #endif
267989dd127SDag-Erling Smørgrav 
268a04a10f8SKris Kennaway 	/* We found no reason not to let this user try to log on... */
269a04a10f8SKris Kennaway 	return 1;
270a04a10f8SKris Kennaway }
271ca3176e7SBrian Feldman 
2724f52dfbbSDag-Erling Smørgrav /*
2734f52dfbbSDag-Erling Smørgrav  * Formats any key left in authctxt->auth_method_key for inclusion in
2744f52dfbbSDag-Erling Smørgrav  * auth_log()'s message. Also includes authxtct->auth_method_info if present.
2754f52dfbbSDag-Erling Smørgrav  */
2764f52dfbbSDag-Erling Smørgrav static char *
2774f52dfbbSDag-Erling Smørgrav format_method_key(Authctxt *authctxt)
278e4a9863fSDag-Erling Smørgrav {
2794f52dfbbSDag-Erling Smørgrav 	const struct sshkey *key = authctxt->auth_method_key;
2804f52dfbbSDag-Erling Smørgrav 	const char *methinfo = authctxt->auth_method_info;
2814f52dfbbSDag-Erling Smørgrav 	char *fp, *ret = NULL;
282e4a9863fSDag-Erling Smørgrav 
2834f52dfbbSDag-Erling Smørgrav 	if (key == NULL)
2844f52dfbbSDag-Erling Smørgrav 		return NULL;
285e4a9863fSDag-Erling Smørgrav 
2864f52dfbbSDag-Erling Smørgrav 	if (key_is_cert(key)) {
2874f52dfbbSDag-Erling Smørgrav 		fp = sshkey_fingerprint(key->cert->signature_key,
2884f52dfbbSDag-Erling Smørgrav 		    options.fingerprint_hash, SSH_FP_DEFAULT);
2894f52dfbbSDag-Erling Smørgrav 		xasprintf(&ret, "%s ID %s (serial %llu) CA %s %s%s%s",
2904f52dfbbSDag-Erling Smørgrav 		    sshkey_type(key), key->cert->key_id,
2914f52dfbbSDag-Erling Smørgrav 		    (unsigned long long)key->cert->serial,
2924f52dfbbSDag-Erling Smørgrav 		    sshkey_type(key->cert->signature_key),
2934f52dfbbSDag-Erling Smørgrav 		    fp == NULL ? "(null)" : fp,
2944f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : ", ",
2954f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : methinfo);
2964f52dfbbSDag-Erling Smørgrav 		free(fp);
2974f52dfbbSDag-Erling Smørgrav 	} else {
2984f52dfbbSDag-Erling Smørgrav 		fp = sshkey_fingerprint(key, options.fingerprint_hash,
2994f52dfbbSDag-Erling Smørgrav 		    SSH_FP_DEFAULT);
3004f52dfbbSDag-Erling Smørgrav 		xasprintf(&ret, "%s %s%s%s", sshkey_type(key),
3014f52dfbbSDag-Erling Smørgrav 		    fp == NULL ? "(null)" : fp,
3024f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : ", ",
3034f52dfbbSDag-Erling Smørgrav 		    methinfo == NULL ? "" : methinfo);
3044f52dfbbSDag-Erling Smørgrav 		free(fp);
3054f52dfbbSDag-Erling Smørgrav 	}
3064f52dfbbSDag-Erling Smørgrav 	return ret;
307e4a9863fSDag-Erling Smørgrav }
308e4a9863fSDag-Erling Smørgrav 
309e4a9863fSDag-Erling Smørgrav void
3106888a9beSDag-Erling Smørgrav auth_log(Authctxt *authctxt, int authenticated, int partial,
311e4a9863fSDag-Erling Smørgrav     const char *method, const char *submethod)
312ca3176e7SBrian Feldman {
313076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
314ca3176e7SBrian Feldman 	void (*authlog) (const char *fmt,...) = verbose;
3154f52dfbbSDag-Erling Smørgrav 	const char *authmsg;
3164f52dfbbSDag-Erling Smørgrav 	char *extra = NULL;
317ca3176e7SBrian Feldman 
318333ee039SDag-Erling Smørgrav 	if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
319333ee039SDag-Erling Smørgrav 		return;
320333ee039SDag-Erling Smørgrav 
321ca3176e7SBrian Feldman 	/* Raise logging level */
322ca3176e7SBrian Feldman 	if (authenticated == 1 ||
323ca3176e7SBrian Feldman 	    !authctxt->valid ||
32421e764dfSDag-Erling Smørgrav 	    authctxt->failures >= options.max_authtries / 2 ||
325ca3176e7SBrian Feldman 	    strcmp(method, "password") == 0)
326cf2b5f3bSDag-Erling Smørgrav 		authlog = logit;
327ca3176e7SBrian Feldman 
328ca3176e7SBrian Feldman 	if (authctxt->postponed)
329ca3176e7SBrian Feldman 		authmsg = "Postponed";
3306888a9beSDag-Erling Smørgrav 	else if (partial)
3316888a9beSDag-Erling Smørgrav 		authmsg = "Partial";
332b2af61ecSKurt Lidl 	else {
333ca3176e7SBrian Feldman 		authmsg = authenticated ? "Accepted" : "Failed";
3345057f656SKurt Lidl 		if (authenticated)
335342b8b88SKurt Lidl 			BLACKLIST_NOTIFY(BLACKLIST_AUTH_OK, "ssh");
336b2af61ecSKurt Lidl 	}
337ca3176e7SBrian Feldman 
3384f52dfbbSDag-Erling Smørgrav 	if ((extra = format_method_key(authctxt)) == NULL) {
3394f52dfbbSDag-Erling Smørgrav 		if (authctxt->auth_method_info != NULL)
3404f52dfbbSDag-Erling Smørgrav 			extra = xstrdup(authctxt->auth_method_info);
3414f52dfbbSDag-Erling Smørgrav 	}
3424f52dfbbSDag-Erling Smørgrav 
343ca86bcf2SDag-Erling Smørgrav 	authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s",
344ca3176e7SBrian Feldman 	    authmsg,
345ca3176e7SBrian Feldman 	    method,
3466888a9beSDag-Erling Smørgrav 	    submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod,
34721e764dfSDag-Erling Smørgrav 	    authctxt->valid ? "" : "invalid user ",
348af12a3e7SDag-Erling Smørgrav 	    authctxt->user,
349076ad2f8SDag-Erling Smørgrav 	    ssh_remote_ipaddr(ssh),
350076ad2f8SDag-Erling Smørgrav 	    ssh_remote_port(ssh),
3514f52dfbbSDag-Erling Smørgrav 	    extra != NULL ? ": " : "",
3524f52dfbbSDag-Erling Smørgrav 	    extra != NULL ? extra : "");
3534f52dfbbSDag-Erling Smørgrav 
3544f52dfbbSDag-Erling Smørgrav 	free(extra);
355f388f5efSDag-Erling Smørgrav 
356cf2b5f3bSDag-Erling Smørgrav #ifdef CUSTOM_FAILED_LOGIN
357aa49c926SDag-Erling Smørgrav 	if (authenticated == 0 && !authctxt->postponed &&
358aa49c926SDag-Erling Smørgrav 	    (strcmp(method, "password") == 0 ||
359aa49c926SDag-Erling Smørgrav 	    strncmp(method, "keyboard-interactive", 20) == 0 ||
360aa49c926SDag-Erling Smørgrav 	    strcmp(method, "challenge-response") == 0))
361aa49c926SDag-Erling Smørgrav 		record_failed_login(authctxt->user,
362076ad2f8SDag-Erling Smørgrav 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh");
363333ee039SDag-Erling Smørgrav # ifdef WITH_AIXAUTHENTICATE
364333ee039SDag-Erling Smørgrav 	if (authenticated)
365333ee039SDag-Erling Smørgrav 		sys_auth_record_login(authctxt->user,
366076ad2f8SDag-Erling Smørgrav 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh",
367076ad2f8SDag-Erling Smørgrav 		    &loginmsg);
368333ee039SDag-Erling Smørgrav # endif
369aa49c926SDag-Erling Smørgrav #endif
370aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS
371333ee039SDag-Erling Smørgrav 	if (authenticated == 0 && !authctxt->postponed)
372333ee039SDag-Erling Smørgrav 		audit_event(audit_classify_auth(method));
373cf2b5f3bSDag-Erling Smørgrav #endif
374ca3176e7SBrian Feldman }
375ca3176e7SBrian Feldman 
376a0ee8cc6SDag-Erling Smørgrav 
377a0ee8cc6SDag-Erling Smørgrav void
378a0ee8cc6SDag-Erling Smørgrav auth_maxtries_exceeded(Authctxt *authctxt)
379a0ee8cc6SDag-Erling Smørgrav {
380076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
381076ad2f8SDag-Erling Smørgrav 
382bc5531deSDag-Erling Smørgrav 	error("maximum authentication attempts exceeded for "
383ca86bcf2SDag-Erling Smørgrav 	    "%s%.100s from %.200s port %d ssh2",
384a0ee8cc6SDag-Erling Smørgrav 	    authctxt->valid ? "" : "invalid user ",
385a0ee8cc6SDag-Erling Smørgrav 	    authctxt->user,
386076ad2f8SDag-Erling Smørgrav 	    ssh_remote_ipaddr(ssh),
387ca86bcf2SDag-Erling Smørgrav 	    ssh_remote_port(ssh));
388bc5531deSDag-Erling Smørgrav 	packet_disconnect("Too many authentication failures");
389a0ee8cc6SDag-Erling Smørgrav 	/* NOTREACHED */
390a0ee8cc6SDag-Erling Smørgrav }
391a0ee8cc6SDag-Erling Smørgrav 
392ca3176e7SBrian Feldman /*
393ca3176e7SBrian Feldman  * Check whether root logins are disallowed.
394ca3176e7SBrian Feldman  */
395ca3176e7SBrian Feldman int
396*47dd1d1bSDag-Erling Smørgrav auth_root_allowed(struct ssh *ssh, const char *method)
397ca3176e7SBrian Feldman {
398ca3176e7SBrian Feldman 	switch (options.permit_root_login) {
399ca3176e7SBrian Feldman 	case PERMIT_YES:
400ca3176e7SBrian Feldman 		return 1;
401ca3176e7SBrian Feldman 	case PERMIT_NO_PASSWD:
402eccfee6eSDag-Erling Smørgrav 		if (strcmp(method, "publickey") == 0 ||
403eccfee6eSDag-Erling Smørgrav 		    strcmp(method, "hostbased") == 0 ||
404fc1ba28aSDag-Erling Smørgrav 		    strcmp(method, "gssapi-with-mic") == 0)
405ca3176e7SBrian Feldman 			return 1;
406ca3176e7SBrian Feldman 		break;
407ca3176e7SBrian Feldman 	case PERMIT_FORCED_ONLY:
408*47dd1d1bSDag-Erling Smørgrav 		if (auth_opts->force_command != NULL) {
409cf2b5f3bSDag-Erling Smørgrav 			logit("Root login accepted for forced command.");
410ca3176e7SBrian Feldman 			return 1;
411ca3176e7SBrian Feldman 		}
412ca3176e7SBrian Feldman 		break;
413ca3176e7SBrian Feldman 	}
414076ad2f8SDag-Erling Smørgrav 	logit("ROOT LOGIN REFUSED FROM %.200s port %d",
415076ad2f8SDag-Erling Smørgrav 	    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
416ca3176e7SBrian Feldman 	return 0;
417ca3176e7SBrian Feldman }
418af12a3e7SDag-Erling Smørgrav 
419af12a3e7SDag-Erling Smørgrav 
420af12a3e7SDag-Erling Smørgrav /*
421af12a3e7SDag-Erling Smørgrav  * Given a template and a passwd structure, build a filename
422af12a3e7SDag-Erling Smørgrav  * by substituting % tokenised options. Currently, %% becomes '%',
423af12a3e7SDag-Erling Smørgrav  * %h becomes the home directory and %u the username.
424af12a3e7SDag-Erling Smørgrav  *
425af12a3e7SDag-Erling Smørgrav  * This returns a buffer allocated by xmalloc.
426af12a3e7SDag-Erling Smørgrav  */
427e146993eSDag-Erling Smørgrav char *
428d4ecd108SDag-Erling Smørgrav expand_authorized_keys(const char *filename, struct passwd *pw)
429af12a3e7SDag-Erling Smørgrav {
430bc5531deSDag-Erling Smørgrav 	char *file, ret[PATH_MAX];
431333ee039SDag-Erling Smørgrav 	int i;
432af12a3e7SDag-Erling Smørgrav 
433d4ecd108SDag-Erling Smørgrav 	file = percent_expand(filename, "h", pw->pw_dir,
434d4ecd108SDag-Erling Smørgrav 	    "u", pw->pw_name, (char *)NULL);
435af12a3e7SDag-Erling Smørgrav 
436af12a3e7SDag-Erling Smørgrav 	/*
437af12a3e7SDag-Erling Smørgrav 	 * Ensure that filename starts anchored. If not, be backward
438af12a3e7SDag-Erling Smørgrav 	 * compatible and prepend the '%h/'
439af12a3e7SDag-Erling Smørgrav 	 */
440d4ecd108SDag-Erling Smørgrav 	if (*file == '/')
441d4ecd108SDag-Erling Smørgrav 		return (file);
442af12a3e7SDag-Erling Smørgrav 
443333ee039SDag-Erling Smørgrav 	i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file);
444333ee039SDag-Erling Smørgrav 	if (i < 0 || (size_t)i >= sizeof(ret))
445d4ecd108SDag-Erling Smørgrav 		fatal("expand_authorized_keys: path too long");
446e4a9863fSDag-Erling Smørgrav 	free(file);
447333ee039SDag-Erling Smørgrav 	return (xstrdup(ret));
448af12a3e7SDag-Erling Smørgrav }
449af12a3e7SDag-Erling Smørgrav 
450af12a3e7SDag-Erling Smørgrav char *
451e2f6069cSDag-Erling Smørgrav authorized_principals_file(struct passwd *pw)
452e2f6069cSDag-Erling Smørgrav {
453557f75e5SDag-Erling Smørgrav 	if (options.authorized_principals_file == NULL)
454e2f6069cSDag-Erling Smørgrav 		return NULL;
455e2f6069cSDag-Erling Smørgrav 	return expand_authorized_keys(options.authorized_principals_file, pw);
456e2f6069cSDag-Erling Smørgrav }
457e2f6069cSDag-Erling Smørgrav 
458af12a3e7SDag-Erling Smørgrav /* return ok if key exists in sysfile or userfile */
459af12a3e7SDag-Erling Smørgrav HostStatus
4604f52dfbbSDag-Erling Smørgrav check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host,
461af12a3e7SDag-Erling Smørgrav     const char *sysfile, const char *userfile)
462af12a3e7SDag-Erling Smørgrav {
463af12a3e7SDag-Erling Smørgrav 	char *user_hostfile;
464af12a3e7SDag-Erling Smørgrav 	struct stat st;
465af12a3e7SDag-Erling Smørgrav 	HostStatus host_status;
4664a421b63SDag-Erling Smørgrav 	struct hostkeys *hostkeys;
4674a421b63SDag-Erling Smørgrav 	const struct hostkey_entry *found;
468af12a3e7SDag-Erling Smørgrav 
4694a421b63SDag-Erling Smørgrav 	hostkeys = init_hostkeys();
4704a421b63SDag-Erling Smørgrav 	load_hostkeys(hostkeys, host, sysfile);
4714a421b63SDag-Erling Smørgrav 	if (userfile != NULL) {
472af12a3e7SDag-Erling Smørgrav 		user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
473af12a3e7SDag-Erling Smørgrav 		if (options.strict_modes &&
474af12a3e7SDag-Erling Smørgrav 		    (stat(user_hostfile, &st) == 0) &&
475af12a3e7SDag-Erling Smørgrav 		    ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
476af12a3e7SDag-Erling Smørgrav 		    (st.st_mode & 022) != 0)) {
477cf2b5f3bSDag-Erling Smørgrav 			logit("Authentication refused for %.100s: "
478af12a3e7SDag-Erling Smørgrav 			    "bad owner or modes for %.200s",
479af12a3e7SDag-Erling Smørgrav 			    pw->pw_name, user_hostfile);
480e2f6069cSDag-Erling Smørgrav 			auth_debug_add("Ignored %.200s: bad ownership or modes",
481e2f6069cSDag-Erling Smørgrav 			    user_hostfile);
482af12a3e7SDag-Erling Smørgrav 		} else {
483af12a3e7SDag-Erling Smørgrav 			temporarily_use_uid(pw);
4844a421b63SDag-Erling Smørgrav 			load_hostkeys(hostkeys, host, user_hostfile);
485af12a3e7SDag-Erling Smørgrav 			restore_uid();
486af12a3e7SDag-Erling Smørgrav 		}
487e4a9863fSDag-Erling Smørgrav 		free(user_hostfile);
488af12a3e7SDag-Erling Smørgrav 	}
4894a421b63SDag-Erling Smørgrav 	host_status = check_key_in_hostkeys(hostkeys, key, &found);
4904a421b63SDag-Erling Smørgrav 	if (host_status == HOST_REVOKED)
4914a421b63SDag-Erling Smørgrav 		error("WARNING: revoked key for %s attempted authentication",
4924a421b63SDag-Erling Smørgrav 		    found->host);
4934a421b63SDag-Erling Smørgrav 	else if (host_status == HOST_OK)
4944a421b63SDag-Erling Smørgrav 		debug("%s: key for %s found at %s:%ld", __func__,
4954a421b63SDag-Erling Smørgrav 		    found->host, found->file, found->line);
4964a421b63SDag-Erling Smørgrav 	else
4974a421b63SDag-Erling Smørgrav 		debug("%s: key for host %s not found", __func__, host);
498af12a3e7SDag-Erling Smørgrav 
4994a421b63SDag-Erling Smørgrav 	free_hostkeys(hostkeys);
5004a421b63SDag-Erling Smørgrav 
501af12a3e7SDag-Erling Smørgrav 	return host_status;
502af12a3e7SDag-Erling Smørgrav }
503af12a3e7SDag-Erling Smørgrav 
504e2f6069cSDag-Erling Smørgrav static FILE *
505e2f6069cSDag-Erling Smørgrav auth_openfile(const char *file, struct passwd *pw, int strict_modes,
506e2f6069cSDag-Erling Smørgrav     int log_missing, char *file_type)
507d4af9e69SDag-Erling Smørgrav {
508d4af9e69SDag-Erling Smørgrav 	char line[1024];
509d4af9e69SDag-Erling Smørgrav 	struct stat st;
510d4af9e69SDag-Erling Smørgrav 	int fd;
511d4af9e69SDag-Erling Smørgrav 	FILE *f;
512d4af9e69SDag-Erling Smørgrav 
513b15c8340SDag-Erling Smørgrav 	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
514e2f6069cSDag-Erling Smørgrav 		if (log_missing || errno != ENOENT)
515e2f6069cSDag-Erling Smørgrav 			debug("Could not open %s '%s': %s", file_type, file,
516b15c8340SDag-Erling Smørgrav 			   strerror(errno));
517d4af9e69SDag-Erling Smørgrav 		return NULL;
518b15c8340SDag-Erling Smørgrav 	}
519d4af9e69SDag-Erling Smørgrav 
520d4af9e69SDag-Erling Smørgrav 	if (fstat(fd, &st) < 0) {
521d4af9e69SDag-Erling Smørgrav 		close(fd);
522d4af9e69SDag-Erling Smørgrav 		return NULL;
523d4af9e69SDag-Erling Smørgrav 	}
524d4af9e69SDag-Erling Smørgrav 	if (!S_ISREG(st.st_mode)) {
525e2f6069cSDag-Erling Smørgrav 		logit("User %s %s %s is not a regular file",
526e2f6069cSDag-Erling Smørgrav 		    pw->pw_name, file_type, file);
527d4af9e69SDag-Erling Smørgrav 		close(fd);
528d4af9e69SDag-Erling Smørgrav 		return NULL;
529d4af9e69SDag-Erling Smørgrav 	}
530d4af9e69SDag-Erling Smørgrav 	unset_nonblock(fd);
531d4af9e69SDag-Erling Smørgrav 	if ((f = fdopen(fd, "r")) == NULL) {
532d4af9e69SDag-Erling Smørgrav 		close(fd);
533d4af9e69SDag-Erling Smørgrav 		return NULL;
534d4af9e69SDag-Erling Smørgrav 	}
5354a421b63SDag-Erling Smørgrav 	if (strict_modes &&
5364f52dfbbSDag-Erling Smørgrav 	    safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) {
537d4af9e69SDag-Erling Smørgrav 		fclose(f);
538d4af9e69SDag-Erling Smørgrav 		logit("Authentication refused: %s", line);
539e2f6069cSDag-Erling Smørgrav 		auth_debug_add("Ignored %s: %s", file_type, line);
540d4af9e69SDag-Erling Smørgrav 		return NULL;
541d4af9e69SDag-Erling Smørgrav 	}
542d4af9e69SDag-Erling Smørgrav 
543d4af9e69SDag-Erling Smørgrav 	return f;
544d4af9e69SDag-Erling Smørgrav }
545d4af9e69SDag-Erling Smørgrav 
546e2f6069cSDag-Erling Smørgrav 
547e2f6069cSDag-Erling Smørgrav FILE *
548e2f6069cSDag-Erling Smørgrav auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
549e2f6069cSDag-Erling Smørgrav {
550e2f6069cSDag-Erling Smørgrav 	return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
551e2f6069cSDag-Erling Smørgrav }
552e2f6069cSDag-Erling Smørgrav 
553e2f6069cSDag-Erling Smørgrav FILE *
554e2f6069cSDag-Erling Smørgrav auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
555e2f6069cSDag-Erling Smørgrav {
556e2f6069cSDag-Erling Smørgrav 	return auth_openfile(file, pw, strict_modes, 0,
557e2f6069cSDag-Erling Smørgrav 	    "authorized principals");
558e2f6069cSDag-Erling Smørgrav }
559e2f6069cSDag-Erling Smørgrav 
56080628bacSDag-Erling Smørgrav struct passwd *
56180628bacSDag-Erling Smørgrav getpwnamallow(const char *user)
56280628bacSDag-Erling Smørgrav {
563076ad2f8SDag-Erling Smørgrav 	struct ssh *ssh = active_state; /* XXX */
56480628bacSDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
56580628bacSDag-Erling Smørgrav 	extern login_cap_t *lc;
56680628bacSDag-Erling Smørgrav #ifdef BSD_AUTH
56780628bacSDag-Erling Smørgrav 	auth_session_t *as;
56880628bacSDag-Erling Smørgrav #endif
56980628bacSDag-Erling Smørgrav #endif
57080628bacSDag-Erling Smørgrav 	struct passwd *pw;
571462c32cbSDag-Erling Smørgrav 	struct connection_info *ci = get_connection_info(1, options.use_dns);
57280628bacSDag-Erling Smørgrav 
573462c32cbSDag-Erling Smørgrav 	ci->user = user;
574462c32cbSDag-Erling Smørgrav 	parse_server_match_config(&options, ci);
5754f52dfbbSDag-Erling Smørgrav 	log_change_level(options.log_level);
5764f52dfbbSDag-Erling Smørgrav 	process_permitopen(ssh, &options);
577333ee039SDag-Erling Smørgrav 
578b15c8340SDag-Erling Smørgrav #if defined(_AIX) && defined(HAVE_SETAUTHDB)
579b15c8340SDag-Erling Smørgrav 	aix_setauthdb(user);
580b15c8340SDag-Erling Smørgrav #endif
581b15c8340SDag-Erling Smørgrav 
58280628bacSDag-Erling Smørgrav 	pw = getpwnam(user);
583b15c8340SDag-Erling Smørgrav 
584b15c8340SDag-Erling Smørgrav #if defined(_AIX) && defined(HAVE_SETAUTHDB)
585b15c8340SDag-Erling Smørgrav 	aix_restoreauthdb();
586b15c8340SDag-Erling Smørgrav #endif
587b15c8340SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
588b15c8340SDag-Erling Smørgrav 	/*
589b15c8340SDag-Erling Smørgrav 	 * Windows usernames are case-insensitive.  To avoid later problems
590b15c8340SDag-Erling Smørgrav 	 * when trying to match the username, the user is only allowed to
591b15c8340SDag-Erling Smørgrav 	 * login if the username is given in the same case as stored in the
592b15c8340SDag-Erling Smørgrav 	 * user database.
593b15c8340SDag-Erling Smørgrav 	 */
594b15c8340SDag-Erling Smørgrav 	if (pw != NULL && strcmp(user, pw->pw_name) != 0) {
595b15c8340SDag-Erling Smørgrav 		logit("Login name %.100s does not match stored username %.100s",
596b15c8340SDag-Erling Smørgrav 		    user, pw->pw_name);
597b15c8340SDag-Erling Smørgrav 		pw = NULL;
598b15c8340SDag-Erling Smørgrav 	}
599b15c8340SDag-Erling Smørgrav #endif
600f388f5efSDag-Erling Smørgrav 	if (pw == NULL) {
601342b8b88SKurt Lidl 		BLACKLIST_NOTIFY(BLACKLIST_BAD_USER, user);
602076ad2f8SDag-Erling Smørgrav 		logit("Invalid user %.100s from %.100s port %d",
603076ad2f8SDag-Erling Smørgrav 		    user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
604cf2b5f3bSDag-Erling Smørgrav #ifdef CUSTOM_FAILED_LOGIN
605aa49c926SDag-Erling Smørgrav 		record_failed_login(user,
606076ad2f8SDag-Erling Smørgrav 		    auth_get_canonical_hostname(ssh, options.use_dns), "ssh");
607e73e9afaSDag-Erling Smørgrav #endif
608aa49c926SDag-Erling Smørgrav #ifdef SSH_AUDIT_EVENTS
609aa49c926SDag-Erling Smørgrav 		audit_event(SSH_INVALID_USER);
610aa49c926SDag-Erling Smørgrav #endif /* SSH_AUDIT_EVENTS */
611f388f5efSDag-Erling Smørgrav 		return (NULL);
612f388f5efSDag-Erling Smørgrav 	}
613f388f5efSDag-Erling Smørgrav 	if (!allowed_user(pw))
61480628bacSDag-Erling Smørgrav 		return (NULL);
61580628bacSDag-Erling Smørgrav #ifdef HAVE_LOGIN_CAP
616f38aa77fSTony Finch 	if ((lc = login_getpwclass(pw)) == NULL) {
61780628bacSDag-Erling Smørgrav 		debug("unable to get login class: %s", user);
61880628bacSDag-Erling Smørgrav 		return (NULL);
61980628bacSDag-Erling Smørgrav 	}
62080628bacSDag-Erling Smørgrav #ifdef BSD_AUTH
62180628bacSDag-Erling Smørgrav 	if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
62280628bacSDag-Erling Smørgrav 	    auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
62380628bacSDag-Erling Smørgrav 		debug("Approval failure for %s", user);
62480628bacSDag-Erling Smørgrav 		pw = NULL;
62580628bacSDag-Erling Smørgrav 	}
62680628bacSDag-Erling Smørgrav 	if (as != NULL)
62780628bacSDag-Erling Smørgrav 		auth_close(as);
62880628bacSDag-Erling Smørgrav #endif
62980628bacSDag-Erling Smørgrav #endif
63080628bacSDag-Erling Smørgrav 	if (pw != NULL)
63180628bacSDag-Erling Smørgrav 		return (pwcopy(pw));
63280628bacSDag-Erling Smørgrav 	return (NULL);
63380628bacSDag-Erling Smørgrav }
63480628bacSDag-Erling Smørgrav 
635b15c8340SDag-Erling Smørgrav /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */
636b15c8340SDag-Erling Smørgrav int
6374f52dfbbSDag-Erling Smørgrav auth_key_is_revoked(struct sshkey *key)
638b15c8340SDag-Erling Smørgrav {
639bc5531deSDag-Erling Smørgrav 	char *fp = NULL;
640bc5531deSDag-Erling Smørgrav 	int r;
641b15c8340SDag-Erling Smørgrav 
642b15c8340SDag-Erling Smørgrav 	if (options.revoked_keys_file == NULL)
643b15c8340SDag-Erling Smørgrav 		return 0;
644bc5531deSDag-Erling Smørgrav 	if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
645bc5531deSDag-Erling Smørgrav 	    SSH_FP_DEFAULT)) == NULL) {
646bc5531deSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
647bc5531deSDag-Erling Smørgrav 		error("%s: fingerprint key: %s", __func__, ssh_err(r));
648bc5531deSDag-Erling Smørgrav 		goto out;
649bc5531deSDag-Erling Smørgrav 	}
650bc5531deSDag-Erling Smørgrav 
651bc5531deSDag-Erling Smørgrav 	r = sshkey_check_revoked(key, options.revoked_keys_file);
652bc5531deSDag-Erling Smørgrav 	switch (r) {
6536888a9beSDag-Erling Smørgrav 	case 0:
654bc5531deSDag-Erling Smørgrav 		break; /* not revoked */
655bc5531deSDag-Erling Smørgrav 	case SSH_ERR_KEY_REVOKED:
656bc5531deSDag-Erling Smørgrav 		error("Authentication key %s %s revoked by file %s",
657bc5531deSDag-Erling Smørgrav 		    sshkey_type(key), fp, options.revoked_keys_file);
658bc5531deSDag-Erling Smørgrav 		goto out;
6596888a9beSDag-Erling Smørgrav 	default:
660bc5531deSDag-Erling Smørgrav 		error("Error checking authentication key %s %s in "
661bc5531deSDag-Erling Smørgrav 		    "revoked keys file %s: %s", sshkey_type(key), fp,
662bc5531deSDag-Erling Smørgrav 		    options.revoked_keys_file, ssh_err(r));
663bc5531deSDag-Erling Smørgrav 		goto out;
6646888a9beSDag-Erling Smørgrav 	}
665bc5531deSDag-Erling Smørgrav 
666bc5531deSDag-Erling Smørgrav 	/* Success */
667bc5531deSDag-Erling Smørgrav 	r = 0;
668bc5531deSDag-Erling Smørgrav 
669bc5531deSDag-Erling Smørgrav  out:
670bc5531deSDag-Erling Smørgrav 	free(fp);
671bc5531deSDag-Erling Smørgrav 	return r == 0 ? 0 : 1;
672b15c8340SDag-Erling Smørgrav }
673b15c8340SDag-Erling Smørgrav 
67480628bacSDag-Erling Smørgrav void
67580628bacSDag-Erling Smørgrav auth_debug_add(const char *fmt,...)
67680628bacSDag-Erling Smørgrav {
67780628bacSDag-Erling Smørgrav 	char buf[1024];
67880628bacSDag-Erling Smørgrav 	va_list args;
67980628bacSDag-Erling Smørgrav 
68080628bacSDag-Erling Smørgrav 	if (!auth_debug_init)
68180628bacSDag-Erling Smørgrav 		return;
68280628bacSDag-Erling Smørgrav 
68380628bacSDag-Erling Smørgrav 	va_start(args, fmt);
68480628bacSDag-Erling Smørgrav 	vsnprintf(buf, sizeof(buf), fmt, args);
68580628bacSDag-Erling Smørgrav 	va_end(args);
68680628bacSDag-Erling Smørgrav 	buffer_put_cstring(&auth_debug, buf);
68780628bacSDag-Erling Smørgrav }
68880628bacSDag-Erling Smørgrav 
68980628bacSDag-Erling Smørgrav void
69080628bacSDag-Erling Smørgrav auth_debug_send(void)
69180628bacSDag-Erling Smørgrav {
69280628bacSDag-Erling Smørgrav 	char *msg;
69380628bacSDag-Erling Smørgrav 
69480628bacSDag-Erling Smørgrav 	if (!auth_debug_init)
69580628bacSDag-Erling Smørgrav 		return;
69680628bacSDag-Erling Smørgrav 	while (buffer_len(&auth_debug)) {
69780628bacSDag-Erling Smørgrav 		msg = buffer_get_string(&auth_debug, NULL);
69880628bacSDag-Erling Smørgrav 		packet_send_debug("%s", msg);
699e4a9863fSDag-Erling Smørgrav 		free(msg);
70080628bacSDag-Erling Smørgrav 	}
70180628bacSDag-Erling Smørgrav }
70280628bacSDag-Erling Smørgrav 
70380628bacSDag-Erling Smørgrav void
70480628bacSDag-Erling Smørgrav auth_debug_reset(void)
70580628bacSDag-Erling Smørgrav {
70680628bacSDag-Erling Smørgrav 	if (auth_debug_init)
70780628bacSDag-Erling Smørgrav 		buffer_clear(&auth_debug);
70880628bacSDag-Erling Smørgrav 	else {
70980628bacSDag-Erling Smørgrav 		buffer_init(&auth_debug);
71080628bacSDag-Erling Smørgrav 		auth_debug_init = 1;
71180628bacSDag-Erling Smørgrav 	}
71280628bacSDag-Erling Smørgrav }
713cf2b5f3bSDag-Erling Smørgrav 
714cf2b5f3bSDag-Erling Smørgrav struct passwd *
715cf2b5f3bSDag-Erling Smørgrav fakepw(void)
716cf2b5f3bSDag-Erling Smørgrav {
717cf2b5f3bSDag-Erling Smørgrav 	static struct passwd fake;
718cf2b5f3bSDag-Erling Smørgrav 
719cf2b5f3bSDag-Erling Smørgrav 	memset(&fake, 0, sizeof(fake));
720cf2b5f3bSDag-Erling Smørgrav 	fake.pw_name = "NOUSER";
721cf2b5f3bSDag-Erling Smørgrav 	fake.pw_passwd =
722cf2b5f3bSDag-Erling Smørgrav 	    "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK";
723e4a9863fSDag-Erling Smørgrav #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
724cf2b5f3bSDag-Erling Smørgrav 	fake.pw_gecos = "NOUSER";
725e4a9863fSDag-Erling Smørgrav #endif
726d4af9e69SDag-Erling Smørgrav 	fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid;
727d4af9e69SDag-Erling Smørgrav 	fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid;
728e4a9863fSDag-Erling Smørgrav #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
729cf2b5f3bSDag-Erling Smørgrav 	fake.pw_class = "";
730cf2b5f3bSDag-Erling Smørgrav #endif
731cf2b5f3bSDag-Erling Smørgrav 	fake.pw_dir = "/nonexist";
732cf2b5f3bSDag-Erling Smørgrav 	fake.pw_shell = "/nonexist";
733cf2b5f3bSDag-Erling Smørgrav 
734cf2b5f3bSDag-Erling Smørgrav 	return (&fake);
735cf2b5f3bSDag-Erling Smørgrav }
736076ad2f8SDag-Erling Smørgrav 
737076ad2f8SDag-Erling Smørgrav /*
738076ad2f8SDag-Erling Smørgrav  * Returns the remote DNS hostname as a string. The returned string must not
739076ad2f8SDag-Erling Smørgrav  * be freed. NB. this will usually trigger a DNS query the first time it is
740076ad2f8SDag-Erling Smørgrav  * called.
741076ad2f8SDag-Erling Smørgrav  * This function does additional checks on the hostname to mitigate some
742076ad2f8SDag-Erling Smørgrav  * attacks on legacy rhosts-style authentication.
743076ad2f8SDag-Erling Smørgrav  * XXX is RhostsRSAAuthentication vulnerable to these?
744076ad2f8SDag-Erling Smørgrav  * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
745076ad2f8SDag-Erling Smørgrav  */
746076ad2f8SDag-Erling Smørgrav 
747076ad2f8SDag-Erling Smørgrav static char *
748076ad2f8SDag-Erling Smørgrav remote_hostname(struct ssh *ssh)
749076ad2f8SDag-Erling Smørgrav {
750076ad2f8SDag-Erling Smørgrav 	struct sockaddr_storage from;
751076ad2f8SDag-Erling Smørgrav 	socklen_t fromlen;
752076ad2f8SDag-Erling Smørgrav 	struct addrinfo hints, *ai, *aitop;
753076ad2f8SDag-Erling Smørgrav 	char name[NI_MAXHOST], ntop2[NI_MAXHOST];
754076ad2f8SDag-Erling Smørgrav 	const char *ntop = ssh_remote_ipaddr(ssh);
755076ad2f8SDag-Erling Smørgrav 
756076ad2f8SDag-Erling Smørgrav 	/* Get IP address of client. */
757076ad2f8SDag-Erling Smørgrav 	fromlen = sizeof(from);
758076ad2f8SDag-Erling Smørgrav 	memset(&from, 0, sizeof(from));
759076ad2f8SDag-Erling Smørgrav 	if (getpeername(ssh_packet_get_connection_in(ssh),
760076ad2f8SDag-Erling Smørgrav 	    (struct sockaddr *)&from, &fromlen) < 0) {
761076ad2f8SDag-Erling Smørgrav 		debug("getpeername failed: %.100s", strerror(errno));
762076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
763076ad2f8SDag-Erling Smørgrav 	}
764076ad2f8SDag-Erling Smørgrav 
765076ad2f8SDag-Erling Smørgrav 	ipv64_normalise_mapped(&from, &fromlen);
766076ad2f8SDag-Erling Smørgrav 	if (from.ss_family == AF_INET6)
767076ad2f8SDag-Erling Smørgrav 		fromlen = sizeof(struct sockaddr_in6);
768076ad2f8SDag-Erling Smørgrav 
769076ad2f8SDag-Erling Smørgrav 	debug3("Trying to reverse map address %.100s.", ntop);
770076ad2f8SDag-Erling Smørgrav 	/* Map the IP address to a host name. */
771076ad2f8SDag-Erling Smørgrav 	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
772076ad2f8SDag-Erling Smørgrav 	    NULL, 0, NI_NAMEREQD) != 0) {
773076ad2f8SDag-Erling Smørgrav 		/* Host name not found.  Use ip address. */
774076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
775076ad2f8SDag-Erling Smørgrav 	}
776076ad2f8SDag-Erling Smørgrav 
777076ad2f8SDag-Erling Smørgrav 	/*
778076ad2f8SDag-Erling Smørgrav 	 * if reverse lookup result looks like a numeric hostname,
779076ad2f8SDag-Erling Smørgrav 	 * someone is trying to trick us by PTR record like following:
780076ad2f8SDag-Erling Smørgrav 	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
781076ad2f8SDag-Erling Smørgrav 	 */
782076ad2f8SDag-Erling Smørgrav 	memset(&hints, 0, sizeof(hints));
783076ad2f8SDag-Erling Smørgrav 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
784076ad2f8SDag-Erling Smørgrav 	hints.ai_flags = AI_NUMERICHOST;
785076ad2f8SDag-Erling Smørgrav 	if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
786076ad2f8SDag-Erling Smørgrav 		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
787076ad2f8SDag-Erling Smørgrav 		    name, ntop);
788076ad2f8SDag-Erling Smørgrav 		freeaddrinfo(ai);
789076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
790076ad2f8SDag-Erling Smørgrav 	}
791076ad2f8SDag-Erling Smørgrav 
792076ad2f8SDag-Erling Smørgrav 	/* Names are stored in lowercase. */
793076ad2f8SDag-Erling Smørgrav 	lowercase(name);
794076ad2f8SDag-Erling Smørgrav 
795076ad2f8SDag-Erling Smørgrav 	/*
796076ad2f8SDag-Erling Smørgrav 	 * Map it back to an IP address and check that the given
797076ad2f8SDag-Erling Smørgrav 	 * address actually is an address of this host.  This is
798076ad2f8SDag-Erling Smørgrav 	 * necessary because anyone with access to a name server can
799076ad2f8SDag-Erling Smørgrav 	 * define arbitrary names for an IP address. Mapping from
800076ad2f8SDag-Erling Smørgrav 	 * name to IP address can be trusted better (but can still be
801076ad2f8SDag-Erling Smørgrav 	 * fooled if the intruder has access to the name server of
802076ad2f8SDag-Erling Smørgrav 	 * the domain).
803076ad2f8SDag-Erling Smørgrav 	 */
804076ad2f8SDag-Erling Smørgrav 	memset(&hints, 0, sizeof(hints));
805076ad2f8SDag-Erling Smørgrav 	hints.ai_family = from.ss_family;
806076ad2f8SDag-Erling Smørgrav 	hints.ai_socktype = SOCK_STREAM;
807076ad2f8SDag-Erling Smørgrav 	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
808076ad2f8SDag-Erling Smørgrav 		logit("reverse mapping checking getaddrinfo for %.700s "
809076ad2f8SDag-Erling Smørgrav 		    "[%s] failed.", name, ntop);
810076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
811076ad2f8SDag-Erling Smørgrav 	}
812076ad2f8SDag-Erling Smørgrav 	/* Look for the address from the list of addresses. */
813076ad2f8SDag-Erling Smørgrav 	for (ai = aitop; ai; ai = ai->ai_next) {
814076ad2f8SDag-Erling Smørgrav 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
815076ad2f8SDag-Erling Smørgrav 		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
816076ad2f8SDag-Erling Smørgrav 		    (strcmp(ntop, ntop2) == 0))
817076ad2f8SDag-Erling Smørgrav 				break;
818076ad2f8SDag-Erling Smørgrav 	}
819076ad2f8SDag-Erling Smørgrav 	freeaddrinfo(aitop);
820076ad2f8SDag-Erling Smørgrav 	/* If we reached the end of the list, the address was not there. */
821076ad2f8SDag-Erling Smørgrav 	if (ai == NULL) {
822076ad2f8SDag-Erling Smørgrav 		/* Address not found for the host name. */
823076ad2f8SDag-Erling Smørgrav 		logit("Address %.100s maps to %.600s, but this does not "
824076ad2f8SDag-Erling Smørgrav 		    "map back to the address.", ntop, name);
825076ad2f8SDag-Erling Smørgrav 		return strdup(ntop);
826076ad2f8SDag-Erling Smørgrav 	}
827076ad2f8SDag-Erling Smørgrav 	return strdup(name);
828076ad2f8SDag-Erling Smørgrav }
829076ad2f8SDag-Erling Smørgrav 
830076ad2f8SDag-Erling Smørgrav /*
831076ad2f8SDag-Erling Smørgrav  * Return the canonical name of the host in the other side of the current
832076ad2f8SDag-Erling Smørgrav  * connection.  The host name is cached, so it is efficient to call this
833076ad2f8SDag-Erling Smørgrav  * several times.
834076ad2f8SDag-Erling Smørgrav  */
835076ad2f8SDag-Erling Smørgrav 
836076ad2f8SDag-Erling Smørgrav const char *
837076ad2f8SDag-Erling Smørgrav auth_get_canonical_hostname(struct ssh *ssh, int use_dns)
838076ad2f8SDag-Erling Smørgrav {
839076ad2f8SDag-Erling Smørgrav 	static char *dnsname;
840076ad2f8SDag-Erling Smørgrav 
841076ad2f8SDag-Erling Smørgrav 	if (!use_dns)
842076ad2f8SDag-Erling Smørgrav 		return ssh_remote_ipaddr(ssh);
843076ad2f8SDag-Erling Smørgrav 	else if (dnsname != NULL)
844076ad2f8SDag-Erling Smørgrav 		return dnsname;
845076ad2f8SDag-Erling Smørgrav 	else {
846076ad2f8SDag-Erling Smørgrav 		dnsname = remote_hostname(ssh);
847076ad2f8SDag-Erling Smørgrav 		return dnsname;
848076ad2f8SDag-Erling Smørgrav 	}
849076ad2f8SDag-Erling Smørgrav }
850*47dd1d1bSDag-Erling Smørgrav 
851*47dd1d1bSDag-Erling Smørgrav /*
852*47dd1d1bSDag-Erling Smørgrav  * Runs command in a subprocess wuth a minimal environment.
853*47dd1d1bSDag-Erling Smørgrav  * Returns pid on success, 0 on failure.
854*47dd1d1bSDag-Erling Smørgrav  * The child stdout and stderr maybe captured, left attached or sent to
855*47dd1d1bSDag-Erling Smørgrav  * /dev/null depending on the contents of flags.
856*47dd1d1bSDag-Erling Smørgrav  * "tag" is prepended to log messages.
857*47dd1d1bSDag-Erling Smørgrav  * NB. "command" is only used for logging; the actual command executed is
858*47dd1d1bSDag-Erling Smørgrav  * av[0].
859*47dd1d1bSDag-Erling Smørgrav  */
860*47dd1d1bSDag-Erling Smørgrav pid_t
861*47dd1d1bSDag-Erling Smørgrav subprocess(const char *tag, struct passwd *pw, const char *command,
862*47dd1d1bSDag-Erling Smørgrav     int ac, char **av, FILE **child, u_int flags)
863*47dd1d1bSDag-Erling Smørgrav {
864*47dd1d1bSDag-Erling Smørgrav 	FILE *f = NULL;
865*47dd1d1bSDag-Erling Smørgrav 	struct stat st;
866*47dd1d1bSDag-Erling Smørgrav 	int fd, devnull, p[2], i;
867*47dd1d1bSDag-Erling Smørgrav 	pid_t pid;
868*47dd1d1bSDag-Erling Smørgrav 	char *cp, errmsg[512];
869*47dd1d1bSDag-Erling Smørgrav 	u_int envsize;
870*47dd1d1bSDag-Erling Smørgrav 	char **child_env;
871*47dd1d1bSDag-Erling Smørgrav 
872*47dd1d1bSDag-Erling Smørgrav 	if (child != NULL)
873*47dd1d1bSDag-Erling Smørgrav 		*child = NULL;
874*47dd1d1bSDag-Erling Smørgrav 
875*47dd1d1bSDag-Erling Smørgrav 	debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__,
876*47dd1d1bSDag-Erling Smørgrav 	    tag, command, pw->pw_name, flags);
877*47dd1d1bSDag-Erling Smørgrav 
878*47dd1d1bSDag-Erling Smørgrav 	/* Check consistency */
879*47dd1d1bSDag-Erling Smørgrav 	if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 &&
880*47dd1d1bSDag-Erling Smørgrav 	    (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) {
881*47dd1d1bSDag-Erling Smørgrav 		error("%s: inconsistent flags", __func__);
882*47dd1d1bSDag-Erling Smørgrav 		return 0;
883*47dd1d1bSDag-Erling Smørgrav 	}
884*47dd1d1bSDag-Erling Smørgrav 	if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) {
885*47dd1d1bSDag-Erling Smørgrav 		error("%s: inconsistent flags/output", __func__);
886*47dd1d1bSDag-Erling Smørgrav 		return 0;
887*47dd1d1bSDag-Erling Smørgrav 	}
888*47dd1d1bSDag-Erling Smørgrav 
889*47dd1d1bSDag-Erling Smørgrav 	/*
890*47dd1d1bSDag-Erling Smørgrav 	 * If executing an explicit binary, then verify the it exists
891*47dd1d1bSDag-Erling Smørgrav 	 * and appears safe-ish to execute
892*47dd1d1bSDag-Erling Smørgrav 	 */
893*47dd1d1bSDag-Erling Smørgrav 	if (*av[0] != '/') {
894*47dd1d1bSDag-Erling Smørgrav 		error("%s path is not absolute", tag);
895*47dd1d1bSDag-Erling Smørgrav 		return 0;
896*47dd1d1bSDag-Erling Smørgrav 	}
897*47dd1d1bSDag-Erling Smørgrav 	temporarily_use_uid(pw);
898*47dd1d1bSDag-Erling Smørgrav 	if (stat(av[0], &st) < 0) {
899*47dd1d1bSDag-Erling Smørgrav 		error("Could not stat %s \"%s\": %s", tag,
900*47dd1d1bSDag-Erling Smørgrav 		    av[0], strerror(errno));
901*47dd1d1bSDag-Erling Smørgrav 		restore_uid();
902*47dd1d1bSDag-Erling Smørgrav 		return 0;
903*47dd1d1bSDag-Erling Smørgrav 	}
904*47dd1d1bSDag-Erling Smørgrav 	if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) {
905*47dd1d1bSDag-Erling Smørgrav 		error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
906*47dd1d1bSDag-Erling Smørgrav 		restore_uid();
907*47dd1d1bSDag-Erling Smørgrav 		return 0;
908*47dd1d1bSDag-Erling Smørgrav 	}
909*47dd1d1bSDag-Erling Smørgrav 	/* Prepare to keep the child's stdout if requested */
910*47dd1d1bSDag-Erling Smørgrav 	if (pipe(p) != 0) {
911*47dd1d1bSDag-Erling Smørgrav 		error("%s: pipe: %s", tag, strerror(errno));
912*47dd1d1bSDag-Erling Smørgrav 		restore_uid();
913*47dd1d1bSDag-Erling Smørgrav 		return 0;
914*47dd1d1bSDag-Erling Smørgrav 	}
915*47dd1d1bSDag-Erling Smørgrav 	restore_uid();
916*47dd1d1bSDag-Erling Smørgrav 
917*47dd1d1bSDag-Erling Smørgrav 	switch ((pid = fork())) {
918*47dd1d1bSDag-Erling Smørgrav 	case -1: /* error */
919*47dd1d1bSDag-Erling Smørgrav 		error("%s: fork: %s", tag, strerror(errno));
920*47dd1d1bSDag-Erling Smørgrav 		close(p[0]);
921*47dd1d1bSDag-Erling Smørgrav 		close(p[1]);
922*47dd1d1bSDag-Erling Smørgrav 		return 0;
923*47dd1d1bSDag-Erling Smørgrav 	case 0: /* child */
924*47dd1d1bSDag-Erling Smørgrav 		/* Prepare a minimal environment for the child. */
925*47dd1d1bSDag-Erling Smørgrav 		envsize = 5;
926*47dd1d1bSDag-Erling Smørgrav 		child_env = xcalloc(sizeof(*child_env), envsize);
927*47dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
928*47dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "USER", pw->pw_name);
929*47dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
930*47dd1d1bSDag-Erling Smørgrav 		child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
931*47dd1d1bSDag-Erling Smørgrav 		if ((cp = getenv("LANG")) != NULL)
932*47dd1d1bSDag-Erling Smørgrav 			child_set_env(&child_env, &envsize, "LANG", cp);
933*47dd1d1bSDag-Erling Smørgrav 
934*47dd1d1bSDag-Erling Smørgrav 		for (i = 0; i < NSIG; i++)
935*47dd1d1bSDag-Erling Smørgrav 			signal(i, SIG_DFL);
936*47dd1d1bSDag-Erling Smørgrav 
937*47dd1d1bSDag-Erling Smørgrav 		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
938*47dd1d1bSDag-Erling Smørgrav 			error("%s: open %s: %s", tag, _PATH_DEVNULL,
939*47dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
940*47dd1d1bSDag-Erling Smørgrav 			_exit(1);
941*47dd1d1bSDag-Erling Smørgrav 		}
942*47dd1d1bSDag-Erling Smørgrav 		if (dup2(devnull, STDIN_FILENO) == -1) {
943*47dd1d1bSDag-Erling Smørgrav 			error("%s: dup2: %s", tag, strerror(errno));
944*47dd1d1bSDag-Erling Smørgrav 			_exit(1);
945*47dd1d1bSDag-Erling Smørgrav 		}
946*47dd1d1bSDag-Erling Smørgrav 
947*47dd1d1bSDag-Erling Smørgrav 		/* Set up stdout as requested; leave stderr in place for now. */
948*47dd1d1bSDag-Erling Smørgrav 		fd = -1;
949*47dd1d1bSDag-Erling Smørgrav 		if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0)
950*47dd1d1bSDag-Erling Smørgrav 			fd = p[1];
951*47dd1d1bSDag-Erling Smørgrav 		else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0)
952*47dd1d1bSDag-Erling Smørgrav 			fd = devnull;
953*47dd1d1bSDag-Erling Smørgrav 		if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) {
954*47dd1d1bSDag-Erling Smørgrav 			error("%s: dup2: %s", tag, strerror(errno));
955*47dd1d1bSDag-Erling Smørgrav 			_exit(1);
956*47dd1d1bSDag-Erling Smørgrav 		}
957*47dd1d1bSDag-Erling Smørgrav 		closefrom(STDERR_FILENO + 1);
958*47dd1d1bSDag-Erling Smørgrav 
959*47dd1d1bSDag-Erling Smørgrav 		/* Don't use permanently_set_uid() here to avoid fatal() */
960*47dd1d1bSDag-Erling Smørgrav 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
961*47dd1d1bSDag-Erling Smørgrav 			error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
962*47dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
963*47dd1d1bSDag-Erling Smørgrav 			_exit(1);
964*47dd1d1bSDag-Erling Smørgrav 		}
965*47dd1d1bSDag-Erling Smørgrav 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
966*47dd1d1bSDag-Erling Smørgrav 			error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
967*47dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
968*47dd1d1bSDag-Erling Smørgrav 			_exit(1);
969*47dd1d1bSDag-Erling Smørgrav 		}
970*47dd1d1bSDag-Erling Smørgrav 		/* stdin is pointed to /dev/null at this point */
971*47dd1d1bSDag-Erling Smørgrav 		if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 &&
972*47dd1d1bSDag-Erling Smørgrav 		    dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
973*47dd1d1bSDag-Erling Smørgrav 			error("%s: dup2: %s", tag, strerror(errno));
974*47dd1d1bSDag-Erling Smørgrav 			_exit(1);
975*47dd1d1bSDag-Erling Smørgrav 		}
976*47dd1d1bSDag-Erling Smørgrav 
977*47dd1d1bSDag-Erling Smørgrav 		execve(av[0], av, child_env);
978*47dd1d1bSDag-Erling Smørgrav 		error("%s exec \"%s\": %s", tag, command, strerror(errno));
979*47dd1d1bSDag-Erling Smørgrav 		_exit(127);
980*47dd1d1bSDag-Erling Smørgrav 	default: /* parent */
981*47dd1d1bSDag-Erling Smørgrav 		break;
982*47dd1d1bSDag-Erling Smørgrav 	}
983*47dd1d1bSDag-Erling Smørgrav 
984*47dd1d1bSDag-Erling Smørgrav 	close(p[1]);
985*47dd1d1bSDag-Erling Smørgrav 	if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0)
986*47dd1d1bSDag-Erling Smørgrav 		close(p[0]);
987*47dd1d1bSDag-Erling Smørgrav 	else if ((f = fdopen(p[0], "r")) == NULL) {
988*47dd1d1bSDag-Erling Smørgrav 		error("%s: fdopen: %s", tag, strerror(errno));
989*47dd1d1bSDag-Erling Smørgrav 		close(p[0]);
990*47dd1d1bSDag-Erling Smørgrav 		/* Don't leave zombie child */
991*47dd1d1bSDag-Erling Smørgrav 		kill(pid, SIGTERM);
992*47dd1d1bSDag-Erling Smørgrav 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
993*47dd1d1bSDag-Erling Smørgrav 			;
994*47dd1d1bSDag-Erling Smørgrav 		return 0;
995*47dd1d1bSDag-Erling Smørgrav 	}
996*47dd1d1bSDag-Erling Smørgrav 	/* Success */
997*47dd1d1bSDag-Erling Smørgrav 	debug3("%s: %s pid %ld", __func__, tag, (long)pid);
998*47dd1d1bSDag-Erling Smørgrav 	if (child != NULL)
999*47dd1d1bSDag-Erling Smørgrav 		*child = f;
1000*47dd1d1bSDag-Erling Smørgrav 	return pid;
1001*47dd1d1bSDag-Erling Smørgrav }
1002*47dd1d1bSDag-Erling Smørgrav 
1003*47dd1d1bSDag-Erling Smørgrav /* These functions link key/cert options to the auth framework */
1004*47dd1d1bSDag-Erling Smørgrav 
1005*47dd1d1bSDag-Erling Smørgrav /* Log sshauthopt options locally and (optionally) for remote transmission */
1006*47dd1d1bSDag-Erling Smørgrav void
1007*47dd1d1bSDag-Erling Smørgrav auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
1008*47dd1d1bSDag-Erling Smørgrav {
1009*47dd1d1bSDag-Erling Smørgrav 	int do_env = options.permit_user_env && opts->nenv > 0;
1010*47dd1d1bSDag-Erling Smørgrav 	int do_permitopen = opts->npermitopen > 0 &&
1011*47dd1d1bSDag-Erling Smørgrav 	    (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0;
1012*47dd1d1bSDag-Erling Smørgrav 	size_t i;
1013*47dd1d1bSDag-Erling Smørgrav 	char msg[1024], buf[64];
1014*47dd1d1bSDag-Erling Smørgrav 
1015*47dd1d1bSDag-Erling Smørgrav 	snprintf(buf, sizeof(buf), "%d", opts->force_tun_device);
1016*47dd1d1bSDag-Erling Smørgrav 	/* Try to keep this alphabetically sorted */
1017*47dd1d1bSDag-Erling Smørgrav 	snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s",
1018*47dd1d1bSDag-Erling Smørgrav 	    opts->permit_agent_forwarding_flag ? " agent-forwarding" : "",
1019*47dd1d1bSDag-Erling Smørgrav 	    opts->force_command == NULL ? "" : " command",
1020*47dd1d1bSDag-Erling Smørgrav 	    do_env ?  " environment" : "",
1021*47dd1d1bSDag-Erling Smørgrav 	    opts->valid_before == 0 ? "" : "expires",
1022*47dd1d1bSDag-Erling Smørgrav 	    do_permitopen ?  " permitopen" : "",
1023*47dd1d1bSDag-Erling Smørgrav 	    opts->permit_port_forwarding_flag ? " port-forwarding" : "",
1024*47dd1d1bSDag-Erling Smørgrav 	    opts->cert_principals == NULL ? "" : " principals",
1025*47dd1d1bSDag-Erling Smørgrav 	    opts->permit_pty_flag ? " pty" : "",
1026*47dd1d1bSDag-Erling Smørgrav 	    opts->force_tun_device == -1 ? "" : " tun=",
1027*47dd1d1bSDag-Erling Smørgrav 	    opts->force_tun_device == -1 ? "" : buf,
1028*47dd1d1bSDag-Erling Smørgrav 	    opts->permit_user_rc ? " user-rc" : "",
1029*47dd1d1bSDag-Erling Smørgrav 	    opts->permit_x11_forwarding_flag ? " x11-forwarding" : "");
1030*47dd1d1bSDag-Erling Smørgrav 
1031*47dd1d1bSDag-Erling Smørgrav 	debug("%s: %s", loc, msg);
1032*47dd1d1bSDag-Erling Smørgrav 	if (do_remote)
1033*47dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: %s", loc, msg);
1034*47dd1d1bSDag-Erling Smørgrav 
1035*47dd1d1bSDag-Erling Smørgrav 	if (options.permit_user_env) {
1036*47dd1d1bSDag-Erling Smørgrav 		for (i = 0; i < opts->nenv; i++) {
1037*47dd1d1bSDag-Erling Smørgrav 			debug("%s: environment: %s", loc, opts->env[i]);
1038*47dd1d1bSDag-Erling Smørgrav 			if (do_remote) {
1039*47dd1d1bSDag-Erling Smørgrav 				auth_debug_add("%s: environment: %s",
1040*47dd1d1bSDag-Erling Smørgrav 				    loc, opts->env[i]);
1041*47dd1d1bSDag-Erling Smørgrav 			}
1042*47dd1d1bSDag-Erling Smørgrav 		}
1043*47dd1d1bSDag-Erling Smørgrav 	}
1044*47dd1d1bSDag-Erling Smørgrav 
1045*47dd1d1bSDag-Erling Smørgrav 	/* Go into a little more details for the local logs. */
1046*47dd1d1bSDag-Erling Smørgrav 	if (opts->valid_before != 0) {
1047*47dd1d1bSDag-Erling Smørgrav 		format_absolute_time(opts->valid_before, buf, sizeof(buf));
1048*47dd1d1bSDag-Erling Smørgrav 		debug("%s: expires at %s", loc, buf);
1049*47dd1d1bSDag-Erling Smørgrav 	}
1050*47dd1d1bSDag-Erling Smørgrav 	if (opts->cert_principals != NULL) {
1051*47dd1d1bSDag-Erling Smørgrav 		debug("%s: authorized principals: \"%s\"",
1052*47dd1d1bSDag-Erling Smørgrav 		    loc, opts->cert_principals);
1053*47dd1d1bSDag-Erling Smørgrav 	}
1054*47dd1d1bSDag-Erling Smørgrav 	if (opts->force_command != NULL)
1055*47dd1d1bSDag-Erling Smørgrav 		debug("%s: forced command: \"%s\"", loc, opts->force_command);
1056*47dd1d1bSDag-Erling Smørgrav 	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) {
1057*47dd1d1bSDag-Erling Smørgrav 		for (i = 0; i < opts->npermitopen; i++) {
1058*47dd1d1bSDag-Erling Smørgrav 			debug("%s: permitted open: %s",
1059*47dd1d1bSDag-Erling Smørgrav 			    loc, opts->permitopen[i]);
1060*47dd1d1bSDag-Erling Smørgrav 		}
1061*47dd1d1bSDag-Erling Smørgrav 	}
1062*47dd1d1bSDag-Erling Smørgrav }
1063*47dd1d1bSDag-Erling Smørgrav 
1064*47dd1d1bSDag-Erling Smørgrav /* Activate a new set of key/cert options; merging with what is there. */
1065*47dd1d1bSDag-Erling Smørgrav int
1066*47dd1d1bSDag-Erling Smørgrav auth_activate_options(struct ssh *ssh, struct sshauthopt *opts)
1067*47dd1d1bSDag-Erling Smørgrav {
1068*47dd1d1bSDag-Erling Smørgrav 	struct sshauthopt *old = auth_opts;
1069*47dd1d1bSDag-Erling Smørgrav 	const char *emsg = NULL;
1070*47dd1d1bSDag-Erling Smørgrav 
1071*47dd1d1bSDag-Erling Smørgrav 	debug("%s: setting new authentication options", __func__);
1072*47dd1d1bSDag-Erling Smørgrav 	if ((auth_opts = sshauthopt_merge(old, opts, &emsg)) == NULL) {
1073*47dd1d1bSDag-Erling Smørgrav 		error("Inconsistent authentication options: %s", emsg);
1074*47dd1d1bSDag-Erling Smørgrav 		return -1;
1075*47dd1d1bSDag-Erling Smørgrav 	}
1076*47dd1d1bSDag-Erling Smørgrav 	return 0;
1077*47dd1d1bSDag-Erling Smørgrav }
1078*47dd1d1bSDag-Erling Smørgrav 
1079*47dd1d1bSDag-Erling Smørgrav /* Disable forwarding, etc for the session */
1080*47dd1d1bSDag-Erling Smørgrav void
1081*47dd1d1bSDag-Erling Smørgrav auth_restrict_session(struct ssh *ssh)
1082*47dd1d1bSDag-Erling Smørgrav {
1083*47dd1d1bSDag-Erling Smørgrav 	struct sshauthopt *restricted;
1084*47dd1d1bSDag-Erling Smørgrav 
1085*47dd1d1bSDag-Erling Smørgrav 	debug("%s: restricting session", __func__);
1086*47dd1d1bSDag-Erling Smørgrav 
1087*47dd1d1bSDag-Erling Smørgrav 	/* A blank sshauthopt defaults to permitting nothing */
1088*47dd1d1bSDag-Erling Smørgrav 	restricted = sshauthopt_new();
1089*47dd1d1bSDag-Erling Smørgrav 	restricted->restricted = 1;
1090*47dd1d1bSDag-Erling Smørgrav 
1091*47dd1d1bSDag-Erling Smørgrav 	if (auth_activate_options(ssh, restricted) != 0)
1092*47dd1d1bSDag-Erling Smørgrav 		fatal("%s: failed to restrict session", __func__);
1093*47dd1d1bSDag-Erling Smørgrav 	sshauthopt_free(restricted);
1094*47dd1d1bSDag-Erling Smørgrav }
1095*47dd1d1bSDag-Erling Smørgrav 
1096*47dd1d1bSDag-Erling Smørgrav int
1097*47dd1d1bSDag-Erling Smørgrav auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw,
1098*47dd1d1bSDag-Erling Smørgrav     struct sshauthopt *opts, int allow_cert_authority, const char *loc)
1099*47dd1d1bSDag-Erling Smørgrav {
1100*47dd1d1bSDag-Erling Smørgrav 	const char *remote_ip = ssh_remote_ipaddr(ssh);
1101*47dd1d1bSDag-Erling Smørgrav 	const char *remote_host = auth_get_canonical_hostname(ssh,
1102*47dd1d1bSDag-Erling Smørgrav 	    options.use_dns);
1103*47dd1d1bSDag-Erling Smørgrav 	time_t now = time(NULL);
1104*47dd1d1bSDag-Erling Smørgrav 	char buf[64];
1105*47dd1d1bSDag-Erling Smørgrav 
1106*47dd1d1bSDag-Erling Smørgrav 	/*
1107*47dd1d1bSDag-Erling Smørgrav 	 * Check keys/principals file expiry time.
1108*47dd1d1bSDag-Erling Smørgrav 	 * NB. validity interval in certificate is handled elsewhere.
1109*47dd1d1bSDag-Erling Smørgrav 	 */
1110*47dd1d1bSDag-Erling Smørgrav 	if (opts->valid_before && now > 0 &&
1111*47dd1d1bSDag-Erling Smørgrav 	    opts->valid_before < (uint64_t)now) {
1112*47dd1d1bSDag-Erling Smørgrav 		format_absolute_time(opts->valid_before, buf, sizeof(buf));
1113*47dd1d1bSDag-Erling Smørgrav 		debug("%s: entry expired at %s", loc, buf);
1114*47dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: entry expired at %s", loc, buf);
1115*47dd1d1bSDag-Erling Smørgrav 		return -1;
1116*47dd1d1bSDag-Erling Smørgrav 	}
1117*47dd1d1bSDag-Erling Smørgrav 	/* Consistency checks */
1118*47dd1d1bSDag-Erling Smørgrav 	if (opts->cert_principals != NULL && !opts->cert_authority) {
1119*47dd1d1bSDag-Erling Smørgrav 		debug("%s: principals on non-CA key", loc);
1120*47dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: principals on non-CA key", loc);
1121*47dd1d1bSDag-Erling Smørgrav 		/* deny access */
1122*47dd1d1bSDag-Erling Smørgrav 		return -1;
1123*47dd1d1bSDag-Erling Smørgrav 	}
1124*47dd1d1bSDag-Erling Smørgrav 	/* cert-authority flag isn't valid in authorized_principals files */
1125*47dd1d1bSDag-Erling Smørgrav 	if (!allow_cert_authority && opts->cert_authority) {
1126*47dd1d1bSDag-Erling Smørgrav 		debug("%s: cert-authority flag invalid here", loc);
1127*47dd1d1bSDag-Erling Smørgrav 		auth_debug_add("%s: cert-authority flag invalid here", loc);
1128*47dd1d1bSDag-Erling Smørgrav 		/* deny access */
1129*47dd1d1bSDag-Erling Smørgrav 		return -1;
1130*47dd1d1bSDag-Erling Smørgrav 	}
1131*47dd1d1bSDag-Erling Smørgrav 
1132*47dd1d1bSDag-Erling Smørgrav 	/* Perform from= checks */
1133*47dd1d1bSDag-Erling Smørgrav 	if (opts->required_from_host_keys != NULL) {
1134*47dd1d1bSDag-Erling Smørgrav 		switch (match_host_and_ip(remote_host, remote_ip,
1135*47dd1d1bSDag-Erling Smørgrav 		    opts->required_from_host_keys )) {
1136*47dd1d1bSDag-Erling Smørgrav 		case 1:
1137*47dd1d1bSDag-Erling Smørgrav 			/* Host name matches. */
1138*47dd1d1bSDag-Erling Smørgrav 			break;
1139*47dd1d1bSDag-Erling Smørgrav 		case -1:
1140*47dd1d1bSDag-Erling Smørgrav 		default:
1141*47dd1d1bSDag-Erling Smørgrav 			debug("%s: invalid from criteria", loc);
1142*47dd1d1bSDag-Erling Smørgrav 			auth_debug_add("%s: invalid from criteria", loc);
1143*47dd1d1bSDag-Erling Smørgrav 			/* FALLTHROUGH */
1144*47dd1d1bSDag-Erling Smørgrav 		case 0:
1145*47dd1d1bSDag-Erling Smørgrav 			logit("%s: Authentication tried for %.100s with "
1146*47dd1d1bSDag-Erling Smørgrav 			    "correct key but not from a permitted "
1147*47dd1d1bSDag-Erling Smørgrav 			    "host (host=%.200s, ip=%.200s, required=%.200s).",
1148*47dd1d1bSDag-Erling Smørgrav 			    loc, pw->pw_name, remote_host, remote_ip,
1149*47dd1d1bSDag-Erling Smørgrav 			    opts->required_from_host_keys);
1150*47dd1d1bSDag-Erling Smørgrav 			auth_debug_add("%s: Your host '%.200s' is not "
1151*47dd1d1bSDag-Erling Smørgrav 			    "permitted to use this key for login.",
1152*47dd1d1bSDag-Erling Smørgrav 			    loc, remote_host);
1153*47dd1d1bSDag-Erling Smørgrav 			/* deny access */
1154*47dd1d1bSDag-Erling Smørgrav 			return -1;
1155*47dd1d1bSDag-Erling Smørgrav 		}
1156*47dd1d1bSDag-Erling Smørgrav 	}
1157*47dd1d1bSDag-Erling Smørgrav 	/* Check source-address restriction from certificate */
1158*47dd1d1bSDag-Erling Smørgrav 	if (opts->required_from_host_cert != NULL) {
1159*47dd1d1bSDag-Erling Smørgrav 		switch (addr_match_cidr_list(remote_ip,
1160*47dd1d1bSDag-Erling Smørgrav 		    opts->required_from_host_cert)) {
1161*47dd1d1bSDag-Erling Smørgrav 		case 1:
1162*47dd1d1bSDag-Erling Smørgrav 			/* accepted */
1163*47dd1d1bSDag-Erling Smørgrav 			break;
1164*47dd1d1bSDag-Erling Smørgrav 		case -1:
1165*47dd1d1bSDag-Erling Smørgrav 		default:
1166*47dd1d1bSDag-Erling Smørgrav 			/* invalid */
1167*47dd1d1bSDag-Erling Smørgrav 			error("%s: Certificate source-address invalid",
1168*47dd1d1bSDag-Erling Smørgrav 			    loc);
1169*47dd1d1bSDag-Erling Smørgrav 			/* FALLTHROUGH */
1170*47dd1d1bSDag-Erling Smørgrav 		case 0:
1171*47dd1d1bSDag-Erling Smørgrav 			logit("%s: Authentication tried for %.100s with valid "
1172*47dd1d1bSDag-Erling Smørgrav 			    "certificate but not from a permitted source "
1173*47dd1d1bSDag-Erling Smørgrav 			    "address (%.200s).", loc, pw->pw_name, remote_ip);
1174*47dd1d1bSDag-Erling Smørgrav 			auth_debug_add("%s: Your address '%.200s' is not "
1175*47dd1d1bSDag-Erling Smørgrav 			    "permitted to use this certificate for login.",
1176*47dd1d1bSDag-Erling Smørgrav 			    loc, remote_ip);
1177*47dd1d1bSDag-Erling Smørgrav 			return -1;
1178*47dd1d1bSDag-Erling Smørgrav 		}
1179*47dd1d1bSDag-Erling Smørgrav 	}
1180*47dd1d1bSDag-Erling Smørgrav 	/*
1181*47dd1d1bSDag-Erling Smørgrav 	 *
1182*47dd1d1bSDag-Erling Smørgrav 	 * XXX this is spammy. We should report remotely only for keys
1183*47dd1d1bSDag-Erling Smørgrav 	 *     that are successful in actual auth attempts, and not PK_OK
1184*47dd1d1bSDag-Erling Smørgrav 	 *     tests.
1185*47dd1d1bSDag-Erling Smørgrav 	 */
1186*47dd1d1bSDag-Erling Smørgrav 	auth_log_authopts(loc, opts, 1);
1187*47dd1d1bSDag-Erling Smørgrav 
1188*47dd1d1bSDag-Erling Smørgrav 	return 0;
1189*47dd1d1bSDag-Erling Smørgrav }
1190