]>
Commit | Line | Data |
---|---|---|
a306f2dd | 1 | /* |
2 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
3 | * All rights reserved | |
4 | */ | |
5 | ||
6 | #include "includes.h" | |
0b242b12 | 7 | RCSID("$OpenBSD: auth1.c,v 1.2 2000/04/29 18:11:52 markus Exp $"); |
a306f2dd | 8 | |
9 | #include "xmalloc.h" | |
10 | #include "rsa.h" | |
11 | #include "ssh.h" | |
12 | #include "packet.h" | |
13 | #include "buffer.h" | |
14 | #include "cipher.h" | |
15 | #include "mpaux.h" | |
16 | #include "servconf.h" | |
17 | #include "compat.h" | |
18 | #include "auth.h" | |
19 | #include "session.h" | |
20 | ||
21 | /* import */ | |
22 | extern ServerOptions options; | |
23 | extern char *forced_command; | |
24 | ||
25 | /* | |
26 | * convert ssh auth msg type into description | |
27 | */ | |
28 | char * | |
29 | get_authname(int type) | |
30 | { | |
31 | static char buf[1024]; | |
32 | switch (type) { | |
33 | case SSH_CMSG_AUTH_PASSWORD: | |
34 | return "password"; | |
35 | case SSH_CMSG_AUTH_RSA: | |
36 | return "rsa"; | |
37 | case SSH_CMSG_AUTH_RHOSTS_RSA: | |
38 | return "rhosts-rsa"; | |
39 | case SSH_CMSG_AUTH_RHOSTS: | |
40 | return "rhosts"; | |
41 | #ifdef KRB4 | |
42 | case SSH_CMSG_AUTH_KERBEROS: | |
43 | return "kerberos"; | |
44 | #endif | |
45 | #ifdef SKEY | |
46 | case SSH_CMSG_AUTH_TIS_RESPONSE: | |
47 | return "s/key"; | |
48 | #endif | |
49 | } | |
50 | snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); | |
51 | return buf; | |
52 | } | |
53 | ||
54 | /* | |
55 | * The user does not exist or access is denied, | |
56 | * but fake indication that authentication is needed. | |
57 | */ | |
58 | void | |
59 | do_fake_authloop1(char *user) | |
60 | { | |
61 | int attempt = 0; | |
62 | ||
63 | log("Faking authloop for illegal user %.200s from %.200s port %d", | |
64 | user, | |
65 | get_remote_ipaddr(), | |
66 | get_remote_port()); | |
67 | ||
68 | #ifdef WITH_AIXAUTHENTICATE | |
69 | if (strncmp(get_authname(type),"password", | |
70 | strlen(get_authname(type))) == 0) | |
71 | loginfailed(pw->pw_name,get_canonical_hostname(),"ssh"); | |
72 | #endif /* WITH_AIXAUTHENTICATE */ | |
73 | ||
74 | /* Indicate that authentication is needed. */ | |
75 | packet_start(SSH_SMSG_FAILURE); | |
76 | packet_send(); | |
77 | packet_write_wait(); | |
78 | ||
79 | /* | |
80 | * Keep reading packets, and always respond with a failure. This is | |
81 | * to avoid disclosing whether such a user really exists. | |
82 | */ | |
83 | for (attempt = 1;; attempt++) { | |
84 | /* Read a packet. This will not return if the client disconnects. */ | |
85 | int plen; | |
86 | #ifndef SKEY | |
87 | (void)packet_read(&plen); | |
88 | #else /* SKEY */ | |
89 | int type = packet_read(&plen); | |
90 | unsigned int dlen; | |
91 | char *password, *skeyinfo; | |
92 | password = NULL; | |
93 | /* Try to send a fake s/key challenge. */ | |
94 | if (options.skey_authentication == 1 && | |
95 | (skeyinfo = skey_fake_keyinfo(user)) != NULL) { | |
96 | if (type == SSH_CMSG_AUTH_TIS) { | |
97 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | |
98 | packet_put_string(skeyinfo, strlen(skeyinfo)); | |
99 | packet_send(); | |
100 | packet_write_wait(); | |
101 | continue; | |
102 | } else if (type == SSH_CMSG_AUTH_PASSWORD && | |
103 | options.password_authentication && | |
104 | (password = packet_get_string(&dlen)) != NULL && | |
105 | dlen == 5 && | |
106 | strncasecmp(password, "s/key", 5) == 0 ) { | |
107 | packet_send_debug(skeyinfo); | |
108 | } | |
109 | } | |
110 | if (password != NULL) | |
111 | xfree(password); | |
112 | #endif | |
113 | if (attempt > AUTH_FAIL_MAX) | |
114 | packet_disconnect(AUTH_FAIL_MSG, user); | |
115 | ||
116 | /* | |
117 | * Send failure. This should be indistinguishable from a | |
118 | * failed authentication. | |
119 | */ | |
120 | packet_start(SSH_SMSG_FAILURE); | |
121 | packet_send(); | |
122 | packet_write_wait(); | |
123 | } | |
124 | /* NOTREACHED */ | |
125 | abort(); | |
126 | } | |
127 | ||
128 | /* | |
129 | * read packets and try to authenticate local user *pw. | |
130 | * return if authentication is successfull | |
131 | */ | |
132 | void | |
133 | do_authloop(struct passwd * pw) | |
134 | { | |
135 | int attempt = 0; | |
136 | unsigned int bits; | |
137 | RSA *client_host_key; | |
138 | BIGNUM *n; | |
139 | char *client_user = NULL, *password = NULL; | |
140 | char user[1024]; | |
141 | unsigned int dlen; | |
142 | int plen, nlen, elen; | |
143 | unsigned int ulen; | |
144 | int type = 0; | |
145 | void (*authlog) (const char *fmt,...) = verbose; | |
146 | ||
147 | /* Indicate that authentication is needed. */ | |
148 | packet_start(SSH_SMSG_FAILURE); | |
149 | packet_send(); | |
150 | packet_write_wait(); | |
151 | ||
152 | for (attempt = 1;; attempt++) { | |
153 | int authenticated = 0; | |
154 | strlcpy(user, "", sizeof user); | |
155 | ||
156 | /* Get a packet from the client. */ | |
157 | type = packet_read(&plen); | |
158 | ||
159 | /* Process the packet. */ | |
160 | switch (type) { | |
161 | #ifdef AFS | |
162 | case SSH_CMSG_HAVE_KERBEROS_TGT: | |
163 | if (!options.kerberos_tgt_passing) { | |
164 | /* packet_get_all(); */ | |
165 | verbose("Kerberos tgt passing disabled."); | |
166 | break; | |
167 | } else { | |
168 | /* Accept Kerberos tgt. */ | |
169 | char *tgt = packet_get_string(&dlen); | |
170 | packet_integrity_check(plen, 4 + dlen, type); | |
171 | if (!auth_kerberos_tgt(pw, tgt)) | |
172 | verbose("Kerberos tgt REFUSED for %s", pw->pw_name); | |
173 | xfree(tgt); | |
174 | } | |
175 | continue; | |
176 | ||
177 | case SSH_CMSG_HAVE_AFS_TOKEN: | |
178 | if (!options.afs_token_passing || !k_hasafs()) { | |
179 | /* packet_get_all(); */ | |
180 | verbose("AFS token passing disabled."); | |
181 | break; | |
182 | } else { | |
183 | /* Accept AFS token. */ | |
184 | char *token_string = packet_get_string(&dlen); | |
185 | packet_integrity_check(plen, 4 + dlen, type); | |
186 | if (!auth_afs_token(pw, token_string)) | |
187 | verbose("AFS token REFUSED for %s", pw->pw_name); | |
188 | xfree(token_string); | |
189 | } | |
190 | continue; | |
191 | #endif /* AFS */ | |
192 | #ifdef KRB4 | |
193 | case SSH_CMSG_AUTH_KERBEROS: | |
194 | if (!options.kerberos_authentication) { | |
195 | /* packet_get_all(); */ | |
196 | verbose("Kerberos authentication disabled."); | |
197 | break; | |
198 | } else { | |
199 | /* Try Kerberos v4 authentication. */ | |
200 | KTEXT_ST auth; | |
201 | char *tkt_user = NULL; | |
202 | char *kdata = packet_get_string((unsigned int *) &auth.length); | |
203 | packet_integrity_check(plen, 4 + auth.length, type); | |
204 | ||
205 | if (auth.length < MAX_KTXT_LEN) | |
206 | memcpy(auth.dat, kdata, auth.length); | |
207 | xfree(kdata); | |
208 | ||
209 | authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); | |
210 | ||
211 | if (authenticated) { | |
212 | snprintf(user, sizeof user, " tktuser %s", tkt_user); | |
213 | xfree(tkt_user); | |
214 | } | |
215 | } | |
216 | break; | |
217 | #endif /* KRB4 */ | |
218 | ||
219 | case SSH_CMSG_AUTH_RHOSTS: | |
220 | if (!options.rhosts_authentication) { | |
221 | verbose("Rhosts authentication disabled."); | |
222 | break; | |
223 | } | |
224 | /* | |
225 | * Get client user name. Note that we just have to | |
226 | * trust the client; this is one reason why rhosts | |
227 | * authentication is insecure. (Another is | |
228 | * IP-spoofing on a local network.) | |
229 | */ | |
230 | client_user = packet_get_string(&ulen); | |
231 | packet_integrity_check(plen, 4 + ulen, type); | |
232 | ||
233 | /* Try to authenticate using /etc/hosts.equiv and | |
234 | .rhosts. */ | |
235 | authenticated = auth_rhosts(pw, client_user); | |
236 | ||
237 | snprintf(user, sizeof user, " ruser %s", client_user); | |
238 | break; | |
239 | ||
240 | case SSH_CMSG_AUTH_RHOSTS_RSA: | |
241 | if (!options.rhosts_rsa_authentication) { | |
242 | verbose("Rhosts with RSA authentication disabled."); | |
243 | break; | |
244 | } | |
245 | /* | |
246 | * Get client user name. Note that we just have to | |
247 | * trust the client; root on the client machine can | |
248 | * claim to be any user. | |
249 | */ | |
250 | client_user = packet_get_string(&ulen); | |
251 | ||
252 | /* Get the client host key. */ | |
253 | client_host_key = RSA_new(); | |
254 | if (client_host_key == NULL) | |
255 | fatal("RSA_new failed"); | |
256 | client_host_key->e = BN_new(); | |
257 | client_host_key->n = BN_new(); | |
258 | if (client_host_key->e == NULL || client_host_key->n == NULL) | |
259 | fatal("BN_new failed"); | |
260 | bits = packet_get_int(); | |
261 | packet_get_bignum(client_host_key->e, &elen); | |
262 | packet_get_bignum(client_host_key->n, &nlen); | |
263 | ||
264 | if (bits != BN_num_bits(client_host_key->n)) | |
0b242b12 | 265 | log("Warning: keysize mismatch for client_host_key: " |
266 | "actual %d, announced %d", BN_num_bits(client_host_key->n), bits); | |
a306f2dd | 267 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); |
268 | ||
269 | authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); | |
270 | RSA_free(client_host_key); | |
271 | ||
272 | snprintf(user, sizeof user, " ruser %s", client_user); | |
273 | break; | |
274 | ||
275 | case SSH_CMSG_AUTH_RSA: | |
276 | if (!options.rsa_authentication) { | |
277 | verbose("RSA authentication disabled."); | |
278 | break; | |
279 | } | |
280 | /* RSA authentication requested. */ | |
281 | n = BN_new(); | |
282 | packet_get_bignum(n, &nlen); | |
283 | packet_integrity_check(plen, nlen, type); | |
284 | authenticated = auth_rsa(pw, n); | |
285 | BN_clear_free(n); | |
286 | break; | |
287 | ||
288 | case SSH_CMSG_AUTH_PASSWORD: | |
289 | if (!options.password_authentication) { | |
290 | verbose("Password authentication disabled."); | |
291 | break; | |
292 | } | |
293 | /* | |
294 | * Read user password. It is in plain text, but was | |
295 | * transmitted over the encrypted channel so it is | |
296 | * not visible to an outside observer. | |
297 | */ | |
298 | password = packet_get_string(&dlen); | |
299 | packet_integrity_check(plen, 4 + dlen, type); | |
300 | ||
301 | #ifdef USE_PAM | |
302 | /* Do PAM auth with password */ | |
303 | authenticated = auth_pam_password(pw, password); | |
304 | #else /* USE_PAM */ | |
305 | /* Try authentication with the password. */ | |
306 | authenticated = auth_password(pw, password); | |
307 | #endif /* USE_PAM */ | |
308 | ||
309 | memset(password, 0, strlen(password)); | |
310 | xfree(password); | |
311 | break; | |
312 | ||
313 | #ifdef SKEY | |
314 | case SSH_CMSG_AUTH_TIS: | |
315 | debug("rcvd SSH_CMSG_AUTH_TIS"); | |
316 | if (options.skey_authentication == 1) { | |
317 | char *skeyinfo = skey_keyinfo(pw->pw_name); | |
318 | if (skeyinfo == NULL) { | |
319 | debug("generating fake skeyinfo for %.100s.", pw->pw_name); | |
320 | skeyinfo = skey_fake_keyinfo(pw->pw_name); | |
321 | } | |
322 | if (skeyinfo != NULL) { | |
323 | /* we send our s/key- in tis-challenge messages */ | |
324 | debug("sending challenge '%s'", skeyinfo); | |
325 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | |
326 | packet_put_string(skeyinfo, strlen(skeyinfo)); | |
327 | packet_send(); | |
328 | packet_write_wait(); | |
329 | continue; | |
330 | } | |
331 | } | |
332 | break; | |
333 | case SSH_CMSG_AUTH_TIS_RESPONSE: | |
334 | debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); | |
335 | if (options.skey_authentication == 1) { | |
336 | char *response = packet_get_string(&dlen); | |
337 | debug("skey response == '%s'", response); | |
338 | packet_integrity_check(plen, 4 + dlen, type); | |
339 | authenticated = (skey_haskey(pw->pw_name) == 0 && | |
340 | skey_passcheck(pw->pw_name, response) != -1); | |
341 | xfree(response); | |
342 | } | |
343 | break; | |
344 | #else | |
345 | case SSH_CMSG_AUTH_TIS: | |
346 | /* TIS Authentication is unsupported */ | |
347 | log("TIS authentication unsupported."); | |
348 | break; | |
349 | #endif | |
350 | ||
351 | default: | |
352 | /* | |
353 | * Any unknown messages will be ignored (and failure | |
354 | * returned) during authentication. | |
355 | */ | |
356 | log("Unknown message during authentication: type %d", type); | |
357 | break; | |
358 | } | |
359 | ||
360 | /* | |
361 | * Check if the user is logging in as root and root logins | |
362 | * are disallowed. | |
363 | * Note that root login is allowed for forced commands. | |
364 | */ | |
365 | if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) { | |
366 | if (forced_command) { | |
367 | log("Root login accepted for forced command."); | |
368 | } else { | |
369 | authenticated = 0; | |
370 | log("ROOT LOGIN REFUSED FROM %.200s", | |
371 | get_canonical_hostname()); | |
372 | } | |
373 | } | |
374 | ||
375 | /* Raise logging level */ | |
376 | if (authenticated || | |
377 | attempt == AUTH_FAIL_LOG || | |
378 | type == SSH_CMSG_AUTH_PASSWORD) | |
379 | authlog = log; | |
380 | ||
381 | authlog("%s %s for %.200s from %.200s port %d%s", | |
382 | authenticated ? "Accepted" : "Failed", | |
383 | get_authname(type), | |
384 | pw->pw_uid == 0 ? "ROOT" : pw->pw_name, | |
385 | get_remote_ipaddr(), | |
386 | get_remote_port(), | |
387 | user); | |
388 | ||
389 | #ifdef USE_PAM | |
390 | if (authenticated) { | |
391 | if (!do_pam_account(pw->pw_name, client_user)) { | |
392 | if (client_user != NULL) { | |
393 | xfree(client_user); | |
394 | client_user = NULL; | |
395 | } | |
396 | do_fake_authloop1(pw->pw_name); | |
397 | } | |
398 | return; | |
399 | } | |
400 | #else /* USE_PAM */ | |
401 | if (authenticated) { | |
402 | return; | |
403 | } | |
404 | #endif /* USE_PAM */ | |
405 | ||
406 | if (client_user != NULL) { | |
407 | xfree(client_user); | |
408 | client_user = NULL; | |
409 | } | |
410 | ||
411 | if (attempt > AUTH_FAIL_MAX) | |
412 | packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); | |
413 | ||
414 | /* Send a message indicating that the authentication attempt failed. */ | |
415 | packet_start(SSH_SMSG_FAILURE); | |
416 | packet_send(); | |
417 | packet_write_wait(); | |
418 | } | |
419 | } | |
420 | ||
421 | /* | |
422 | * Performs authentication of an incoming connection. Session key has already | |
423 | * been exchanged and encryption is enabled. | |
424 | */ | |
425 | void | |
426 | do_authentication() | |
427 | { | |
428 | struct passwd *pw, pwcopy; | |
429 | int plen; | |
430 | unsigned int ulen; | |
431 | char *user; | |
432 | #ifdef WITH_AIXAUTHENTICATE | |
433 | char *loginmsg; | |
434 | #endif /* WITH_AIXAUTHENTICATE */ | |
435 | ||
436 | /* Get the name of the user that we wish to log in as. */ | |
437 | packet_read_expect(&plen, SSH_CMSG_USER); | |
438 | ||
439 | /* Get the user name. */ | |
440 | user = packet_get_string(&ulen); | |
441 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); | |
442 | ||
443 | setproctitle("%s", user); | |
444 | ||
445 | #ifdef AFS | |
446 | /* If machine has AFS, set process authentication group. */ | |
447 | if (k_hasafs()) { | |
448 | k_setpag(); | |
449 | k_unlog(); | |
450 | } | |
451 | #endif /* AFS */ | |
452 | ||
453 | /* Verify that the user is a valid user. */ | |
454 | pw = getpwnam(user); | |
455 | if (!pw || !allowed_user(pw)) | |
456 | do_fake_authloop1(user); | |
457 | xfree(user); | |
458 | ||
459 | /* Take a copy of the returned structure. */ | |
460 | memset(&pwcopy, 0, sizeof(pwcopy)); | |
461 | pwcopy.pw_name = xstrdup(pw->pw_name); | |
462 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); | |
463 | pwcopy.pw_uid = pw->pw_uid; | |
464 | pwcopy.pw_gid = pw->pw_gid; | |
465 | pwcopy.pw_dir = xstrdup(pw->pw_dir); | |
466 | pwcopy.pw_shell = xstrdup(pw->pw_shell); | |
467 | pw = &pwcopy; | |
468 | ||
469 | #ifdef USE_PAM | |
470 | start_pam(pw); | |
471 | #endif | |
472 | ||
473 | /* | |
474 | * If we are not running as root, the user must have the same uid as | |
475 | * the server. | |
476 | */ | |
477 | if (getuid() != 0 && pw->pw_uid != getuid()) | |
478 | packet_disconnect("Cannot change user when server not running as root."); | |
479 | ||
480 | debug("Attempting authentication for %.100s.", pw->pw_name); | |
481 | ||
482 | /* If the user has no password, accept authentication immediately. */ | |
483 | if (options.password_authentication && | |
484 | #ifdef KRB4 | |
485 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && | |
486 | #endif /* KRB4 */ | |
487 | #ifdef USE_PAM | |
488 | auth_pam_password(pw, "")) { | |
489 | #else /* USE_PAM */ | |
490 | auth_password(pw, "")) { | |
491 | #endif /* USE_PAM */ | |
492 | /* Authentication with empty password succeeded. */ | |
493 | log("Login for user %s from %.100s, accepted without authentication.", | |
494 | pw->pw_name, get_remote_ipaddr()); | |
495 | } else { | |
496 | /* Loop until the user has been authenticated or the | |
497 | connection is closed, do_authloop() returns only if | |
498 | authentication is successfull */ | |
499 | do_authloop(pw); | |
500 | } | |
501 | ||
502 | /* The user has been authenticated and accepted. */ | |
503 | #ifdef WITH_AIXAUTHENTICATE | |
504 | loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg); | |
505 | #endif /* WITH_AIXAUTHENTICATE */ | |
506 | packet_start(SSH_SMSG_SUCCESS); | |
507 | packet_send(); | |
508 | packet_write_wait(); | |
509 | ||
510 | /* Perform session preparation. */ | |
511 | do_authenticated(pw); | |
512 | } |