1 |
|
|
/* $OpenBSD: auth.c,v 1.124 2017/09/12 06:32:07 djm Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 |
|
|
* |
5 |
|
|
* Redistribution and use in source and binary forms, with or without |
6 |
|
|
* modification, are permitted provided that the following conditions |
7 |
|
|
* are met: |
8 |
|
|
* 1. Redistributions of source code must retain the above copyright |
9 |
|
|
* notice, this list of conditions and the following disclaimer. |
10 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer in the |
12 |
|
|
* documentation and/or other materials provided with the distribution. |
13 |
|
|
* |
14 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
15 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
16 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
17 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
18 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
19 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
20 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
21 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
23 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 |
|
|
*/ |
25 |
|
|
|
26 |
|
|
#include <sys/types.h> |
27 |
|
|
#include <sys/stat.h> |
28 |
|
|
#include <sys/socket.h> |
29 |
|
|
|
30 |
|
|
#include <errno.h> |
31 |
|
|
#include <fcntl.h> |
32 |
|
|
#include <login_cap.h> |
33 |
|
|
#include <paths.h> |
34 |
|
|
#include <pwd.h> |
35 |
|
|
#include <stdarg.h> |
36 |
|
|
#include <stdio.h> |
37 |
|
|
#include <string.h> |
38 |
|
|
#include <unistd.h> |
39 |
|
|
#include <limits.h> |
40 |
|
|
#include <netdb.h> |
41 |
|
|
|
42 |
|
|
#include "xmalloc.h" |
43 |
|
|
#include "match.h" |
44 |
|
|
#include "groupaccess.h" |
45 |
|
|
#include "log.h" |
46 |
|
|
#include "buffer.h" |
47 |
|
|
#include "misc.h" |
48 |
|
|
#include "servconf.h" |
49 |
|
|
#include "key.h" |
50 |
|
|
#include "hostfile.h" |
51 |
|
|
#include "auth.h" |
52 |
|
|
#include "auth-options.h" |
53 |
|
|
#include "canohost.h" |
54 |
|
|
#include "uidswap.h" |
55 |
|
|
#include "packet.h" |
56 |
|
|
#ifdef GSSAPI |
57 |
|
|
#include "ssh-gss.h" |
58 |
|
|
#endif |
59 |
|
|
#include "authfile.h" |
60 |
|
|
#include "monitor_wrap.h" |
61 |
|
|
#include "authfile.h" |
62 |
|
|
#include "ssherr.h" |
63 |
|
|
#include "compat.h" |
64 |
|
|
|
65 |
|
|
/* import */ |
66 |
|
|
extern ServerOptions options; |
67 |
|
|
extern int use_privsep; |
68 |
|
|
|
69 |
|
|
/* Debugging messages */ |
70 |
|
|
Buffer auth_debug; |
71 |
|
|
int auth_debug_init; |
72 |
|
|
|
73 |
|
|
/* |
74 |
|
|
* Check if the user is allowed to log in via ssh. If user is listed |
75 |
|
|
* in DenyUsers or one of user's groups is listed in DenyGroups, false |
76 |
|
|
* will be returned. If AllowUsers isn't empty and user isn't listed |
77 |
|
|
* there, or if AllowGroups isn't empty and one of user's groups isn't |
78 |
|
|
* listed there, false will be returned. |
79 |
|
|
* If the user's shell is not executable, false will be returned. |
80 |
|
|
* Otherwise true is returned. |
81 |
|
|
*/ |
82 |
|
|
int |
83 |
|
|
allowed_user(struct passwd * pw) |
84 |
|
|
{ |
85 |
|
|
struct ssh *ssh = active_state; /* XXX */ |
86 |
|
|
struct stat st; |
87 |
|
|
const char *hostname = NULL, *ipaddr = NULL; |
88 |
|
|
int r; |
89 |
|
|
u_int i; |
90 |
|
|
|
91 |
|
|
/* Shouldn't be called if pw is NULL, but better safe than sorry... */ |
92 |
|
|
if (!pw || !pw->pw_name) |
93 |
|
|
return 0; |
94 |
|
|
|
95 |
|
|
/* |
96 |
|
|
* Deny if shell does not exist or is not executable unless we |
97 |
|
|
* are chrooting. |
98 |
|
|
*/ |
99 |
|
|
if (options.chroot_directory == NULL || |
100 |
|
|
strcasecmp(options.chroot_directory, "none") == 0) { |
101 |
|
|
char *shell = xstrdup((pw->pw_shell[0] == '\0') ? |
102 |
|
|
_PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ |
103 |
|
|
|
104 |
|
|
if (stat(shell, &st) != 0) { |
105 |
|
|
logit("User %.100s not allowed because shell %.100s " |
106 |
|
|
"does not exist", pw->pw_name, shell); |
107 |
|
|
free(shell); |
108 |
|
|
return 0; |
109 |
|
|
} |
110 |
|
|
if (S_ISREG(st.st_mode) == 0 || |
111 |
|
|
(st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { |
112 |
|
|
logit("User %.100s not allowed because shell %.100s " |
113 |
|
|
"is not executable", pw->pw_name, shell); |
114 |
|
|
free(shell); |
115 |
|
|
return 0; |
116 |
|
|
} |
117 |
|
|
free(shell); |
118 |
|
|
} |
119 |
|
|
|
120 |
|
|
if (options.num_deny_users > 0 || options.num_allow_users > 0 || |
121 |
|
|
options.num_deny_groups > 0 || options.num_allow_groups > 0) { |
122 |
|
|
hostname = auth_get_canonical_hostname(ssh, options.use_dns); |
123 |
|
|
ipaddr = ssh_remote_ipaddr(ssh); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
/* Return false if user is listed in DenyUsers */ |
127 |
|
|
if (options.num_deny_users > 0) { |
128 |
|
|
for (i = 0; i < options.num_deny_users; i++) { |
129 |
|
|
r = match_user(pw->pw_name, hostname, ipaddr, |
130 |
|
|
options.deny_users[i]); |
131 |
|
|
if (r < 0) { |
132 |
|
|
fatal("Invalid DenyUsers pattern \"%.100s\"", |
133 |
|
|
options.deny_users[i]); |
134 |
|
|
} else if (r != 0) { |
135 |
|
|
logit("User %.100s from %.100s not allowed " |
136 |
|
|
"because listed in DenyUsers", |
137 |
|
|
pw->pw_name, hostname); |
138 |
|
|
return 0; |
139 |
|
|
} |
140 |
|
|
} |
141 |
|
|
} |
142 |
|
|
/* Return false if AllowUsers isn't empty and user isn't listed there */ |
143 |
|
|
if (options.num_allow_users > 0) { |
144 |
|
|
for (i = 0; i < options.num_allow_users; i++) { |
145 |
|
|
r = match_user(pw->pw_name, hostname, ipaddr, |
146 |
|
|
options.allow_users[i]); |
147 |
|
|
if (r < 0) { |
148 |
|
|
fatal("Invalid AllowUsers pattern \"%.100s\"", |
149 |
|
|
options.allow_users[i]); |
150 |
|
|
} else if (r == 1) |
151 |
|
|
break; |
152 |
|
|
} |
153 |
|
|
/* i < options.num_allow_users iff we break for loop */ |
154 |
|
|
if (i >= options.num_allow_users) { |
155 |
|
|
logit("User %.100s from %.100s not allowed because " |
156 |
|
|
"not listed in AllowUsers", pw->pw_name, hostname); |
157 |
|
|
return 0; |
158 |
|
|
} |
159 |
|
|
} |
160 |
|
|
if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { |
161 |
|
|
/* Get the user's group access list (primary and supplementary) */ |
162 |
|
|
if (ga_init(pw->pw_name, pw->pw_gid) == 0) { |
163 |
|
|
logit("User %.100s from %.100s not allowed because " |
164 |
|
|
"not in any group", pw->pw_name, hostname); |
165 |
|
|
return 0; |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
/* Return false if one of user's groups is listed in DenyGroups */ |
169 |
|
|
if (options.num_deny_groups > 0) |
170 |
|
|
if (ga_match(options.deny_groups, |
171 |
|
|
options.num_deny_groups)) { |
172 |
|
|
ga_free(); |
173 |
|
|
logit("User %.100s from %.100s not allowed " |
174 |
|
|
"because a group is listed in DenyGroups", |
175 |
|
|
pw->pw_name, hostname); |
176 |
|
|
return 0; |
177 |
|
|
} |
178 |
|
|
/* |
179 |
|
|
* Return false if AllowGroups isn't empty and one of user's groups |
180 |
|
|
* isn't listed there |
181 |
|
|
*/ |
182 |
|
|
if (options.num_allow_groups > 0) |
183 |
|
|
if (!ga_match(options.allow_groups, |
184 |
|
|
options.num_allow_groups)) { |
185 |
|
|
ga_free(); |
186 |
|
|
logit("User %.100s from %.100s not allowed " |
187 |
|
|
"because none of user's groups are listed " |
188 |
|
|
"in AllowGroups", pw->pw_name, hostname); |
189 |
|
|
return 0; |
190 |
|
|
} |
191 |
|
|
ga_free(); |
192 |
|
|
} |
193 |
|
|
/* We found no reason not to let this user try to log on... */ |
194 |
|
|
return 1; |
195 |
|
|
} |
196 |
|
|
|
197 |
|
|
/* |
198 |
|
|
* Formats any key left in authctxt->auth_method_key for inclusion in |
199 |
|
|
* auth_log()'s message. Also includes authxtct->auth_method_info if present. |
200 |
|
|
*/ |
201 |
|
|
static char * |
202 |
|
|
format_method_key(Authctxt *authctxt) |
203 |
|
|
{ |
204 |
|
|
const struct sshkey *key = authctxt->auth_method_key; |
205 |
|
|
const char *methinfo = authctxt->auth_method_info; |
206 |
|
|
char *fp, *ret = NULL; |
207 |
|
|
|
208 |
|
|
if (key == NULL) |
209 |
|
|
return NULL; |
210 |
|
|
|
211 |
|
|
if (key_is_cert(key)) { |
212 |
|
|
fp = sshkey_fingerprint(key->cert->signature_key, |
213 |
|
|
options.fingerprint_hash, SSH_FP_DEFAULT); |
214 |
|
|
xasprintf(&ret, "%s ID %s (serial %llu) CA %s %s%s%s", |
215 |
|
|
sshkey_type(key), key->cert->key_id, |
216 |
|
|
(unsigned long long)key->cert->serial, |
217 |
|
|
sshkey_type(key->cert->signature_key), |
218 |
|
|
fp == NULL ? "(null)" : fp, |
219 |
|
|
methinfo == NULL ? "" : ", ", |
220 |
|
|
methinfo == NULL ? "" : methinfo); |
221 |
|
|
free(fp); |
222 |
|
|
} else { |
223 |
|
|
fp = sshkey_fingerprint(key, options.fingerprint_hash, |
224 |
|
|
SSH_FP_DEFAULT); |
225 |
|
|
xasprintf(&ret, "%s %s%s%s", sshkey_type(key), |
226 |
|
|
fp == NULL ? "(null)" : fp, |
227 |
|
|
methinfo == NULL ? "" : ", ", |
228 |
|
|
methinfo == NULL ? "" : methinfo); |
229 |
|
|
free(fp); |
230 |
|
|
} |
231 |
|
|
return ret; |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
void |
235 |
|
|
auth_log(Authctxt *authctxt, int authenticated, int partial, |
236 |
|
|
const char *method, const char *submethod) |
237 |
|
|
{ |
238 |
|
|
struct ssh *ssh = active_state; /* XXX */ |
239 |
|
|
void (*authlog) (const char *fmt,...) = verbose; |
240 |
|
|
const char *authmsg; |
241 |
|
|
char *extra = NULL; |
242 |
|
|
|
243 |
|
|
if (use_privsep && !mm_is_monitor() && !authctxt->postponed) |
244 |
|
|
return; |
245 |
|
|
|
246 |
|
|
/* Raise logging level */ |
247 |
|
|
if (authenticated == 1 || |
248 |
|
|
!authctxt->valid || |
249 |
|
|
authctxt->failures >= options.max_authtries / 2 || |
250 |
|
|
strcmp(method, "password") == 0) |
251 |
|
|
authlog = logit; |
252 |
|
|
|
253 |
|
|
if (authctxt->postponed) |
254 |
|
|
authmsg = "Postponed"; |
255 |
|
|
else if (partial) |
256 |
|
|
authmsg = "Partial"; |
257 |
|
|
else |
258 |
|
|
authmsg = authenticated ? "Accepted" : "Failed"; |
259 |
|
|
|
260 |
|
|
if ((extra = format_method_key(authctxt)) == NULL) { |
261 |
|
|
if (authctxt->auth_method_info != NULL) |
262 |
|
|
extra = xstrdup(authctxt->auth_method_info); |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", |
266 |
|
|
authmsg, |
267 |
|
|
method, |
268 |
|
|
submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, |
269 |
|
|
authctxt->valid ? "" : "invalid user ", |
270 |
|
|
authctxt->user, |
271 |
|
|
ssh_remote_ipaddr(ssh), |
272 |
|
|
ssh_remote_port(ssh), |
273 |
|
|
extra != NULL ? ": " : "", |
274 |
|
|
extra != NULL ? extra : ""); |
275 |
|
|
|
276 |
|
|
free(extra); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
void |
280 |
|
|
auth_maxtries_exceeded(Authctxt *authctxt) |
281 |
|
|
{ |
282 |
|
|
struct ssh *ssh = active_state; /* XXX */ |
283 |
|
|
|
284 |
|
|
error("maximum authentication attempts exceeded for " |
285 |
|
|
"%s%.100s from %.200s port %d ssh2", |
286 |
|
|
authctxt->valid ? "" : "invalid user ", |
287 |
|
|
authctxt->user, |
288 |
|
|
ssh_remote_ipaddr(ssh), |
289 |
|
|
ssh_remote_port(ssh)); |
290 |
|
|
packet_disconnect("Too many authentication failures"); |
291 |
|
|
/* NOTREACHED */ |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
/* |
295 |
|
|
* Check whether root logins are disallowed. |
296 |
|
|
*/ |
297 |
|
|
int |
298 |
|
|
auth_root_allowed(const char *method) |
299 |
|
|
{ |
300 |
|
|
struct ssh *ssh = active_state; /* XXX */ |
301 |
|
|
|
302 |
|
|
switch (options.permit_root_login) { |
303 |
|
|
case PERMIT_YES: |
304 |
|
|
return 1; |
305 |
|
|
case PERMIT_NO_PASSWD: |
306 |
|
|
if (strcmp(method, "publickey") == 0 || |
307 |
|
|
strcmp(method, "hostbased") == 0 || |
308 |
|
|
strcmp(method, "gssapi-with-mic") == 0) |
309 |
|
|
return 1; |
310 |
|
|
break; |
311 |
|
|
case PERMIT_FORCED_ONLY: |
312 |
|
|
if (forced_command) { |
313 |
|
|
logit("Root login accepted for forced command."); |
314 |
|
|
return 1; |
315 |
|
|
} |
316 |
|
|
break; |
317 |
|
|
} |
318 |
|
|
logit("ROOT LOGIN REFUSED FROM %.200s port %d", |
319 |
|
|
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
320 |
|
|
return 0; |
321 |
|
|
} |
322 |
|
|
|
323 |
|
|
|
324 |
|
|
/* |
325 |
|
|
* Given a template and a passwd structure, build a filename |
326 |
|
|
* by substituting % tokenised options. Currently, %% becomes '%', |
327 |
|
|
* %h becomes the home directory and %u the username. |
328 |
|
|
* |
329 |
|
|
* This returns a buffer allocated by xmalloc. |
330 |
|
|
*/ |
331 |
|
|
char * |
332 |
|
|
expand_authorized_keys(const char *filename, struct passwd *pw) |
333 |
|
|
{ |
334 |
|
|
char *file, ret[PATH_MAX]; |
335 |
|
|
int i; |
336 |
|
|
|
337 |
|
|
file = percent_expand(filename, "h", pw->pw_dir, |
338 |
|
|
"u", pw->pw_name, (char *)NULL); |
339 |
|
|
|
340 |
|
|
/* |
341 |
|
|
* Ensure that filename starts anchored. If not, be backward |
342 |
|
|
* compatible and prepend the '%h/' |
343 |
|
|
*/ |
344 |
|
|
if (*file == '/') |
345 |
|
|
return (file); |
346 |
|
|
|
347 |
|
|
i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); |
348 |
|
|
if (i < 0 || (size_t)i >= sizeof(ret)) |
349 |
|
|
fatal("expand_authorized_keys: path too long"); |
350 |
|
|
free(file); |
351 |
|
|
return (xstrdup(ret)); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
char * |
355 |
|
|
authorized_principals_file(struct passwd *pw) |
356 |
|
|
{ |
357 |
|
|
if (options.authorized_principals_file == NULL) |
358 |
|
|
return NULL; |
359 |
|
|
return expand_authorized_keys(options.authorized_principals_file, pw); |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
/* return ok if key exists in sysfile or userfile */ |
363 |
|
|
HostStatus |
364 |
|
|
check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host, |
365 |
|
|
const char *sysfile, const char *userfile) |
366 |
|
|
{ |
367 |
|
|
char *user_hostfile; |
368 |
|
|
struct stat st; |
369 |
|
|
HostStatus host_status; |
370 |
|
|
struct hostkeys *hostkeys; |
371 |
|
|
const struct hostkey_entry *found; |
372 |
|
|
|
373 |
|
|
hostkeys = init_hostkeys(); |
374 |
|
|
load_hostkeys(hostkeys, host, sysfile); |
375 |
|
|
if (userfile != NULL) { |
376 |
|
|
user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); |
377 |
|
|
if (options.strict_modes && |
378 |
|
|
(stat(user_hostfile, &st) == 0) && |
379 |
|
|
((st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
380 |
|
|
(st.st_mode & 022) != 0)) { |
381 |
|
|
logit("Authentication refused for %.100s: " |
382 |
|
|
"bad owner or modes for %.200s", |
383 |
|
|
pw->pw_name, user_hostfile); |
384 |
|
|
auth_debug_add("Ignored %.200s: bad ownership or modes", |
385 |
|
|
user_hostfile); |
386 |
|
|
} else { |
387 |
|
|
temporarily_use_uid(pw); |
388 |
|
|
load_hostkeys(hostkeys, host, user_hostfile); |
389 |
|
|
restore_uid(); |
390 |
|
|
} |
391 |
|
|
free(user_hostfile); |
392 |
|
|
} |
393 |
|
|
host_status = check_key_in_hostkeys(hostkeys, key, &found); |
394 |
|
|
if (host_status == HOST_REVOKED) |
395 |
|
|
error("WARNING: revoked key for %s attempted authentication", |
396 |
|
|
found->host); |
397 |
|
|
else if (host_status == HOST_OK) |
398 |
|
|
debug("%s: key for %s found at %s:%ld", __func__, |
399 |
|
|
found->host, found->file, found->line); |
400 |
|
|
else |
401 |
|
|
debug("%s: key for host %s not found", __func__, host); |
402 |
|
|
|
403 |
|
|
free_hostkeys(hostkeys); |
404 |
|
|
|
405 |
|
|
return host_status; |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
static FILE * |
409 |
|
|
auth_openfile(const char *file, struct passwd *pw, int strict_modes, |
410 |
|
|
int log_missing, char *file_type) |
411 |
|
|
{ |
412 |
|
|
char line[1024]; |
413 |
|
|
struct stat st; |
414 |
|
|
int fd; |
415 |
|
|
FILE *f; |
416 |
|
|
|
417 |
|
|
if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { |
418 |
|
|
if (log_missing || errno != ENOENT) |
419 |
|
|
debug("Could not open %s '%s': %s", file_type, file, |
420 |
|
|
strerror(errno)); |
421 |
|
|
return NULL; |
422 |
|
|
} |
423 |
|
|
|
424 |
|
|
if (fstat(fd, &st) < 0) { |
425 |
|
|
close(fd); |
426 |
|
|
return NULL; |
427 |
|
|
} |
428 |
|
|
if (!S_ISREG(st.st_mode)) { |
429 |
|
|
logit("User %s %s %s is not a regular file", |
430 |
|
|
pw->pw_name, file_type, file); |
431 |
|
|
close(fd); |
432 |
|
|
return NULL; |
433 |
|
|
} |
434 |
|
|
unset_nonblock(fd); |
435 |
|
|
if ((f = fdopen(fd, "r")) == NULL) { |
436 |
|
|
close(fd); |
437 |
|
|
return NULL; |
438 |
|
|
} |
439 |
|
|
if (strict_modes && |
440 |
|
|
safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) { |
441 |
|
|
fclose(f); |
442 |
|
|
logit("Authentication refused: %s", line); |
443 |
|
|
auth_debug_add("Ignored %s: %s", file_type, line); |
444 |
|
|
return NULL; |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
return f; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
|
451 |
|
|
FILE * |
452 |
|
|
auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) |
453 |
|
|
{ |
454 |
|
|
return auth_openfile(file, pw, strict_modes, 1, "authorized keys"); |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
FILE * |
458 |
|
|
auth_openprincipals(const char *file, struct passwd *pw, int strict_modes) |
459 |
|
|
{ |
460 |
|
|
return auth_openfile(file, pw, strict_modes, 0, |
461 |
|
|
"authorized principals"); |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
struct passwd * |
465 |
|
|
getpwnamallow(const char *user) |
466 |
|
|
{ |
467 |
|
|
struct ssh *ssh = active_state; /* XXX */ |
468 |
|
|
extern login_cap_t *lc; |
469 |
|
|
auth_session_t *as; |
470 |
|
|
struct passwd *pw; |
471 |
|
|
struct connection_info *ci = get_connection_info(1, options.use_dns); |
472 |
|
|
|
473 |
|
|
ci->user = user; |
474 |
|
|
parse_server_match_config(&options, ci); |
475 |
|
|
log_change_level(options.log_level); |
476 |
|
|
process_permitopen(ssh, &options); |
477 |
|
|
|
478 |
|
|
pw = getpwnam(user); |
479 |
|
|
if (pw == NULL) { |
480 |
|
|
logit("Invalid user %.100s from %.100s port %d", |
481 |
|
|
user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
482 |
|
|
return (NULL); |
483 |
|
|
} |
484 |
|
|
if (!allowed_user(pw)) |
485 |
|
|
return (NULL); |
486 |
|
|
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
487 |
|
|
debug("unable to get login class: %s", user); |
488 |
|
|
return (NULL); |
489 |
|
|
} |
490 |
|
|
if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || |
491 |
|
|
auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { |
492 |
|
|
debug("Approval failure for %s", user); |
493 |
|
|
pw = NULL; |
494 |
|
|
} |
495 |
|
|
if (as != NULL) |
496 |
|
|
auth_close(as); |
497 |
|
|
if (pw != NULL) |
498 |
|
|
return (pwcopy(pw)); |
499 |
|
|
return (NULL); |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ |
503 |
|
|
int |
504 |
|
|
auth_key_is_revoked(struct sshkey *key) |
505 |
|
|
{ |
506 |
|
|
char *fp = NULL; |
507 |
|
|
int r; |
508 |
|
|
|
509 |
|
|
if (options.revoked_keys_file == NULL) |
510 |
|
|
return 0; |
511 |
|
|
if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, |
512 |
|
|
SSH_FP_DEFAULT)) == NULL) { |
513 |
|
|
r = SSH_ERR_ALLOC_FAIL; |
514 |
|
|
error("%s: fingerprint key: %s", __func__, ssh_err(r)); |
515 |
|
|
goto out; |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
r = sshkey_check_revoked(key, options.revoked_keys_file); |
519 |
|
|
switch (r) { |
520 |
|
|
case 0: |
521 |
|
|
break; /* not revoked */ |
522 |
|
|
case SSH_ERR_KEY_REVOKED: |
523 |
|
|
error("Authentication key %s %s revoked by file %s", |
524 |
|
|
sshkey_type(key), fp, options.revoked_keys_file); |
525 |
|
|
goto out; |
526 |
|
|
default: |
527 |
|
|
error("Error checking authentication key %s %s in " |
528 |
|
|
"revoked keys file %s: %s", sshkey_type(key), fp, |
529 |
|
|
options.revoked_keys_file, ssh_err(r)); |
530 |
|
|
goto out; |
531 |
|
|
} |
532 |
|
|
|
533 |
|
|
/* Success */ |
534 |
|
|
r = 0; |
535 |
|
|
|
536 |
|
|
out: |
537 |
|
|
free(fp); |
538 |
|
|
return r == 0 ? 0 : 1; |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
void |
542 |
|
|
auth_debug_add(const char *fmt,...) |
543 |
|
|
{ |
544 |
|
|
char buf[1024]; |
545 |
|
|
va_list args; |
546 |
|
|
|
547 |
|
|
if (!auth_debug_init) |
548 |
|
|
return; |
549 |
|
|
|
550 |
|
|
va_start(args, fmt); |
551 |
|
|
vsnprintf(buf, sizeof(buf), fmt, args); |
552 |
|
|
va_end(args); |
553 |
|
|
buffer_put_cstring(&auth_debug, buf); |
554 |
|
|
} |
555 |
|
|
|
556 |
|
|
void |
557 |
|
|
auth_debug_send(void) |
558 |
|
|
{ |
559 |
|
|
char *msg; |
560 |
|
|
|
561 |
|
|
if (!auth_debug_init) |
562 |
|
|
return; |
563 |
|
|
while (buffer_len(&auth_debug)) { |
564 |
|
|
msg = buffer_get_string(&auth_debug, NULL); |
565 |
|
|
packet_send_debug("%s", msg); |
566 |
|
|
free(msg); |
567 |
|
|
} |
568 |
|
|
} |
569 |
|
|
|
570 |
|
|
void |
571 |
|
|
auth_debug_reset(void) |
572 |
|
|
{ |
573 |
|
|
if (auth_debug_init) |
574 |
|
|
buffer_clear(&auth_debug); |
575 |
|
|
else { |
576 |
|
|
buffer_init(&auth_debug); |
577 |
|
|
auth_debug_init = 1; |
578 |
|
|
} |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
struct passwd * |
582 |
|
|
fakepw(void) |
583 |
|
|
{ |
584 |
|
|
static struct passwd fake; |
585 |
|
|
|
586 |
|
|
memset(&fake, 0, sizeof(fake)); |
587 |
|
|
fake.pw_name = "NOUSER"; |
588 |
|
|
fake.pw_passwd = |
589 |
|
|
"$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK"; |
590 |
|
|
fake.pw_gecos = "NOUSER"; |
591 |
|
|
fake.pw_uid = (uid_t)-1; |
592 |
|
|
fake.pw_gid = (gid_t)-1; |
593 |
|
|
fake.pw_class = ""; |
594 |
|
|
fake.pw_dir = "/nonexist"; |
595 |
|
|
fake.pw_shell = "/nonexist"; |
596 |
|
|
|
597 |
|
|
return (&fake); |
598 |
|
|
} |
599 |
|
|
|
600 |
|
|
/* |
601 |
|
|
* Returns the remote DNS hostname as a string. The returned string must not |
602 |
|
|
* be freed. NB. this will usually trigger a DNS query the first time it is |
603 |
|
|
* called. |
604 |
|
|
* This function does additional checks on the hostname to mitigate some |
605 |
|
|
* attacks on legacy rhosts-style authentication. |
606 |
|
|
* XXX is RhostsRSAAuthentication vulnerable to these? |
607 |
|
|
* XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) |
608 |
|
|
*/ |
609 |
|
|
|
610 |
|
|
static char * |
611 |
|
|
remote_hostname(struct ssh *ssh) |
612 |
|
|
{ |
613 |
|
|
struct sockaddr_storage from; |
614 |
|
|
socklen_t fromlen; |
615 |
|
|
struct addrinfo hints, *ai, *aitop; |
616 |
|
|
char name[NI_MAXHOST], ntop2[NI_MAXHOST]; |
617 |
|
|
const char *ntop = ssh_remote_ipaddr(ssh); |
618 |
|
|
|
619 |
|
|
/* Get IP address of client. */ |
620 |
|
|
fromlen = sizeof(from); |
621 |
|
|
memset(&from, 0, sizeof(from)); |
622 |
|
|
if (getpeername(ssh_packet_get_connection_in(ssh), |
623 |
|
|
(struct sockaddr *)&from, &fromlen) < 0) { |
624 |
|
|
debug("getpeername failed: %.100s", strerror(errno)); |
625 |
|
|
return strdup(ntop); |
626 |
|
|
} |
627 |
|
|
|
628 |
|
|
debug3("Trying to reverse map address %.100s.", ntop); |
629 |
|
|
/* Map the IP address to a host name. */ |
630 |
|
|
if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), |
631 |
|
|
NULL, 0, NI_NAMEREQD) != 0) { |
632 |
|
|
/* Host name not found. Use ip address. */ |
633 |
|
|
return strdup(ntop); |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
/* |
637 |
|
|
* if reverse lookup result looks like a numeric hostname, |
638 |
|
|
* someone is trying to trick us by PTR record like following: |
639 |
|
|
* 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 |
640 |
|
|
*/ |
641 |
|
|
memset(&hints, 0, sizeof(hints)); |
642 |
|
|
hints.ai_socktype = SOCK_DGRAM; /*dummy*/ |
643 |
|
|
hints.ai_flags = AI_NUMERICHOST; |
644 |
|
|
if (getaddrinfo(name, NULL, &hints, &ai) == 0) { |
645 |
|
|
logit("Nasty PTR record \"%s\" is set up for %s, ignoring", |
646 |
|
|
name, ntop); |
647 |
|
|
freeaddrinfo(ai); |
648 |
|
|
return strdup(ntop); |
649 |
|
|
} |
650 |
|
|
|
651 |
|
|
/* Names are stored in lowercase. */ |
652 |
|
|
lowercase(name); |
653 |
|
|
|
654 |
|
|
/* |
655 |
|
|
* Map it back to an IP address and check that the given |
656 |
|
|
* address actually is an address of this host. This is |
657 |
|
|
* necessary because anyone with access to a name server can |
658 |
|
|
* define arbitrary names for an IP address. Mapping from |
659 |
|
|
* name to IP address can be trusted better (but can still be |
660 |
|
|
* fooled if the intruder has access to the name server of |
661 |
|
|
* the domain). |
662 |
|
|
*/ |
663 |
|
|
memset(&hints, 0, sizeof(hints)); |
664 |
|
|
hints.ai_family = from.ss_family; |
665 |
|
|
hints.ai_socktype = SOCK_STREAM; |
666 |
|
|
if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { |
667 |
|
|
logit("reverse mapping checking getaddrinfo for %.700s " |
668 |
|
|
"[%s] failed.", name, ntop); |
669 |
|
|
return strdup(ntop); |
670 |
|
|
} |
671 |
|
|
/* Look for the address from the list of addresses. */ |
672 |
|
|
for (ai = aitop; ai; ai = ai->ai_next) { |
673 |
|
|
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, |
674 |
|
|
sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && |
675 |
|
|
(strcmp(ntop, ntop2) == 0)) |
676 |
|
|
break; |
677 |
|
|
} |
678 |
|
|
freeaddrinfo(aitop); |
679 |
|
|
/* If we reached the end of the list, the address was not there. */ |
680 |
|
|
if (ai == NULL) { |
681 |
|
|
/* Address not found for the host name. */ |
682 |
|
|
logit("Address %.100s maps to %.600s, but this does not " |
683 |
|
|
"map back to the address.", ntop, name); |
684 |
|
|
return strdup(ntop); |
685 |
|
|
} |
686 |
|
|
return strdup(name); |
687 |
|
|
} |
688 |
|
|
|
689 |
|
|
/* |
690 |
|
|
* Return the canonical name of the host in the other side of the current |
691 |
|
|
* connection. The host name is cached, so it is efficient to call this |
692 |
|
|
* several times. |
693 |
|
|
*/ |
694 |
|
|
|
695 |
|
|
const char * |
696 |
|
|
auth_get_canonical_hostname(struct ssh *ssh, int use_dns) |
697 |
|
|
{ |
698 |
|
|
static char *dnsname; |
699 |
|
|
|
700 |
|
|
if (!use_dns) |
701 |
|
|
return ssh_remote_ipaddr(ssh); |
702 |
|
|
else if (dnsname != NULL) |
703 |
|
|
return dnsname; |
704 |
|
|
else { |
705 |
|
|
dnsname = remote_hostname(ssh); |
706 |
|
|
return dnsname; |
707 |
|
|
} |
708 |
|
|
} |