blob: fec73e3a2bf1edf94b3a812be0c7c5362aaa898a [file] [log] [blame]
Damien Millereba71ba2000-04-29 23:57:08 +10001/*
2 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
3 * All rights reserved
Damien Millere4340be2000-09-16 13:29:08 +11004 *
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".
Damien Millereba71ba2000-04-29 23:57:08 +100010 */
11
12#include "includes.h"
Damien Miller0bc1bd82000-11-13 22:57:25 +110013RCSID("$OpenBSD: auth1.c,v 1.7 2000/11/10 01:04:40 markus Exp $");
Damien Millereba71ba2000-04-29 23:57:08 +100014
Damien Millerb8c656e2000-06-28 15:22:41 +100015#ifdef HAVE_OSF_SIA
16# include <sia.h>
17# include <siad.h>
18#endif
19
Damien Miller874d77b2000-10-14 16:23:11 +110020#include "xmalloc.h"
21#include "rsa.h"
22#include "ssh.h"
23#include "packet.h"
24#include "buffer.h"
25#include "mpaux.h"
26#include "servconf.h"
27#include "compat.h"
28#include "auth.h"
29#include "session.h"
30
Damien Millereba71ba2000-04-29 23:57:08 +100031/* import */
32extern ServerOptions options;
33extern char *forced_command;
Damien Miller874d77b2000-10-14 16:23:11 +110034
35#ifdef WITH_AIXAUTHENTICATE
36extern char *aixloginmsg;
37#endif /* WITH_AIXAUTHENTICATE */
Damien Millerfe668e42000-07-08 10:44:13 +100038#ifdef HAVE_OSF_SIA
39extern int saved_argc;
40extern char **saved_argv;
41#endif /* HAVE_OSF_SIA */
Damien Millereba71ba2000-04-29 23:57:08 +100042
43/*
44 * convert ssh auth msg type into description
45 */
46char *
47get_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/*
Damien Miller874d77b2000-10-14 16:23:11 +110073 * 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.
Damien Millereba71ba2000-04-29 23:57:08 +100078 */
79void
Damien Miller874d77b2000-10-14 16:23:11 +110080do_authloop(struct passwd * pw, char *luser)
Damien Millereba71ba2000-04-29 23:57:08 +100081{
Damien Miller874d77b2000-10-14 16:23:11 +110082 int authenticated = 0;
Damien Millereba71ba2000-04-29 23:57:08 +100083 int attempt = 0;
84 unsigned int bits;
85 RSA *client_host_key;
86 BIGNUM *n;
Damien Miller874d77b2000-10-14 16:23:11 +110087 char *client_user, *password;
Damien Millereba71ba2000-04-29 23:57:08 +100088 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
Damien Miller874d77b2000-10-14 16:23:11 +1100100 client_user = NULL;
101
Damien Millereba71ba2000-04-29 23:57:08 +1000102 for (attempt = 1;; attempt++) {
Damien Miller874d77b2000-10-14 16:23:11 +1100103 /* default to fail */
104 authenticated = 0;
105
Damien Millereba71ba2000-04-29 23:57:08 +1000106 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) {
Damien Millereba71ba2000-04-29 23:57:08 +1000116 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))
Damien Miller874d77b2000-10-14 16:23:11 +1100123 verbose("Kerberos tgt REFUSED for %.100s", luser);
Damien Millereba71ba2000-04-29 23:57:08 +1000124 xfree(tgt);
125 }
126 continue;
127
128 case SSH_CMSG_HAVE_AFS_TOKEN:
129 if (!options.afs_token_passing || !k_hasafs()) {
Damien Millereba71ba2000-04-29 23:57:08 +1000130 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))
Damien Miller874d77b2000-10-14 16:23:11 +1100137 verbose("AFS token REFUSED for %.100s", luser);
Damien Millereba71ba2000-04-29 23:57:08 +1000138 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
Damien Miller874d77b2000-10-14 16:23:11 +1100159 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 }
Damien Millereba71ba2000-04-29 23:57:08 +1000165 }
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
Damien Miller874d77b2000-10-14 16:23:11 +1100184 /* Try to authenticate using /etc/hosts.equiv and .rhosts. */
Damien Millereba71ba2000-04-29 23:57:08 +1000185 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))
Damien Miller874d77b2000-10-14 16:23:11 +1100215 verbose("Warning: keysize mismatch for client_host_key: "
Damien Millerbd483e72000-04-30 10:00:53 +1000216 "actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
Damien Millereba71ba2000-04-29 23:57:08 +1000217 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);
Damien Millerb8c656e2000-06-28 15:22:41 +1000254#elif defined(HAVE_OSF_SIA)
255 /* Do SIA auth with password */
Damien Millerb8c656e2000-06-28 15:22:41 +1000256 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 */
Damien Miller874d77b2000-10-14 16:23:11 +1100262 /* Try authentication with the password. */
Damien Millereba71ba2000-04-29 23:57:08 +1000263 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) {
Damien Miller874d77b2000-10-14 16:23:11 +1100274 char *skeyinfo = NULL;
275 if (pw != NULL)
Ben Lindstrom305fb002000-11-10 02:41:30 +0000276 skeyinfo = skey_keyinfo(pw->pw_name);
Damien Millereba71ba2000-04-29 23:57:08 +1000277 if (skeyinfo == NULL) {
Damien Miller874d77b2000-10-14 16:23:11 +1100278 debug("generating fake skeyinfo for %.100s.", luser);
279 skeyinfo = skey_fake_keyinfo(luser);
Damien Millereba71ba2000-04-29 23:57:08 +1000280 }
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);
Damien Miller874d77b2000-10-14 16:23:11 +1100285 packet_put_cstring(skeyinfo);
Damien Millereba71ba2000-04-29 23:57:08 +1000286 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);
Damien Miller874d77b2000-10-14 16:23:11 +1100298 authenticated = (pw != NULL &&
299 skey_haskey(pw->pw_name) == 0 &&
300 skey_passcheck(pw->pw_name, response) != -1);
Damien Millereba71ba2000-04-29 23:57:08 +1000301 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 }
Damien Miller874d77b2000-10-14 16:23:11 +1100319 if (authenticated && pw == NULL)
320 fatal("internal error: authenticated for pw == NULL");
Damien Millereba71ba2000-04-29 23:57:08 +1000321
Damien Millerbac2d8a2000-09-05 16:13:06 +1100322#ifdef HAVE_CYGWIN
Damien Miller874d77b2000-10-14 16:23:11 +1100323 if (authenticated &&
Damien Millerb70b61f2000-09-16 16:25:12 +1100324 !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD,pw->pw_uid)) {
Damien Millerbac2d8a2000-09-05 16:13:06 +1100325 packet_disconnect("Authentication rejected for uid %d.",
Damien Miller874d77b2000-10-14 16:23:11 +1100326 (int)pw->pw_uid);
Damien Millerbac2d8a2000-09-05 16:13:06 +1100327 authenticated = 0;
328 }
329#endif
330
Damien Millereba71ba2000-04-29 23:57:08 +1000331 /*
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 */
Damien Miller874d77b2000-10-14 16:23:11 +1100336 if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
Damien Millereba71ba2000-04-29 23:57:08 +1000337 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
Damien Miller874d77b2000-10-14 16:23:11 +1100352 authlog("%s %s for %s%.100s from %.200s port %d%s",
Damien Millereba71ba2000-04-29 23:57:08 +1000353 authenticated ? "Accepted" : "Failed",
354 get_authname(type),
Damien Miller874d77b2000-10-14 16:23:11 +1100355 pw ? "" : "illegal user ",
356 pw && pw->pw_uid == 0 ? "ROOT" : luser,
Damien Millereba71ba2000-04-29 23:57:08 +1000357 get_remote_ipaddr(),
358 get_remote_port(),
359 user);
360
361#ifdef USE_PAM
Damien Miller874d77b2000-10-14 16:23:11 +1100362 if (authenticated && !do_pam_account(pw->pw_name, client_user))
363 authenticated = 0;
364#endif
Damien Millereba71ba2000-04-29 23:57:08 +1000365
366 if (client_user != NULL) {
367 xfree(client_user);
368 client_user = NULL;
369 }
370
Damien Miller874d77b2000-10-14 16:23:11 +1100371 if (authenticated)
372 return;
373
Damien Millerd2c208a2000-05-17 22:00:02 +1000374 if (attempt > AUTH_FAIL_MAX) {
375#ifdef WITH_AIXAUTHENTICATE
Damien Miller874d77b2000-10-14 16:23:11 +1100376 loginfailed(user,get_canonical_hostname(),"ssh");
Damien Millerd2c208a2000-05-17 22:00:02 +1000377#endif /* WITH_AIXAUTHENTICATE */
Damien Miller874d77b2000-10-14 16:23:11 +1100378 packet_disconnect(AUTH_FAIL_MSG, luser);
Damien Millerd2c208a2000-05-17 22:00:02 +1000379 }
Damien Millereba71ba2000-04-29 23:57:08 +1000380
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 */
392void
393do_authentication()
394{
395 struct passwd *pw, pwcopy;
396 int plen;
397 unsigned int ulen;
398 char *user;
Damien Millereba71ba2000-04-29 23:57:08 +1000399
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);
Damien Miller874d77b2000-10-14 16:23:11 +1100419 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;
Damien Millerad833b32000-08-23 10:46:23 +1000426#ifdef HAVE_PW_CLASS_IN_PASSWD
Damien Miller874d77b2000-10-14 16:23:11 +1100427 pwcopy.pw_class = xstrdup(pw->pw_class);
Damien Millerad833b32000-08-23 10:46:23 +1000428#endif
Damien Miller874d77b2000-10-14 16:23:11 +1100429 pwcopy.pw_dir = xstrdup(pw->pw_dir);
430 pwcopy.pw_shell = xstrdup(pw->pw_shell);
431 pw = &pwcopy;
432 } else {
433 pw = NULL;
434 }
Damien Millereba71ba2000-04-29 23:57:08 +1000435
436#ifdef USE_PAM
Damien Miller874d77b2000-10-14 16:23:11 +1100437 if (pw)
438 start_pam(pw);
Damien Millereba71ba2000-04-29 23:57:08 +1000439#endif
440
441 /*
442 * If we are not running as root, the user must have the same uid as
Damien Miller874d77b2000-10-14 16:23:11 +1100443 * the server. (Unless you are running Windows)
Damien Millereba71ba2000-04-29 23:57:08 +1000444 */
Damien Miller874d77b2000-10-14 16:23:11 +1100445#ifndef HAVE_CYGWIN
446 if (getuid() != 0 && pw && pw->pw_uid != getuid())
Damien Millereba71ba2000-04-29 23:57:08 +1000447 packet_disconnect("Cannot change user when server not running as root.");
Damien Millerbac2d8a2000-09-05 16:13:06 +1100448#endif
Damien Millereba71ba2000-04-29 23:57:08 +1000449
Damien Miller874d77b2000-10-14 16:23:11 +1100450 debug("Attempting authentication for %s%.100s.", pw ? "" : "illegal user ", user);
Damien Millereba71ba2000-04-29 23:57:08 +1000451
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, "")) {
Damien Millerd8cfda62000-07-01 12:56:09 +1000459#elif defined(HAVE_OSF_SIA)
460 (sia_validate_user(NULL, saved_argc, saved_argv,
Damien Miller874d77b2000-10-14 16:23:11 +1100461 get_canonical_hostname(), pw->pw_name, NULL, 0,
462 NULL, "") == SIASUCCESS)) {
Damien Millerd8cfda62000-07-01 12:56:09 +1000463#else /* !HAVE_OSF_SIA && !USE_PAM */
Damien Miller874d77b2000-10-14 16:23:11 +1100464 auth_password(pw, "")) {
Damien Millereba71ba2000-04-29 23:57:08 +1000465#endif /* USE_PAM */
466 /* Authentication with empty password succeeded. */
467 log("Login for user %s from %.100s, accepted without authentication.",
Damien Miller874d77b2000-10-14 16:23:11 +1100468 user, get_remote_ipaddr());
Damien Millereba71ba2000-04-29 23:57:08 +1000469 } else {
470 /* Loop until the user has been authenticated or the
471 connection is closed, do_authloop() returns only if
472 authentication is successfull */
Damien Miller874d77b2000-10-14 16:23:11 +1100473 do_authloop(pw, user);
Damien Millereba71ba2000-04-29 23:57:08 +1000474 }
Damien Miller874d77b2000-10-14 16:23:11 +1100475 if (pw == NULL)
476 fatal("internal error, authentication successfull for user '%.100s'", user);
Damien Millereba71ba2000-04-29 23:57:08 +1000477
478 /* The user has been authenticated and accepted. */
Damien Miller874d77b2000-10-14 16:23:11 +1100479 packet_start(SSH_SMSG_SUCCESS);
480 packet_send();
481 packet_write_wait();
482
Damien Millereba71ba2000-04-29 23:57:08 +1000483#ifdef WITH_AIXAUTHENTICATE
Damien Millerd2c208a2000-05-17 22:00:02 +1000484 /* 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;
Damien Millereba71ba2000-04-29 23:57:08 +1000487#endif /* WITH_AIXAUTHENTICATE */
Damien Millereba71ba2000-04-29 23:57:08 +1000488
489 /* Perform session preparation. */
490 do_authenticated(pw);
491}