Remove built-in support for clear-text kerberos authentication.
[Samba/ekacnet.git] / source / auth / pass_check.c
blob77839e4bb0cdb5d6f8107cd8693137c501cf38bf
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 Password checking
5 Copyright (C) Andrew Tridgell 1992-1998
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* this module is for checking a username/password against a system
23 password database. The SMB encrypted password support is elsewhere */
25 #include "includes.h"
27 /* these are kept here to keep the string_combinations function simple */
28 static fstring this_user;
29 #if !defined(WITH_PAM)
30 static fstring this_salt;
31 static fstring this_crypted;
32 #endif
34 #ifdef WITH_AFS
36 #include <afs/stds.h>
37 #include <afs/kautils.h>
39 /*******************************************************************
40 check on AFS authentication
41 ********************************************************************/
42 static BOOL afs_auth(char *user, char *password)
44 long password_expires = 0;
45 char *reason;
47 /* For versions of AFS prior to 3.3, this routine has few arguments, */
48 /* but since I can't find the old documentation... :-) */
49 setpag();
50 if (ka_UserAuthenticateGeneral
51 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
52 (char *)0, /* cell */
53 password, 0, /* lifetime, default */
54 &password_expires, /*days 'til it expires */
55 0, /* spare 2 */
56 &reason) == 0)
58 return (True);
60 DEBUG(1,
61 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
62 return (False);
64 #endif
67 #ifdef WITH_DFS
69 #include <dce/dce_error.h>
70 #include <dce/sec_login.h>
72 /*****************************************************************
73 This new version of the DFS_AUTH code was donated by Karsten Muuss
74 <muuss@or.uni-bonn.de>. It fixes the following problems with the
75 old code :
77 - Server credentials may expire
78 - Client credential cache files have wrong owner
79 - purge_context() function is called with invalid argument
81 This new code was modified to ensure that on exit the uid/gid is
82 still root, and the original directory is restored. JRA.
83 ******************************************************************/
85 sec_login_handle_t my_dce_sec_context;
86 int dcelogin_atmost_once = 0;
88 /*******************************************************************
89 check on a DCE/DFS authentication
90 ********************************************************************/
91 static BOOL dfs_auth(char *user, char *password)
93 error_status_t err;
94 int err2;
95 int prterr;
96 signed32 expire_time, current_time;
97 boolean32 password_reset;
98 struct passwd *pw;
99 sec_passwd_rec_t passwd_rec;
100 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
101 unsigned char dce_errstr[dce_c_error_string_len];
102 gid_t egid;
104 if (dcelogin_atmost_once)
105 return (False);
107 #ifdef HAVE_CRYPT
109 * We only go for a DCE login context if the given password
110 * matches that stored in the local password file..
111 * Assumes local passwd file is kept in sync w/ DCE RGY!
114 if (strcmp((char *)crypt(password, this_salt), this_crypted))
116 return (False);
118 #endif
120 sec_login_get_current_context(&my_dce_sec_context, &err);
121 if (err != error_status_ok)
123 dce_error_inq_text(err, dce_errstr, &err2);
124 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
126 return (False);
129 sec_login_certify_identity(my_dce_sec_context, &err);
130 if (err != error_status_ok)
132 dce_error_inq_text(err, dce_errstr, &err2);
133 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
135 return (False);
138 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
139 if (err != error_status_ok)
141 dce_error_inq_text(err, dce_errstr, &err2);
142 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
144 return (False);
147 time(&current_time);
149 if (expire_time < (current_time + 60))
151 struct passwd *pw;
152 sec_passwd_rec_t *key;
154 sec_login_get_pwent(my_dce_sec_context,
155 (sec_login_passwd_t *) & pw, &err);
156 if (err != error_status_ok)
158 dce_error_inq_text(err, dce_errstr, &err2);
159 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
161 return (False);
164 sec_login_refresh_identity(my_dce_sec_context, &err);
165 if (err != error_status_ok)
167 dce_error_inq_text(err, dce_errstr, &err2);
168 DEBUG(0, ("DCE can't refresh identity. %s\n",
169 dce_errstr));
171 return (False);
174 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
175 (unsigned char *)pw->pw_name,
176 sec_c_key_version_none,
177 (void **)&key, &err);
178 if (err != error_status_ok)
180 dce_error_inq_text(err, dce_errstr, &err2);
181 DEBUG(0, ("DCE can't get key for %s. %s\n",
182 pw->pw_name, dce_errstr));
184 return (False);
187 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
188 &password_reset, &auth_src,
189 &err);
190 if (err != error_status_ok)
192 dce_error_inq_text(err, dce_errstr, &err2);
193 DEBUG(0,
194 ("DCE can't validate and certify identity for %s. %s\n",
195 pw->pw_name, dce_errstr));
198 sec_key_mgmt_free_key(key, &err);
199 if (err != error_status_ok)
201 dce_error_inq_text(err, dce_errstr, &err2);
202 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
206 if (sec_login_setup_identity((unsigned char *)user,
207 sec_login_no_flags,
208 &my_dce_sec_context, &err) == 0)
210 dce_error_inq_text(err, dce_errstr, &err2);
211 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
212 user, dce_errstr));
213 return (False);
216 sec_login_get_pwent(my_dce_sec_context,
217 (sec_login_passwd_t *) & pw, &err);
218 if (err != error_status_ok)
220 dce_error_inq_text(err, dce_errstr, &err2);
221 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
223 return (False);
226 sec_login_purge_context(&my_dce_sec_context, &err);
227 if (err != error_status_ok)
229 dce_error_inq_text(err, dce_errstr, &err2);
230 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
232 return (False);
236 * NB. I'd like to change these to call something like change_to_user()
237 * instead but currently we don't have a connection
238 * context to become the correct user. This is already
239 * fairly platform specific code however, so I think
240 * this should be ok. I have added code to go
241 * back to being root on error though. JRA.
244 egid = getegid();
246 set_effective_gid(pw->pw_gid);
247 set_effective_uid(pw->pw_uid);
249 if (sec_login_setup_identity((unsigned char *)user,
250 sec_login_no_flags,
251 &my_dce_sec_context, &err) == 0)
253 dce_error_inq_text(err, dce_errstr, &err2);
254 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
255 user, dce_errstr));
256 goto err;
259 sec_login_get_pwent(my_dce_sec_context,
260 (sec_login_passwd_t *) & pw, &err);
261 if (err != error_status_ok)
263 dce_error_inq_text(err, dce_errstr, &err2);
264 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
265 goto err;
268 passwd_rec.version_number = sec_passwd_c_version_none;
269 passwd_rec.pepper = NULL;
270 passwd_rec.key.key_type = sec_passwd_plain;
271 passwd_rec.key.tagged_union.plain = (idl_char *) password;
273 sec_login_validate_identity(my_dce_sec_context,
274 &passwd_rec, &password_reset,
275 &auth_src, &err);
276 if (err != error_status_ok)
278 dce_error_inq_text(err, dce_errstr, &err2);
279 DEBUG(0,
280 ("DCE Identity Validation failed for principal %s: %s\n",
281 user, dce_errstr));
282 goto err;
285 sec_login_certify_identity(my_dce_sec_context, &err);
286 if (err != error_status_ok)
288 dce_error_inq_text(err, dce_errstr, &err2);
289 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
290 goto err;
293 if (auth_src != sec_login_auth_src_network)
295 DEBUG(0, ("DCE context has no network credentials.\n"));
298 sec_login_set_context(my_dce_sec_context, &err);
299 if (err != error_status_ok)
301 dce_error_inq_text(err, dce_errstr, &err2);
302 DEBUG(0,
303 ("DCE login failed for principal %s, cant set context: %s\n",
304 user, dce_errstr));
306 sec_login_purge_context(&my_dce_sec_context, &err);
307 goto err;
310 sec_login_get_pwent(my_dce_sec_context,
311 (sec_login_passwd_t *) & pw, &err);
312 if (err != error_status_ok)
314 dce_error_inq_text(err, dce_errstr, &err2);
315 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
316 goto err;
319 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
320 user, sys_getpid()));
322 DEBUG(3, ("DCE principal: %s\n"
323 " uid: %d\n"
324 " gid: %d\n",
325 pw->pw_name, pw->pw_uid, pw->pw_gid));
326 DEBUG(3, (" info: %s\n"
327 " dir: %s\n"
328 " shell: %s\n",
329 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
331 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
332 if (err != error_status_ok)
334 dce_error_inq_text(err, dce_errstr, &err2);
335 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
336 goto err;
339 set_effective_uid(0);
340 set_effective_gid(0);
342 DEBUG(0,
343 ("DCE context expires: %s", asctime(localtime(&expire_time))));
345 dcelogin_atmost_once = 1;
346 return (True);
348 err:
350 /* Go back to root, JRA. */
351 set_effective_uid(0);
352 set_effective_gid(egid);
353 return (False);
356 void dfs_unlogin(void)
358 error_status_t err;
359 int err2;
360 unsigned char dce_errstr[dce_c_error_string_len];
362 sec_login_purge_context(&my_dce_sec_context, &err);
363 if (err != error_status_ok)
365 dce_error_inq_text(err, dce_errstr, &err2);
366 DEBUG(0,
367 ("DCE purge login context failed for server instance %d: %s\n",
368 sys_getpid(), dce_errstr));
371 #endif
373 #ifdef LINUX_BIGCRYPT
374 /****************************************************************************
375 an enhanced crypt for Linux to handle password longer than 8 characters
376 ****************************************************************************/
377 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
379 #define LINUX_PASSWORD_SEG_CHARS 8
380 char salt[3];
381 int i;
383 StrnCpy(salt, salt1, 2);
384 crypted += 2;
386 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
387 char *p = crypt(password, salt) + 2;
388 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
389 return (0);
390 password += LINUX_PASSWORD_SEG_CHARS;
391 crypted += strlen(p);
394 return (1);
396 #endif
398 #ifdef OSF1_ENH_SEC
399 /****************************************************************************
400 an enhanced crypt for OSF1
401 ****************************************************************************/
402 static char *osf1_bigcrypt(char *password, char *salt1)
404 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
405 char *p1;
406 char *p2 = password;
407 char salt[3];
408 int i;
409 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
410 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
411 parts++;
413 StrnCpy(salt, salt1, 2);
414 StrnCpy(result, salt1, 2);
415 result[2] = '\0';
417 for (i = 0; i < parts; i++) {
418 p1 = crypt(p2, salt);
419 strncat(result, p1 + 2,
420 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
421 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
422 p2 += AUTH_CLEARTEXT_SEG_CHARS;
425 return (result);
427 #endif
430 /****************************************************************************
431 apply a function to upper/lower case combinations
432 of a string and return true if one of them returns true.
433 try all combinations with N uppercase letters.
434 offset is the first char to try and change (start with 0)
435 it assumes the string starts lowercased
436 ****************************************************************************/
437 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (char *),
438 int N)
440 int len = strlen(s);
441 int i;
442 NTSTATUS nt_status;
444 #ifdef PASSWORD_LENGTH
445 len = MIN(len, PASSWORD_LENGTH);
446 #endif
448 if (N <= 0 || offset >= len)
449 return (fn(s));
451 for (i = offset; i < (len - (N - 1)); i++) {
452 char c = s[i];
453 if (!islower(c))
454 continue;
455 s[i] = toupper(c);
456 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
457 return (nt_status);
459 s[i] = c;
461 return (NT_STATUS_WRONG_PASSWORD);
464 /****************************************************************************
465 apply a function to upper/lower case combinations
466 of a string and return true if one of them returns true.
467 try all combinations with up to N uppercase letters.
468 offset is the first char to try and change (start with 0)
469 it assumes the string starts lowercased
470 ****************************************************************************/
471 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (char *), int N)
473 int n;
474 NTSTATUS nt_status;
475 for (n = 1; n <= N; n++)
476 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
477 return nt_status;
478 return NT_STATUS_WRONG_PASSWORD;
482 /****************************************************************************
483 core of password checking routine
484 ****************************************************************************/
485 static NTSTATUS password_check(char *password)
487 #ifdef WITH_PAM
488 return smb_pam_passcheck(this_user, password);
489 #else
491 BOOL ret;
493 #ifdef WITH_AFS
494 if (afs_auth(this_user, password))
495 return NT_STATUS_OK;
496 #endif /* WITH_AFS */
498 #ifdef WITH_DFS
499 if (dfs_auth(this_user, password))
500 return NT_STATUS_OK;
501 #endif /* WITH_DFS */
503 #ifdef OSF1_ENH_SEC
505 ret = (strcmp(osf1_bigcrypt(password, this_salt),
506 this_crypted) == 0);
507 if (!ret) {
508 DEBUG(2,
509 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
510 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
512 if (ret) {
513 return NT_STATUS_OK;
514 } else {
515 return NT_STATUS_WRONG_PASSWORD;
518 #endif /* OSF1_ENH_SEC */
520 #ifdef ULTRIX_AUTH
521 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
522 if (ret) {
523 return NT_STATUS_OK;
524 } else {
525 return NT_STATUS_WRONG_PASSWORD;
528 #endif /* ULTRIX_AUTH */
530 #ifdef LINUX_BIGCRYPT
531 ret = (linux_bigcrypt(password, this_salt, this_crypted));
532 if (ret) {
533 return NT_STATUS_OK;
534 } else {
535 return NT_STATUS_WRONG_PASSWORD;
537 #endif /* LINUX_BIGCRYPT */
539 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
542 * Some systems have bigcrypt in the C library but might not
543 * actually use it for the password hashes (HPUX 10.20) is
544 * a noteable example. So we try bigcrypt first, followed
545 * by crypt.
548 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
549 return NT_STATUS_OK;
550 else
551 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
552 if (ret) {
553 return NT_STATUS_OK;
554 } else {
555 return NT_STATUS_WRONG_PASSWORD;
557 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
559 #ifdef HAVE_BIGCRYPT
560 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
561 if (ret) {
562 return NT_STATUS_OK;
563 } else {
564 return NT_STATUS_WRONG_PASSWORD;
566 #endif /* HAVE_BIGCRYPT */
568 #ifndef HAVE_CRYPT
569 DEBUG(1, ("Warning - no crypt available\n"));
570 return NT_STATUS_LOGON_FAILURE;
571 #else /* HAVE_CRYPT */
572 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
573 if (ret) {
574 return NT_STATUS_OK;
575 } else {
576 return NT_STATUS_WRONG_PASSWORD;
578 #endif /* HAVE_CRYPT */
579 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
580 #endif /* WITH_PAM || KRB4_AUTH || KRB5_AUTH */
585 /****************************************************************************
586 CHECK if a username/password is OK
587 the function pointer fn() points to a function to call when a successful
588 match is found and is used to update the encrypted password file
589 return NT_STATUS_OK on correct match, appropriate error otherwise
590 ****************************************************************************/
592 NTSTATUS pass_check(struct passwd *pass, char *user, char *password,
593 int pwlen, BOOL (*fn) (char *, char *), BOOL run_cracker)
595 pstring pass2;
596 int level = lp_passwordlevel();
598 NTSTATUS nt_status;
599 if (password)
600 password[pwlen] = 0;
602 #if DEBUG_PASSWORD
603 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
604 #endif
606 if (!password)
607 return NT_STATUS_LOGON_FAILURE;
609 if (((!*password) || (!pwlen)) && !lp_null_passwords())
610 return NT_STATUS_LOGON_FAILURE;
612 #if defined(WITH_PAM)
615 * If we're using PAM we want to short-circuit all the
616 * checks below and dive straight into the PAM code.
619 fstrcpy(this_user, user);
621 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
623 #else /* Not using PAM or Kerebos */
625 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
627 if (!pass) {
628 DEBUG(3, ("Couldn't find user %s\n", user));
629 return NT_STATUS_NO_SUCH_USER;
632 #ifdef HAVE_GETSPNAM
634 struct spwd *spass;
636 /* many shadow systems require you to be root to get
637 the password, in most cases this should already be
638 the case when this function is called, except
639 perhaps for IPC password changing requests */
641 spass = getspnam(pass->pw_name);
642 if (spass && spass->sp_pwdp)
643 pstrcpy(pass->pw_passwd, spass->sp_pwdp);
645 #elif defined(IA_UINFO)
647 /* Need to get password with SVR4.2's ia_ functions
648 instead of get{sp,pw}ent functions. Required by
649 UnixWare 2.x, tested on version
650 2.1. (tangent@cyberport.com) */
651 uinfo_t uinfo;
652 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
653 ia_get_logpwd(uinfo, &(pass->pw_passwd));
655 #endif
657 #ifdef HAVE_GETPRPWNAM
659 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
660 if (pr_pw && pr_pw->ufld.fd_encrypt)
661 pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt);
663 #endif
665 #ifdef OSF1_ENH_SEC
667 struct pr_passwd *mypasswd;
668 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
669 user));
670 mypasswd = getprpwnam(user);
671 if (mypasswd) {
672 fstrcpy(pass->pw_name, mypasswd->ufld.fd_name);
673 fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt);
674 } else {
675 DEBUG(5,
676 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
677 user));
680 #endif
682 #ifdef ULTRIX_AUTH
684 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
685 if (ap) {
686 fstrcpy(pass->pw_passwd, ap->a_password);
687 endauthent();
690 #endif
692 /* extract relevant info */
693 fstrcpy(this_salt, pass->pw_passwd);
695 #if defined(HAVE_TRUNCATED_SALT)
696 /* crypt on some platforms (HPUX in particular)
697 won't work with more than 2 salt characters. */
698 this_salt[2] = 0;
699 #endif
701 fstrcpy(this_crypted, pass->pw_passwd);
703 if (!*this_crypted) {
704 if (!lp_null_passwords()) {
705 DEBUG(2, ("Disallowing %s with null password\n",
706 this_user));
707 return NT_STATUS_LOGON_FAILURE;
709 if (!*password) {
710 DEBUG(3,
711 ("Allowing access to %s with null password\n",
712 this_user));
713 return NT_STATUS_OK;
717 #endif /* defined(WITH_PAM) */
719 /* try it as it came to us */
720 nt_status = password_check(password);
721 if NT_STATUS_IS_OK(nt_status) {
722 if (fn) {
723 fn(user, password);
725 return (nt_status);
726 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
727 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
728 return (nt_status);
731 if (!run_cracker) {
732 return (nt_status);
735 /* if the password was given to us with mixed case then we don't
736 * need to proceed as we know it hasn't been case modified by the
737 * client */
738 if (strhasupper(password) && strhaslower(password)) {
739 return nt_status;
742 /* make a copy of it */
743 StrnCpy(pass2, password, sizeof(pstring) - 1);
745 /* try all lowercase if it's currently all uppercase */
746 if (strhasupper(password)) {
747 strlower(password);
748 if NT_STATUS_IS_OK(nt_status = password_check(password)) {
749 if (fn)
750 fn(user, password);
751 return (nt_status);
755 /* give up? */
756 if (level < 1) {
757 /* restore it */
758 fstrcpy(password, pass2);
759 return NT_STATUS_WRONG_PASSWORD;
762 /* last chance - all combinations of up to level chars upper! */
763 strlower(password);
766 if NT_STATUS_IS_OK(nt_status = string_combinations(password, password_check, level)) {
767 if (fn)
768 fn(user, password);
769 return nt_status;
772 /* restore it */
773 fstrcpy(password, pass2);
775 return NT_STATUS_WRONG_PASSWORD;