1 |
/* $OpenBSD: auth1.c,v 1.80 2014/02/02 03:44:31 djm Exp $ */ |
2 |
/* |
3 |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 |
* All rights reserved |
5 |
* |
6 |
* As far as I am concerned, the code I have written for this software |
7 |
* can be used freely for any purpose. Any derived versions of this |
8 |
* software must be clearly marked as such, and if the derived work is |
9 |
* incompatible with the protocol description in the RFC file, it must be |
10 |
* called by a name other than "ssh" or "Secure Shell". |
11 |
*/ |
12 |
|
13 |
#include "includes.h" |
14 |
|
15 |
#include <sys/types.h> |
16 |
|
17 |
#include <stdarg.h> |
18 |
#include <stdio.h> |
19 |
#include <string.h> |
20 |
#include <unistd.h> |
21 |
#include <pwd.h> |
22 |
|
23 |
#include "openbsd-compat/sys-queue.h" |
24 |
#include "xmalloc.h" |
25 |
#include "rsa.h" |
26 |
#include "ssh1.h" |
27 |
#include "packet.h" |
28 |
#include "buffer.h" |
29 |
#include "log.h" |
30 |
#include "servconf.h" |
31 |
#include "compat.h" |
32 |
#include "key.h" |
33 |
#include "hostfile.h" |
34 |
#include "auth.h" |
35 |
#include "channels.h" |
36 |
#include "session.h" |
37 |
#include "uidswap.h" |
38 |
#ifdef GSSAPI |
39 |
#include "ssh-gss.h" |
40 |
#endif |
41 |
#include "monitor_wrap.h" |
42 |
#include "buffer.h" |
43 |
|
44 |
/* import */ |
45 |
extern ServerOptions options; |
46 |
extern Buffer loginmsg; |
47 |
|
48 |
static int auth1_process_password(Authctxt *); |
49 |
static int auth1_process_rsa(Authctxt *); |
50 |
static int auth1_process_rhosts_rsa(Authctxt *); |
51 |
static int auth1_process_tis_challenge(Authctxt *); |
52 |
static int auth1_process_tis_response(Authctxt *); |
53 |
|
54 |
static char *client_user = NULL; /* Used to fill in remote user for PAM */ |
55 |
|
56 |
struct AuthMethod1 { |
57 |
int type; |
58 |
char *name; |
59 |
int *enabled; |
60 |
int (*method)(Authctxt *); |
61 |
}; |
62 |
|
63 |
const struct AuthMethod1 auth1_methods[] = { |
64 |
{ |
65 |
SSH_CMSG_AUTH_PASSWORD, "password", |
66 |
&options.password_authentication, auth1_process_password |
67 |
}, |
68 |
{ |
69 |
SSH_CMSG_AUTH_RSA, "rsa", |
70 |
&options.rsa_authentication, auth1_process_rsa |
71 |
}, |
72 |
{ |
73 |
SSH_CMSG_AUTH_RHOSTS_RSA, "rhosts-rsa", |
74 |
&options.rhosts_rsa_authentication, auth1_process_rhosts_rsa |
75 |
}, |
76 |
{ |
77 |
SSH_CMSG_AUTH_TIS, "challenge-response", |
78 |
&options.challenge_response_authentication, |
79 |
auth1_process_tis_challenge |
80 |
}, |
81 |
{ |
82 |
SSH_CMSG_AUTH_TIS_RESPONSE, "challenge-response", |
83 |
&options.challenge_response_authentication, |
84 |
auth1_process_tis_response |
85 |
}, |
86 |
{ -1, NULL, NULL, NULL} |
87 |
}; |
88 |
|
89 |
static const struct AuthMethod1 |
90 |
*lookup_authmethod1(int type) |
91 |
{ |
92 |
int i; |
93 |
|
94 |
for (i = 0; auth1_methods[i].name != NULL; i++) |
95 |
if (auth1_methods[i].type == type) |
96 |
return (&(auth1_methods[i])); |
97 |
|
98 |
return (NULL); |
99 |
} |
100 |
|
101 |
static char * |
102 |
get_authname(int type) |
103 |
{ |
104 |
const struct AuthMethod1 *a; |
105 |
static char buf[64]; |
106 |
|
107 |
if ((a = lookup_authmethod1(type)) != NULL) |
108 |
return (a->name); |
109 |
snprintf(buf, sizeof(buf), "bad-auth-msg-%d", type); |
110 |
return (buf); |
111 |
} |
112 |
|
113 |
/*ARGSUSED*/ |
114 |
static int |
115 |
auth1_process_password(Authctxt *authctxt) |
116 |
{ |
117 |
int authenticated = 0; |
118 |
char *password; |
119 |
u_int dlen; |
120 |
|
121 |
/* |
122 |
* Read user password. It is in plain text, but was |
123 |
* transmitted over the encrypted channel so it is |
124 |
* not visible to an outside observer. |
125 |
*/ |
126 |
password = packet_get_string(&dlen); |
127 |
packet_check_eom(); |
128 |
|
129 |
/* Try authentication with the password. */ |
130 |
authenticated = PRIVSEP(auth_password(authctxt, password)); |
131 |
|
132 |
explicit_bzero(password, dlen); |
133 |
free(password); |
134 |
|
135 |
return (authenticated); |
136 |
} |
137 |
|
138 |
/*ARGSUSED*/ |
139 |
static int |
140 |
auth1_process_rsa(Authctxt *authctxt) |
141 |
{ |
142 |
int authenticated = 0; |
143 |
BIGNUM *n; |
144 |
|
145 |
/* RSA authentication requested. */ |
146 |
if ((n = BN_new()) == NULL) |
147 |
fatal("do_authloop: BN_new failed"); |
148 |
packet_get_bignum(n); |
149 |
packet_check_eom(); |
150 |
authenticated = auth_rsa(authctxt, n); |
151 |
BN_clear_free(n); |
152 |
|
153 |
return (authenticated); |
154 |
} |
155 |
|
156 |
/*ARGSUSED*/ |
157 |
static int |
158 |
auth1_process_rhosts_rsa(Authctxt *authctxt) |
159 |
{ |
160 |
int keybits, authenticated = 0; |
161 |
u_int bits; |
162 |
Key *client_host_key; |
163 |
u_int ulen; |
164 |
|
165 |
/* |
166 |
* Get client user name. Note that we just have to |
167 |
* trust the client; root on the client machine can |
168 |
* claim to be any user. |
169 |
*/ |
170 |
client_user = packet_get_cstring(&ulen); |
171 |
|
172 |
/* Get the client host key. */ |
173 |
client_host_key = key_new(KEY_RSA1); |
174 |
bits = packet_get_int(); |
175 |
packet_get_bignum(client_host_key->rsa->e); |
176 |
packet_get_bignum(client_host_key->rsa->n); |
177 |
|
178 |
keybits = BN_num_bits(client_host_key->rsa->n); |
179 |
if (keybits < 0 || bits != (u_int)keybits) { |
180 |
verbose("Warning: keysize mismatch for client_host_key: " |
181 |
"actual %d, announced %d", |
182 |
BN_num_bits(client_host_key->rsa->n), bits); |
183 |
} |
184 |
packet_check_eom(); |
185 |
|
186 |
authenticated = auth_rhosts_rsa(authctxt, client_user, |
187 |
client_host_key); |
188 |
key_free(client_host_key); |
189 |
|
190 |
auth_info(authctxt, "ruser %.100s", client_user); |
191 |
|
192 |
return (authenticated); |
193 |
} |
194 |
|
195 |
/*ARGSUSED*/ |
196 |
static int |
197 |
auth1_process_tis_challenge(Authctxt *authctxt) |
198 |
{ |
199 |
char *challenge; |
200 |
|
201 |
if ((challenge = get_challenge(authctxt)) == NULL) |
202 |
return (0); |
203 |
|
204 |
debug("sending challenge '%s'", challenge); |
205 |
packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); |
206 |
packet_put_cstring(challenge); |
207 |
free(challenge); |
208 |
packet_send(); |
209 |
packet_write_wait(); |
210 |
|
211 |
return (-1); |
212 |
} |
213 |
|
214 |
/*ARGSUSED*/ |
215 |
static int |
216 |
auth1_process_tis_response(Authctxt *authctxt) |
217 |
{ |
218 |
int authenticated = 0; |
219 |
char *response; |
220 |
u_int dlen; |
221 |
|
222 |
response = packet_get_string(&dlen); |
223 |
packet_check_eom(); |
224 |
authenticated = verify_response(authctxt, response); |
225 |
explicit_bzero(response, dlen); |
226 |
free(response); |
227 |
|
228 |
return (authenticated); |
229 |
} |
230 |
|
231 |
/* |
232 |
* read packets, try to authenticate the user and |
233 |
* return only if authentication is successful |
234 |
*/ |
235 |
static void |
236 |
do_authloop(Authctxt *authctxt) |
237 |
{ |
238 |
int authenticated = 0; |
239 |
int prev = 0, type = 0; |
240 |
const struct AuthMethod1 *meth; |
241 |
|
242 |
debug("Attempting authentication for %s%.100s.", |
243 |
authctxt->valid ? "" : "invalid user ", authctxt->user); |
244 |
|
245 |
/* If the user has no password, accept authentication immediately. */ |
246 |
if (options.permit_empty_passwd && options.password_authentication && |
247 |
#ifdef KRB5 |
248 |
(!options.kerberos_authentication || options.kerberos_or_local_passwd) && |
249 |
#endif |
250 |
PRIVSEP(auth_password(authctxt, ""))) { |
251 |
#ifdef USE_PAM |
252 |
if (options.use_pam && (PRIVSEP(do_pam_account()))) |
253 |
#endif |
254 |
{ |
255 |
auth_log(authctxt, 1, 0, "without authentication", |
256 |
NULL); |
257 |
return; |
258 |
} |
259 |
} |
260 |
|
261 |
/* Indicate that authentication is needed. */ |
262 |
packet_start(SSH_SMSG_FAILURE); |
263 |
packet_send(); |
264 |
packet_write_wait(); |
265 |
|
266 |
for (;;) { |
267 |
/* default to fail */ |
268 |
authenticated = 0; |
269 |
|
270 |
|
271 |
/* Get a packet from the client. */ |
272 |
prev = type; |
273 |
type = packet_read(); |
274 |
|
275 |
/* |
276 |
* If we started challenge-response authentication but the |
277 |
* next packet is not a response to our challenge, release |
278 |
* the resources allocated by get_challenge() (which would |
279 |
* normally have been released by verify_response() had we |
280 |
* received such a response) |
281 |
*/ |
282 |
if (prev == SSH_CMSG_AUTH_TIS && |
283 |
type != SSH_CMSG_AUTH_TIS_RESPONSE) |
284 |
abandon_challenge_response(authctxt); |
285 |
|
286 |
if (authctxt->failures >= options.max_authtries) |
287 |
goto skip; |
288 |
if ((meth = lookup_authmethod1(type)) == NULL) { |
289 |
logit("Unknown message during authentication: " |
290 |
"type %d", type); |
291 |
goto skip; |
292 |
} |
293 |
|
294 |
if (!*(meth->enabled)) { |
295 |
verbose("%s authentication disabled.", meth->name); |
296 |
goto skip; |
297 |
} |
298 |
|
299 |
authenticated = meth->method(authctxt); |
300 |
if (authenticated == -1) |
301 |
continue; /* "postponed" */ |
302 |
|
303 |
#ifdef BSD_AUTH |
304 |
if (authctxt->as) { |
305 |
auth_close(authctxt->as); |
306 |
authctxt->as = NULL; |
307 |
} |
308 |
#endif |
309 |
if (!authctxt->valid && authenticated) |
310 |
fatal("INTERNAL ERROR: authenticated invalid user %s", |
311 |
authctxt->user); |
312 |
|
313 |
#ifdef _UNICOS |
314 |
if (authenticated && cray_access_denied(authctxt->user)) { |
315 |
authenticated = 0; |
316 |
fatal("Access denied for user %s.",authctxt->user); |
317 |
} |
318 |
#endif /* _UNICOS */ |
319 |
|
320 |
#ifndef HAVE_CYGWIN |
321 |
/* Special handling for root */ |
322 |
if (authenticated && authctxt->pw->pw_uid == 0 && |
323 |
!auth_root_allowed(meth->name)) { |
324 |
authenticated = 0; |
325 |
# ifdef SSH_AUDIT_EVENTS |
326 |
PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); |
327 |
# endif |
328 |
} |
329 |
#endif |
330 |
|
331 |
#ifdef USE_PAM |
332 |
if (options.use_pam && authenticated && |
333 |
!PRIVSEP(do_pam_account())) { |
334 |
char *msg; |
335 |
size_t len; |
336 |
|
337 |
error("Access denied for user %s by PAM account " |
338 |
"configuration", authctxt->user); |
339 |
len = buffer_len(&loginmsg); |
340 |
buffer_append(&loginmsg, "\0", 1); |
341 |
msg = buffer_ptr(&loginmsg); |
342 |
/* strip trailing newlines */ |
343 |
if (len > 0) |
344 |
while (len > 0 && msg[--len] == '\n') |
345 |
msg[len] = '\0'; |
346 |
else |
347 |
msg = "Access denied."; |
348 |
packet_disconnect("%s", msg); |
349 |
} |
350 |
#endif |
351 |
|
352 |
skip: |
353 |
/* Log before sending the reply */ |
354 |
auth_log(authctxt, authenticated, 0, get_authname(type), NULL); |
355 |
|
356 |
free(client_user); |
357 |
client_user = NULL; |
358 |
|
359 |
if (authenticated) |
360 |
return; |
361 |
|
362 |
if (++authctxt->failures >= options.max_authtries) { |
363 |
#ifdef SSH_AUDIT_EVENTS |
364 |
PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); |
365 |
#endif |
366 |
packet_disconnect(AUTH_FAIL_MSG, authctxt->user); |
367 |
} |
368 |
|
369 |
packet_start(SSH_SMSG_FAILURE); |
370 |
packet_send(); |
371 |
packet_write_wait(); |
372 |
} |
373 |
} |
374 |
|
375 |
/* |
376 |
* Performs authentication of an incoming connection. Session key has already |
377 |
* been exchanged and encryption is enabled. |
378 |
*/ |
379 |
void |
380 |
do_authentication(Authctxt *authctxt) |
381 |
{ |
382 |
u_int ulen; |
383 |
char *user, *style = NULL; |
384 |
|
385 |
/* Get the name of the user that we wish to log in as. */ |
386 |
packet_read_expect(SSH_CMSG_USER); |
387 |
|
388 |
/* Get the user name. */ |
389 |
user = packet_get_cstring(&ulen); |
390 |
packet_check_eom(); |
391 |
|
392 |
if ((style = strchr(user, ':')) != NULL) |
393 |
*style++ = '\0'; |
394 |
|
395 |
authctxt->user = user; |
396 |
authctxt->style = style; |
397 |
|
398 |
/* Verify that the user is a valid user. */ |
399 |
if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL) |
400 |
authctxt->valid = 1; |
401 |
else { |
402 |
debug("do_authentication: invalid user %s", user); |
403 |
authctxt->pw = fakepw(); |
404 |
} |
405 |
|
406 |
/* Configuration may have changed as a result of Match */ |
407 |
if (options.num_auth_methods != 0) |
408 |
fatal("AuthenticationMethods is not supported with SSH " |
409 |
"protocol 1"); |
410 |
|
411 |
setproctitle("%s%s", authctxt->valid ? user : "unknown", |
412 |
use_privsep ? " [net]" : ""); |
413 |
|
414 |
#ifdef USE_PAM |
415 |
if (options.use_pam) |
416 |
PRIVSEP(start_pam(authctxt)); |
417 |
#endif |
418 |
|
419 |
/* |
420 |
* If we are not running as root, the user must have the same uid as |
421 |
* the server. |
422 |
*/ |
423 |
#ifndef HAVE_CYGWIN |
424 |
if (!use_privsep && getuid() != 0 && authctxt->pw && |
425 |
authctxt->pw->pw_uid != getuid()) |
426 |
packet_disconnect("Cannot change user when server not running as root."); |
427 |
#endif |
428 |
|
429 |
/* |
430 |
* Loop until the user has been authenticated or the connection is |
431 |
* closed, do_authloop() returns only if authentication is successful |
432 |
*/ |
433 |
do_authloop(authctxt); |
434 |
|
435 |
/* The user has been authenticated and accepted. */ |
436 |
packet_start(SSH_SMSG_SUCCESS); |
437 |
packet_send(); |
438 |
packet_write_wait(); |
439 |
} |