]>
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" | |
59c97189 | 13 | RCSID("$OpenBSD: auth1.c,v 1.11 2001/01/18 16:59:59 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"; | |
59c97189 | 59 | case SSH_CMSG_AUTH_TIS: |
60 | case SSH_CMSG_AUTH_TIS_RESPONSE: | |
61 | return "challenge-response"; | |
a306f2dd | 62 | #ifdef KRB4 |
63 | case SSH_CMSG_AUTH_KERBEROS: | |
64 | return "kerberos"; | |
a306f2dd | 65 | #endif |
66 | } | |
67 | snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); | |
68 | return buf; | |
69 | } | |
70 | ||
71 | /* | |
59c97189 | 72 | * read packets, try to authenticate the user and |
73 | * return only if authentication is successful | |
a306f2dd | 74 | */ |
75 | void | |
59c97189 | 76 | do_authloop(Authctxt *authctxt) |
a306f2dd | 77 | { |
94ec8c6b | 78 | int authenticated = 0; |
1e3b8b07 | 79 | u_int bits; |
a306f2dd | 80 | RSA *client_host_key; |
81 | BIGNUM *n; | |
94ec8c6b | 82 | char *client_user, *password; |
59c97189 | 83 | char info[1024]; |
1e3b8b07 | 84 | u_int dlen; |
a306f2dd | 85 | int plen, nlen, elen; |
1e3b8b07 | 86 | u_int ulen; |
a306f2dd | 87 | int type = 0; |
59c97189 | 88 | struct passwd *pw = authctxt->pw; |
89 | ||
90 | debug("Attempting authentication for %s%.100s.", | |
91 | authctxt->valid ? "" : "illegal user ", authctxt->user); | |
92 | ||
93 | /* If the user has no password, accept authentication immediately. */ | |
94 | if (options.password_authentication && | |
95 | #ifdef KRB4 | |
96 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && | |
97 | #endif | |
f4ebf0e8 | 98 | #ifdef USE_PAM |
59c97189 | 99 | auth_pam_password(pw, password)) { |
100 | #else | |
101 | auth_password(pw, "")) { | |
102 | #endif | |
103 | auth_log(authctxt, 1, "without authentication", ""); | |
104 | return; | |
105 | } | |
a306f2dd | 106 | |
107 | /* Indicate that authentication is needed. */ | |
108 | packet_start(SSH_SMSG_FAILURE); | |
109 | packet_send(); | |
110 | packet_write_wait(); | |
111 | ||
94ec8c6b | 112 | client_user = NULL; |
113 | ||
59c97189 | 114 | for (;;) { |
94ec8c6b | 115 | /* default to fail */ |
116 | authenticated = 0; | |
117 | ||
59c97189 | 118 | info[0] = '\0'; |
a306f2dd | 119 | |
120 | /* Get a packet from the client. */ | |
121 | type = packet_read(&plen); | |
122 | ||
123 | /* Process the packet. */ | |
124 | switch (type) { | |
125 | #ifdef AFS | |
126 | case SSH_CMSG_HAVE_KERBEROS_TGT: | |
127 | if (!options.kerberos_tgt_passing) { | |
a306f2dd | 128 | verbose("Kerberos tgt passing disabled."); |
129 | break; | |
130 | } else { | |
131 | /* Accept Kerberos tgt. */ | |
132 | char *tgt = packet_get_string(&dlen); | |
133 | packet_integrity_check(plen, 4 + dlen, type); | |
134 | if (!auth_kerberos_tgt(pw, tgt)) | |
59c97189 | 135 | verbose("Kerberos tgt REFUSED for %.100s", authctxt->user); |
a306f2dd | 136 | xfree(tgt); |
137 | } | |
138 | continue; | |
139 | ||
140 | case SSH_CMSG_HAVE_AFS_TOKEN: | |
141 | if (!options.afs_token_passing || !k_hasafs()) { | |
a306f2dd | 142 | verbose("AFS token passing disabled."); |
143 | break; | |
144 | } else { | |
145 | /* Accept AFS token. */ | |
146 | char *token_string = packet_get_string(&dlen); | |
147 | packet_integrity_check(plen, 4 + dlen, type); | |
148 | if (!auth_afs_token(pw, token_string)) | |
59c97189 | 149 | verbose("AFS token REFUSED for %.100s", authctxt->user); |
a306f2dd | 150 | xfree(token_string); |
151 | } | |
152 | continue; | |
153 | #endif /* AFS */ | |
154 | #ifdef KRB4 | |
155 | case SSH_CMSG_AUTH_KERBEROS: | |
156 | if (!options.kerberos_authentication) { | |
a306f2dd | 157 | verbose("Kerberos authentication disabled."); |
158 | break; | |
159 | } else { | |
160 | /* Try Kerberos v4 authentication. */ | |
161 | KTEXT_ST auth; | |
162 | char *tkt_user = NULL; | |
1e3b8b07 | 163 | char *kdata = packet_get_string((u_int *) &auth.length); |
a306f2dd | 164 | packet_integrity_check(plen, 4 + auth.length, type); |
165 | ||
59c97189 | 166 | if (authctxt->valid) { |
167 | if (auth.length < MAX_KTXT_LEN) | |
168 | memcpy(auth.dat, kdata, auth.length); | |
94ec8c6b | 169 | authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); |
170 | if (authenticated) { | |
59c97189 | 171 | snprintf(info, sizeof info, |
172 | " tktuser %.100s", tkt_user); | |
94ec8c6b | 173 | xfree(tkt_user); |
174 | } | |
a306f2dd | 175 | } |
59c97189 | 176 | xfree(kdata); |
a306f2dd | 177 | } |
178 | break; | |
179 | #endif /* KRB4 */ | |
180 | ||
181 | case SSH_CMSG_AUTH_RHOSTS: | |
182 | if (!options.rhosts_authentication) { | |
183 | verbose("Rhosts authentication disabled."); | |
184 | break; | |
185 | } | |
186 | /* | |
187 | * Get client user name. Note that we just have to | |
188 | * trust the client; this is one reason why rhosts | |
189 | * authentication is insecure. (Another is | |
190 | * IP-spoofing on a local network.) | |
191 | */ | |
192 | client_user = packet_get_string(&ulen); | |
193 | packet_integrity_check(plen, 4 + ulen, type); | |
194 | ||
94ec8c6b | 195 | /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ |
a306f2dd | 196 | authenticated = auth_rhosts(pw, client_user); |
197 | ||
59c97189 | 198 | snprintf(info, sizeof info, " ruser %.100s", client_user); |
a306f2dd | 199 | break; |
200 | ||
201 | case SSH_CMSG_AUTH_RHOSTS_RSA: | |
202 | if (!options.rhosts_rsa_authentication) { | |
203 | verbose("Rhosts with RSA authentication disabled."); | |
204 | break; | |
205 | } | |
206 | /* | |
207 | * Get client user name. Note that we just have to | |
208 | * trust the client; root on the client machine can | |
209 | * claim to be any user. | |
210 | */ | |
211 | client_user = packet_get_string(&ulen); | |
212 | ||
213 | /* Get the client host key. */ | |
214 | client_host_key = RSA_new(); | |
215 | if (client_host_key == NULL) | |
216 | fatal("RSA_new failed"); | |
217 | client_host_key->e = BN_new(); | |
218 | client_host_key->n = BN_new(); | |
219 | if (client_host_key->e == NULL || client_host_key->n == NULL) | |
220 | fatal("BN_new failed"); | |
221 | bits = packet_get_int(); | |
222 | packet_get_bignum(client_host_key->e, &elen); | |
223 | packet_get_bignum(client_host_key->n, &nlen); | |
224 | ||
225 | if (bits != BN_num_bits(client_host_key->n)) | |
94ec8c6b | 226 | verbose("Warning: keysize mismatch for client_host_key: " |
0b242b12 | 227 | "actual %d, announced %d", BN_num_bits(client_host_key->n), bits); |
a306f2dd | 228 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); |
229 | ||
230 | authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); | |
231 | RSA_free(client_host_key); | |
232 | ||
59c97189 | 233 | snprintf(info, sizeof info, " ruser %.100s", client_user); |
a306f2dd | 234 | break; |
235 | ||
236 | case SSH_CMSG_AUTH_RSA: | |
237 | if (!options.rsa_authentication) { | |
238 | verbose("RSA authentication disabled."); | |
239 | break; | |
240 | } | |
241 | /* RSA authentication requested. */ | |
242 | n = BN_new(); | |
243 | packet_get_bignum(n, &nlen); | |
244 | packet_integrity_check(plen, nlen, type); | |
245 | authenticated = auth_rsa(pw, n); | |
246 | BN_clear_free(n); | |
247 | break; | |
248 | ||
249 | case SSH_CMSG_AUTH_PASSWORD: | |
250 | if (!options.password_authentication) { | |
251 | verbose("Password authentication disabled."); | |
252 | break; | |
253 | } | |
254 | /* | |
255 | * Read user password. It is in plain text, but was | |
256 | * transmitted over the encrypted channel so it is | |
257 | * not visible to an outside observer. | |
258 | */ | |
259 | password = packet_get_string(&dlen); | |
260 | packet_integrity_check(plen, 4 + dlen, type); | |
261 | ||
262 | #ifdef USE_PAM | |
263 | /* Do PAM auth with password */ | |
264 | authenticated = auth_pam_password(pw, password); | |
4d33e531 | 265 | #elif defined(HAVE_OSF_SIA) |
266 | /* Do SIA auth with password */ | |
4d33e531 | 267 | if (sia_validate_user(NULL, saved_argc, saved_argv, |
268 | get_canonical_hostname(), pw->pw_name, NULL, 0, | |
269 | NULL, password) == SIASUCCESS) { | |
270 | authenticated = 1; | |
271 | } | |
272 | #else /* !USE_PAM && !HAVE_OSF_SIA */ | |
94ec8c6b | 273 | /* Try authentication with the password. */ |
a306f2dd | 274 | authenticated = auth_password(pw, password); |
275 | #endif /* USE_PAM */ | |
276 | ||
277 | memset(password, 0, strlen(password)); | |
278 | xfree(password); | |
279 | break; | |
280 | ||
59c97189 | 281 | #ifdef SKEY /* ISSUE: Is this right? we don't define |
282 | having skey_authentication in | |
283 | servconf.h by default so I assume | |
284 | we need to deal with this via #ifdef | |
285 | in some reasonable way */ | |
a306f2dd | 286 | case SSH_CMSG_AUTH_TIS: |
287 | debug("rcvd SSH_CMSG_AUTH_TIS"); | |
288 | if (options.skey_authentication == 1) { | |
59c97189 | 289 | char *challenge = get_challenge(authctxt, authctxt->style); |
290 | if (challenge != NULL) { | |
291 | debug("sending challenge '%s'", challenge); | |
a306f2dd | 292 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); |
59c97189 | 293 | packet_put_cstring(challenge); |
a306f2dd | 294 | packet_send(); |
295 | packet_write_wait(); | |
296 | continue; | |
297 | } | |
298 | } | |
299 | break; | |
300 | case SSH_CMSG_AUTH_TIS_RESPONSE: | |
301 | debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); | |
302 | if (options.skey_authentication == 1) { | |
303 | char *response = packet_get_string(&dlen); | |
59c97189 | 304 | debug("got response '%s'", response); |
a306f2dd | 305 | packet_integrity_check(plen, 4 + dlen, type); |
59c97189 | 306 | authenticated = verify_response(authctxt, response); |
307 | memset(response, 'r', dlen); | |
a306f2dd | 308 | xfree(response); |
309 | } | |
310 | break; | |
59c97189 | 311 | #endif /* ISSUE: End of wrong SKEY defines */ |
a306f2dd | 312 | |
313 | default: | |
314 | /* | |
315 | * Any unknown messages will be ignored (and failure | |
316 | * returned) during authentication. | |
317 | */ | |
318 | log("Unknown message during authentication: type %d", type); | |
319 | break; | |
320 | } | |
59c97189 | 321 | if (!authctxt->valid && authenticated) |
322 | fatal("INTERNAL ERROR: authenticated invalid user %s", | |
323 | authctxt->user); | |
a306f2dd | 324 | |
59c97189 | 325 | #ifdef HAVE_CYGWIN /* ISSUE: Right place? */ |
94ec8c6b | 326 | if (authenticated && |
9cd45ea4 | 327 | !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD,pw->pw_uid)) { |
3c62e7eb | 328 | packet_disconnect("Authentication rejected for uid %d.", |
94ec8c6b | 329 | (int)pw->pw_uid); |
3c62e7eb | 330 | authenticated = 0; |
331 | } | |
332 | #endif | |
333 | ||
59c97189 | 334 | /* Special handling for root */ |
335 | if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed()) | |
336 | authenticated = 0; | |
a306f2dd | 337 | |
59c97189 | 338 | #ifdef USE_PAM /* ISSUE: Right place? */ |
94ec8c6b | 339 | if (authenticated && !do_pam_account(pw->pw_name, client_user)) |
340 | authenticated = 0; | |
341 | #endif | |
a306f2dd | 342 | |
59c97189 | 343 | /* Log before sending the reply */ |
344 | auth_log(authctxt, authenticated, get_authname(type), info); | |
345 | ||
a306f2dd | 346 | if (client_user != NULL) { |
347 | xfree(client_user); | |
348 | client_user = NULL; | |
349 | } | |
350 | ||
94ec8c6b | 351 | if (authenticated) |
352 | return; | |
353 | ||
59c97189 | 354 | if (authctxt->failures++ > AUTH_FAIL_MAX) { |
c1ef8333 | 355 | #ifdef WITH_AIXAUTHENTICATE |
94ec8c6b | 356 | loginfailed(user,get_canonical_hostname(),"ssh"); |
c1ef8333 | 357 | #endif /* WITH_AIXAUTHENTICATE */ |
59c97189 | 358 | packet_disconnect(AUTH_FAIL_MSG, authctxt->user); |
c1ef8333 | 359 | } |
a306f2dd | 360 | |
a306f2dd | 361 | packet_start(SSH_SMSG_FAILURE); |
362 | packet_send(); | |
363 | packet_write_wait(); | |
364 | } | |
365 | } | |
366 | ||
367 | /* | |
368 | * Performs authentication of an incoming connection. Session key has already | |
369 | * been exchanged and encryption is enabled. | |
370 | */ | |
371 | void | |
372 | do_authentication() | |
373 | { | |
59c97189 | 374 | Authctxt *authctxt; |
375 | struct passwd *pw; | |
a306f2dd | 376 | int plen; |
1e3b8b07 | 377 | u_int ulen; |
59c97189 | 378 | char *user, *style = NULL; |
a306f2dd | 379 | |
380 | /* Get the name of the user that we wish to log in as. */ | |
381 | packet_read_expect(&plen, SSH_CMSG_USER); | |
382 | ||
383 | /* Get the user name. */ | |
384 | user = packet_get_string(&ulen); | |
385 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); | |
386 | ||
59c97189 | 387 | if ((style = strchr(user, ':')) != NULL) |
388 | *style++ = 0; | |
389 | ||
390 | authctxt = authctxt_new(); | |
391 | authctxt->user = user; | |
392 | authctxt->style = style; | |
393 | ||
a306f2dd | 394 | setproctitle("%s", user); |
395 | ||
396 | #ifdef AFS | |
397 | /* If machine has AFS, set process authentication group. */ | |
398 | if (k_hasafs()) { | |
399 | k_setpag(); | |
400 | k_unlog(); | |
401 | } | |
402 | #endif /* AFS */ | |
403 | ||
404 | /* Verify that the user is a valid user. */ | |
405 | pw = getpwnam(user); | |
94ec8c6b | 406 | if (pw && allowed_user(pw)) { |
59c97189 | 407 | authctxt->valid = 1; |
408 | pw = pwcopy(pw); | |
94ec8c6b | 409 | } else { |
59c97189 | 410 | debug("do_authentication: illegal user %s", user); |
94ec8c6b | 411 | pw = NULL; |
412 | } | |
59c97189 | 413 | authctxt->pw = pw; |
a306f2dd | 414 | |
415 | #ifdef USE_PAM | |
94ec8c6b | 416 | if (pw) |
04fc7a67 | 417 | start_pam(user); |
a306f2dd | 418 | #endif |
419 | ||
420 | /* | |
421 | * If we are not running as root, the user must have the same uid as | |
94ec8c6b | 422 | * the server. (Unless you are running Windows) |
a306f2dd | 423 | */ |
94ec8c6b | 424 | #ifndef HAVE_CYGWIN |
425 | if (getuid() != 0 && pw && pw->pw_uid != getuid()) | |
a306f2dd | 426 | packet_disconnect("Cannot change user when server not running as root."); |
3c62e7eb | 427 | #endif |
a306f2dd | 428 | |
59c97189 | 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); | |
a306f2dd | 434 | |
435 | /* The user has been authenticated and accepted. */ | |
94ec8c6b | 436 | packet_start(SSH_SMSG_SUCCESS); |
437 | packet_send(); | |
438 | packet_write_wait(); | |
59c97189 | 439 | |
a306f2dd | 440 | #ifdef WITH_AIXAUTHENTICATE |
c1ef8333 | 441 | /* We don't have a pty yet, so just label the line as "ssh" */ |
59c97189 | 442 | if (loginsuccess(authctxt->user,get_canonical_hostname(),"ssh",&aixloginmsg) < 0) |
c1ef8333 | 443 | aixloginmsg = NULL; |
a306f2dd | 444 | #endif /* WITH_AIXAUTHENTICATE */ |
59c97189 | 445 | |
446 | xfree(authctxt->user); | |
447 | xfree(authctxt); | |
a306f2dd | 448 | |
449 | /* Perform session preparation. */ | |
450 | do_authenticated(pw); | |
451 | } |