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