blob: 6ac5d2527b8888cad26ee5bde08e4298b06d244d [file] [log] [blame]
Damien Millereba71ba2000-04-29 23:57:08 +10001/*
2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
Damien Millereba71ba2000-04-29 23:57:08 +100012 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
Damien Millere4340be2000-09-16 13:29:08 +110024
Damien Millereba71ba2000-04-29 23:57:08 +100025#include "includes.h"
Damien Miller62cee002000-09-23 17:15:56 +110026RCSID("$OpenBSD: auth2.c,v 1.15 2000/09/21 11:25:32 markus Exp $");
Damien Millereba71ba2000-04-29 23:57:08 +100027
28#include <openssl/dsa.h>
29#include <openssl/rsa.h>
30#include <openssl/evp.h>
31
32#include "xmalloc.h"
33#include "rsa.h"
34#include "ssh.h"
35#include "pty.h"
36#include "packet.h"
37#include "buffer.h"
38#include "cipher.h"
39#include "servconf.h"
40#include "compat.h"
41#include "channels.h"
42#include "bufaux.h"
43#include "ssh2.h"
44#include "auth.h"
45#include "session.h"
46#include "dispatch.h"
47#include "auth.h"
48#include "key.h"
49#include "kex.h"
50
51#include "dsa.h"
52#include "uidswap.h"
Damien Millerf6d9e222000-06-18 14:50:44 +100053#include "auth-options.h"
Damien Millereba71ba2000-04-29 23:57:08 +100054
Damien Millerb8c656e2000-06-28 15:22:41 +100055#ifdef HAVE_OSF_SIA
56# include <sia.h>
57# include <siad.h>
58#endif
59
Damien Millereba71ba2000-04-29 23:57:08 +100060/* import */
61extern ServerOptions options;
62extern unsigned char *session_id2;
63extern int session_id2_len;
64
65/* protocol */
66
Damien Miller62cee002000-09-23 17:15:56 +110067void input_service_request(int type, int plen, void *ctxt);
68void input_userauth_request(int type, int plen, void *ctxt);
69void protocol_error(int type, int plen, void *ctxt);
Damien Millereba71ba2000-04-29 23:57:08 +100070
71/* auth */
72int ssh2_auth_none(struct passwd *pw);
73int ssh2_auth_password(struct passwd *pw);
Damien Millerf6d9e222000-06-18 14:50:44 +100074int ssh2_auth_pubkey(struct passwd *pw, char *service);
Damien Millereba71ba2000-04-29 23:57:08 +100075
76/* helper */
77struct passwd* auth_set_user(char *u, char *s);
78int user_dsa_key_allowed(struct passwd *pw, Key *key);
79
80typedef struct Authctxt Authctxt;
81struct Authctxt {
82 char *user;
83 char *service;
84 struct passwd pw;
85 int valid;
86};
87static Authctxt *authctxt = NULL;
88static int userauth_success = 0;
89
90/*
91 * loop until userauth_success == TRUE
92 */
93
94void
95do_authentication2()
96{
Damien Miller35dabd02000-05-01 21:10:33 +100097 /* turn off skey/kerberos, not supported by SSH2 */
Damien Miller1cead2c2000-05-01 22:55:23 +100098#ifdef SKEY
Damien Miller35dabd02000-05-01 21:10:33 +100099 options.skey_authentication = 0;
Damien Miller1cead2c2000-05-01 22:55:23 +1000100#endif
101#ifdef KRB4
Damien Miller35dabd02000-05-01 21:10:33 +1000102 options.kerberos_authentication = 0;
Damien Miller1cead2c2000-05-01 22:55:23 +1000103#endif
Damien Miller35dabd02000-05-01 21:10:33 +1000104
Damien Millereba71ba2000-04-29 23:57:08 +1000105 dispatch_init(&protocol_error);
106 dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
Damien Miller62cee002000-09-23 17:15:56 +1100107 dispatch_run(DISPATCH_BLOCK, &userauth_success, NULL);
Damien Millereba71ba2000-04-29 23:57:08 +1000108 do_authenticated2();
109}
110
111void
Damien Miller62cee002000-09-23 17:15:56 +1100112protocol_error(int type, int plen, void *ctxt)
Damien Millereba71ba2000-04-29 23:57:08 +1000113{
114 log("auth: protocol error: type %d plen %d", type, plen);
115 packet_start(SSH2_MSG_UNIMPLEMENTED);
116 packet_put_int(0);
117 packet_send();
118 packet_write_wait();
119}
120
121void
Damien Miller62cee002000-09-23 17:15:56 +1100122input_service_request(int type, int plen, void *ctxt)
Damien Millereba71ba2000-04-29 23:57:08 +1000123{
124 unsigned int len;
125 int accept = 0;
126 char *service = packet_get_string(&len);
127 packet_done();
128
129 if (strcmp(service, "ssh-userauth") == 0) {
130 if (!userauth_success) {
131 accept = 1;
132 /* now we can handle user-auth requests */
133 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
134 }
135 }
136 /* XXX all other service requests are denied */
137
138 if (accept) {
139 packet_start(SSH2_MSG_SERVICE_ACCEPT);
140 packet_put_cstring(service);
141 packet_send();
142 packet_write_wait();
143 } else {
144 debug("bad service request %s", service);
145 packet_disconnect("bad service request %s", service);
146 }
147 xfree(service);
148}
149
150void
Damien Miller62cee002000-09-23 17:15:56 +1100151input_userauth_request(int type, int plen, void *ctxt)
Damien Millereba71ba2000-04-29 23:57:08 +1000152{
153 static void (*authlog) (const char *fmt,...) = verbose;
154 static int attempt = 0;
Damien Millerf6d9e222000-06-18 14:50:44 +1000155 unsigned int len;
Damien Millereba71ba2000-04-29 23:57:08 +1000156 int authenticated = 0;
Damien Millerf6d9e222000-06-18 14:50:44 +1000157 char *user, *service, *method, *authmsg = NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000158 struct passwd *pw;
Damien Millerd2c208a2000-05-17 22:00:02 +1000159#ifdef WITH_AIXAUTHENTICATE
160 extern char *aixloginmsg;
161#endif /* WITH_AIXAUTHENTICATE */
Damien Millereba71ba2000-04-29 23:57:08 +1000162
Damien Millereba71ba2000-04-29 23:57:08 +1000163 user = packet_get_string(&len);
164 service = packet_get_string(&len);
165 method = packet_get_string(&len);
Damien Millerd2c208a2000-05-17 22:00:02 +1000166 if (++attempt == AUTH_FAIL_MAX) {
167#ifdef WITH_AIXAUTHENTICATE
168 loginfailed(user,get_canonical_hostname(),"ssh");
169#endif /* WITH_AIXAUTHENTICATE */
170 packet_disconnect("too many failed userauth_requests");
171 }
Damien Millereba71ba2000-04-29 23:57:08 +1000172 debug("userauth-request for user %s service %s method %s", user, service, method);
173
174 /* XXX we only allow the ssh-connection service */
175 pw = auth_set_user(user, service);
176 if (pw && strcmp(service, "ssh-connection")==0) {
177 if (strcmp(method, "none") == 0) {
178 authenticated = ssh2_auth_none(pw);
179 } else if (strcmp(method, "password") == 0) {
180 authenticated = ssh2_auth_password(pw);
181 } else if (strcmp(method, "publickey") == 0) {
Damien Millerf6d9e222000-06-18 14:50:44 +1000182 authenticated = ssh2_auth_pubkey(pw, service);
Damien Millereba71ba2000-04-29 23:57:08 +1000183 }
184 }
Damien Millerb70b61f2000-09-16 16:25:12 +1100185
186#ifdef HAVE_CYGWIN
187 if (authenticated && !check_nt_auth(strcmp(method, "password") == 0, pw->pw_uid)) {
188 packet_disconnect("Authentication rejected for uid %d.",
189 (int) pw->pw_uid);
190 authenticated = 0;
191 }
192#endif
193
Damien Millereba71ba2000-04-29 23:57:08 +1000194 if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
195 authenticated = 0;
196 log("ROOT LOGIN REFUSED FROM %.200s",
197 get_canonical_hostname());
198 }
199
200#ifdef USE_PAM
Damien Millerb70b61f2000-09-16 16:25:12 +1100201 if (authenticated && !do_pam_account(pw->pw_name, NULL))
202 authenticated = 0;
Damien Millereba71ba2000-04-29 23:57:08 +1000203#endif /* USE_PAM */
204
Damien Millere247cc42000-05-07 12:03:14 +1000205 /* Raise logging level */
206 if (authenticated == 1 ||
207 attempt == AUTH_FAIL_LOG ||
208 strcmp(method, "password") == 0)
209 authlog = log;
210
211 /* Log before sending the reply */
Damien Millereba71ba2000-04-29 23:57:08 +1000212 if (authenticated == 1) {
213 authmsg = "Accepted";
Damien Millere247cc42000-05-07 12:03:14 +1000214 } else if (authenticated == 0) {
215 authmsg = "Failed";
216 } else {
217 authmsg = "Postponed";
218 }
219 authlog("%s %s for %.200s from %.200s port %d ssh2",
220 authmsg,
221 method,
222 pw && pw->pw_uid == 0 ? "ROOT" : user,
223 get_remote_ipaddr(),
224 get_remote_port());
225
226 /* XXX todo: check if multiple auth methods are needed */
227 if (authenticated == 1) {
Damien Millerd2c208a2000-05-17 22:00:02 +1000228#ifdef WITH_AIXAUTHENTICATE
229 /* We don't have a pty yet, so just label the line as "ssh" */
230 if (loginsuccess(user,get_canonical_hostname(),"ssh",
231 &aixloginmsg) < 0)
232 aixloginmsg = NULL;
233#endif /* WITH_AIXAUTHENTICATE */
Damien Millereba71ba2000-04-29 23:57:08 +1000234 /* turn off userauth */
235 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
236 packet_start(SSH2_MSG_USERAUTH_SUCCESS);
237 packet_send();
238 packet_write_wait();
239 /* now we can break out */
240 userauth_success = 1;
241 } else if (authenticated == 0) {
Damien Millereba71ba2000-04-29 23:57:08 +1000242 packet_start(SSH2_MSG_USERAUTH_FAILURE);
243 packet_put_cstring("publickey,password"); /* XXX dynamic */
244 packet_put_char(0); /* XXX partial success, unused */
245 packet_send();
246 packet_write_wait();
Damien Millereba71ba2000-04-29 23:57:08 +1000247 }
Damien Millereba71ba2000-04-29 23:57:08 +1000248
249 xfree(service);
250 xfree(user);
251 xfree(method);
252}
253
254int
255ssh2_auth_none(struct passwd *pw)
256{
Damien Millerb8c656e2000-06-28 15:22:41 +1000257#ifdef HAVE_OSF_SIA
258 extern int saved_argc;
259 extern char **saved_argv;
260#endif
261
Damien Millereba71ba2000-04-29 23:57:08 +1000262 packet_done();
Damien Millerb8c656e2000-06-28 15:22:41 +1000263
Damien Millereba71ba2000-04-29 23:57:08 +1000264#ifdef USE_PAM
265 return auth_pam_password(pw, "");
Damien Millerb8c656e2000-06-28 15:22:41 +1000266#elif defined(HAVE_OSF_SIA)
267 return(sia_validate_user(NULL, saved_argc, saved_argv,
268 get_canonical_hostname(), pw->pw_name, NULL, 0, NULL,
269 "") == SIASUCCESS);
270#else /* !HAVE_OSF_SIA && !USE_PAM */
Damien Millereba71ba2000-04-29 23:57:08 +1000271 return auth_password(pw, "");
272#endif /* USE_PAM */
273}
274int
275ssh2_auth_password(struct passwd *pw)
276{
277 char *password;
278 int authenticated = 0;
279 int change;
280 unsigned int len;
Damien Millerb8c656e2000-06-28 15:22:41 +1000281#ifdef HAVE_OSF_SIA
282 extern int saved_argc;
283 extern char **saved_argv;
284#endif
Damien Millereba71ba2000-04-29 23:57:08 +1000285 change = packet_get_char();
286 if (change)
287 log("password change not supported");
288 password = packet_get_string(&len);
289 packet_done();
290 if (options.password_authentication &&
291#ifdef USE_PAM
292 auth_pam_password(pw, password) == 1)
Damien Millerb8c656e2000-06-28 15:22:41 +1000293#elif defined(HAVE_OSF_SIA)
294 sia_validate_user(NULL, saved_argc, saved_argv,
295 get_canonical_hostname(), pw->pw_name, NULL, 0,
296 NULL, password) == SIASUCCESS)
297#else /* !USE_PAM && !HAVE_OSF_SIA */
Damien Millereba71ba2000-04-29 23:57:08 +1000298 auth_password(pw, password) == 1)
299#endif /* USE_PAM */
300 authenticated = 1;
301 memset(password, 0, len);
302 xfree(password);
303 return authenticated;
304}
305int
Damien Millerf6d9e222000-06-18 14:50:44 +1000306ssh2_auth_pubkey(struct passwd *pw, char *service)
Damien Millereba71ba2000-04-29 23:57:08 +1000307{
308 Buffer b;
309 Key *key;
310 char *pkalg, *pkblob, *sig;
311 unsigned int alen, blen, slen;
312 int have_sig;
313 int authenticated = 0;
314
Damien Millere247cc42000-05-07 12:03:14 +1000315 if (options.dsa_authentication == 0) {
Damien Millereba71ba2000-04-29 23:57:08 +1000316 debug("pubkey auth disabled");
317 return 0;
318 }
319 have_sig = packet_get_char();
320 pkalg = packet_get_string(&alen);
321 if (strcmp(pkalg, KEX_DSS) != 0) {
322 xfree(pkalg);
323 log("bad pkalg %s", pkalg); /*XXX*/
324 return 0;
325 }
326 pkblob = packet_get_string(&blen);
327 key = dsa_key_from_blob(pkblob, blen);
328 if (key != NULL) {
329 if (have_sig) {
330 sig = packet_get_string(&slen);
331 packet_done();
332 buffer_init(&b);
Damien Miller6536c7d2000-06-22 21:32:31 +1000333 if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) {
334 buffer_put_string(&b, session_id2, session_id2_len);
335 } else {
336 buffer_append(&b, session_id2, session_id2_len);
337 }
Damien Millerf6d9e222000-06-18 14:50:44 +1000338 /* reconstruct packet */
Damien Millereba71ba2000-04-29 23:57:08 +1000339 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
Damien Millerf6d9e222000-06-18 14:50:44 +1000340 buffer_put_cstring(&b, pw->pw_name);
341 buffer_put_cstring(&b,
342 datafellows & SSH_BUG_PUBKEYAUTH ?
343 "ssh-userauth" :
344 service);
345 buffer_put_cstring(&b, "publickey");
346 buffer_put_char(&b, have_sig);
347 buffer_put_cstring(&b, KEX_DSS);
348 buffer_put_string(&b, pkblob, blen);
Damien Millereba71ba2000-04-29 23:57:08 +1000349#ifdef DEBUG_DSS
350 buffer_dump(&b);
351#endif
352 /* test for correct signature */
353 if (user_dsa_key_allowed(pw, key) &&
354 dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
355 authenticated = 1;
356 buffer_clear(&b);
357 xfree(sig);
358 } else {
359 packet_done();
360 debug("test key...");
361 /* test whether pkalg/pkblob are acceptable */
362 /* XXX fake reply and always send PK_OK ? */
Damien Millere247cc42000-05-07 12:03:14 +1000363 /*
364 * XXX this allows testing whether a user is allowed
365 * to login: if you happen to have a valid pubkey this
366 * message is sent. the message is NEVER sent at all
367 * if a user is not allowed to login. is this an
368 * issue? -markus
369 */
Damien Millereba71ba2000-04-29 23:57:08 +1000370 if (user_dsa_key_allowed(pw, key)) {
371 packet_start(SSH2_MSG_USERAUTH_PK_OK);
372 packet_put_string(pkalg, alen);
373 packet_put_string(pkblob, blen);
374 packet_send();
375 packet_write_wait();
376 authenticated = -1;
377 }
378 }
379 key_free(key);
380 }
381 xfree(pkalg);
382 xfree(pkblob);
383 return authenticated;
384}
385
386/* set and get current user */
387
388struct passwd*
389auth_get_user(void)
390{
391 return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
392}
393
394struct passwd*
395auth_set_user(char *u, char *s)
396{
397 struct passwd *pw, *copy;
398
399 if (authctxt == NULL) {
400 authctxt = xmalloc(sizeof(*authctxt));
401 authctxt->valid = 0;
402 authctxt->user = xstrdup(u);
403 authctxt->service = xstrdup(s);
404 setproctitle("%s", u);
405 pw = getpwnam(u);
406 if (!pw || !allowed_user(pw)) {
407 log("auth_set_user: illegal user %s", u);
408 return NULL;
409 }
410#ifdef USE_PAM
411 start_pam(pw);
412#endif
413 copy = &authctxt->pw;
414 memset(copy, 0, sizeof(*copy));
415 copy->pw_name = xstrdup(pw->pw_name);
416 copy->pw_passwd = xstrdup(pw->pw_passwd);
417 copy->pw_uid = pw->pw_uid;
418 copy->pw_gid = pw->pw_gid;
Damien Millerad833b32000-08-23 10:46:23 +1000419#ifdef HAVE_PW_CLASS_IN_PASSWD
420 copy->pw_class = xstrdup(pw->pw_class);
421#endif
Damien Millereba71ba2000-04-29 23:57:08 +1000422 copy->pw_dir = xstrdup(pw->pw_dir);
423 copy->pw_shell = xstrdup(pw->pw_shell);
424 authctxt->valid = 1;
425 } else {
426 if (strcmp(u, authctxt->user) != 0 ||
427 strcmp(s, authctxt->service) != 0) {
428 log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
429 u, s, authctxt->user, authctxt->service);
430 return NULL;
431 }
432 }
433 return auth_get_user();
434}
435
436/* return 1 if user allows given key */
437int
438user_dsa_key_allowed(struct passwd *pw, Key *key)
439{
440 char line[8192], file[1024];
441 int found_key = 0;
442 unsigned int bits = -1;
443 FILE *f;
444 unsigned long linenum = 0;
445 struct stat st;
446 Key *found;
447
448 /* Temporarily use the user's uid. */
449 temporarily_use_uid(pw->pw_uid);
450
451 /* The authorized keys. */
452 snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
453 SSH_USER_PERMITTED_KEYS2);
454
455 /* Fail quietly if file does not exist */
456 if (stat(file, &st) < 0) {
457 /* Restore the privileged uid. */
458 restore_uid();
459 return 0;
460 }
461 /* Open the file containing the authorized keys. */
462 f = fopen(file, "r");
463 if (!f) {
464 /* Restore the privileged uid. */
465 restore_uid();
466 return 0;
467 }
468 if (options.strict_modes) {
469 int fail = 0;
470 char buf[1024];
471 /* Check open file in order to avoid open/stat races */
472 if (fstat(fileno(f), &st) < 0 ||
473 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
474 (st.st_mode & 022) != 0) {
475 snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
476 "bad ownership or modes for '%s'.", pw->pw_name, file);
477 fail = 1;
478 } else {
479 /* Check path to SSH_USER_PERMITTED_KEYS */
480 int i;
481 static const char *check[] = {
482 "", SSH_USER_DIR, NULL
483 };
484 for (i = 0; check[i]; i++) {
485 snprintf(line, sizeof line, "%.500s/%.100s",
486 pw->pw_dir, check[i]);
487 if (stat(line, &st) < 0 ||
488 (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
489 (st.st_mode & 022) != 0) {
490 snprintf(buf, sizeof buf,
491 "DSA authentication refused for %.100s: "
492 "bad ownership or modes for '%s'.",
493 pw->pw_name, line);
494 fail = 1;
495 break;
496 }
497 }
498 }
499 if (fail) {
Damien Millereba71ba2000-04-29 23:57:08 +1000500 fclose(f);
Damien Miller37023962000-07-11 17:31:38 +1000501 log("%s",buf);
Damien Millereba71ba2000-04-29 23:57:08 +1000502 restore_uid();
503 return 0;
504 }
505 }
506 found_key = 0;
507 found = key_new(KEY_DSA);
508
509 while (fgets(line, sizeof(line), f)) {
Damien Millerf6d9e222000-06-18 14:50:44 +1000510 char *cp, *options = NULL;
Damien Millereba71ba2000-04-29 23:57:08 +1000511 linenum++;
512 /* Skip leading whitespace, empty and comment lines. */
513 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
514 ;
515 if (!*cp || *cp == '\n' || *cp == '#')
516 continue;
Damien Millerf6d9e222000-06-18 14:50:44 +1000517
Damien Millereba71ba2000-04-29 23:57:08 +1000518 bits = key_read(found, &cp);
Damien Millerf6d9e222000-06-18 14:50:44 +1000519 if (bits == 0) {
520 /* no key? check if there are options for this key */
521 int quoted = 0;
522 options = cp;
523 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
524 if (*cp == '\\' && cp[1] == '"')
525 cp++; /* Skip both */
526 else if (*cp == '"')
527 quoted = !quoted;
528 }
529 /* Skip remaining whitespace. */
530 for (; *cp == ' ' || *cp == '\t'; cp++)
531 ;
532 bits = key_read(found, &cp);
533 if (bits == 0) {
534 /* still no key? advance to next line*/
535 continue;
536 }
537 }
538 if (key_equal(found, key) &&
539 auth_parse_options(pw, options, linenum) == 1) {
Damien Millereba71ba2000-04-29 23:57:08 +1000540 found_key = 1;
541 debug("matching key found: file %s, line %ld",
542 file, linenum);
543 break;
544 }
545 }
546 restore_uid();
547 fclose(f);
548 key_free(found);
549 return found_key;
550}