2 Unix SMB/Netbios implementation.
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 */
27 extern int DEBUGLEVEL;
29 /* these are kept here to keep the string_combinations function simple */
30 static fstring this_user;
31 #if !(defined(WITH_PAM) || defined(KRB4_AUTH) || defined(KRB5_AUTH))
32 static fstring this_salt;
33 static fstring this_crypted;
39 #include <afs/kautils.h>
41 /*******************************************************************
42 check on AFS authentication
43 ********************************************************************/
44 static BOOL afs_auth(char *user, char *password)
46 long password_expires = 0;
49 /* For versions of AFS prior to 3.3, this routine has few arguments, */
50 /* but since I can't find the old documentation... :-) */
52 if (ka_UserAuthenticateGeneral
53 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
55 password, 0, /* lifetime, default */
56 &password_expires, /*days 'til it expires */
63 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
71 #include <dce/dce_error.h>
72 #include <dce/sec_login.h>
74 /*****************************************************************
75 This new version of the DFS_AUTH code was donated by Karsten Muuss
76 <muuss@or.uni-bonn.de>. It fixes the following problems with the
79 - Server credentials may expire
80 - Client credential cache files have wrong owner
81 - purge_context() function is called with invalid argument
83 This new code was modified to ensure that on exit the uid/gid is
84 still root, and the original directory is restored. JRA.
85 ******************************************************************/
87 sec_login_handle_t my_dce_sec_context;
88 int dcelogin_atmost_once = 0;
90 /*******************************************************************
91 check on a DCE/DFS authentication
92 ********************************************************************/
93 static BOOL dfs_auth(char *user, char *password)
98 signed32 expire_time, current_time;
99 boolean32 password_reset;
101 sec_passwd_rec_t passwd_rec;
102 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
103 unsigned char dce_errstr[dce_c_error_string_len];
106 if (dcelogin_atmost_once)
111 * We only go for a DCE login context if the given password
112 * matches that stored in the local password file..
113 * Assumes local passwd file is kept in sync w/ DCE RGY!
116 if (strcmp((char *)crypt(password, this_salt), this_crypted))
122 sec_login_get_current_context(&my_dce_sec_context, &err);
123 if (err != error_status_ok)
125 dce_error_inq_text(err, dce_errstr, &err2);
126 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
131 sec_login_certify_identity(my_dce_sec_context, &err);
132 if (err != error_status_ok)
134 dce_error_inq_text(err, dce_errstr, &err2);
135 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
140 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
141 if (err != error_status_ok)
143 dce_error_inq_text(err, dce_errstr, &err2);
144 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
151 if (expire_time < (current_time + 60))
154 sec_passwd_rec_t *key;
156 sec_login_get_pwent(my_dce_sec_context,
157 (sec_login_passwd_t *) & pw, &err);
158 if (err != error_status_ok)
160 dce_error_inq_text(err, dce_errstr, &err2);
161 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
166 sec_login_refresh_identity(my_dce_sec_context, &err);
167 if (err != error_status_ok)
169 dce_error_inq_text(err, dce_errstr, &err2);
170 DEBUG(0, ("DCE can't refresh identity. %s\n",
176 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
177 (unsigned char *)pw->pw_name,
178 sec_c_key_version_none,
179 (void **)&key, &err);
180 if (err != error_status_ok)
182 dce_error_inq_text(err, dce_errstr, &err2);
183 DEBUG(0, ("DCE can't get key for %s. %s\n",
184 pw->pw_name, dce_errstr));
189 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
190 &password_reset, &auth_src,
192 if (err != error_status_ok)
194 dce_error_inq_text(err, dce_errstr, &err2);
196 ("DCE can't validate and certify identity for %s. %s\n",
197 pw->pw_name, dce_errstr));
200 sec_key_mgmt_free_key(key, &err);
201 if (err != error_status_ok)
203 dce_error_inq_text(err, dce_errstr, &err2);
204 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
208 if (sec_login_setup_identity((unsigned char *)user,
210 &my_dce_sec_context, &err) == 0)
212 dce_error_inq_text(err, dce_errstr, &err2);
213 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
218 sec_login_get_pwent(my_dce_sec_context,
219 (sec_login_passwd_t *) & pw, &err);
220 if (err != error_status_ok)
222 dce_error_inq_text(err, dce_errstr, &err2);
223 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
228 sec_login_purge_context(&my_dce_sec_context, &err);
229 if (err != error_status_ok)
231 dce_error_inq_text(err, dce_errstr, &err2);
232 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
238 * NB. I'd like to change these to call something like become_user()
239 * instead but currently we don't have a connection
240 * context to become the correct user. This is already
241 * fairly platform specific code however, so I think
242 * this should be ok. I have added code to go
243 * back to being root on error though. JRA.
248 set_effective_gid(pw->pw_gid);
249 set_effective_uid(pw->pw_uid);
251 if (sec_login_setup_identity((unsigned char *)user,
253 &my_dce_sec_context, &err) == 0)
255 dce_error_inq_text(err, dce_errstr, &err2);
256 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
261 sec_login_get_pwent(my_dce_sec_context,
262 (sec_login_passwd_t *) & pw, &err);
263 if (err != error_status_ok)
265 dce_error_inq_text(err, dce_errstr, &err2);
266 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
270 passwd_rec.version_number = sec_passwd_c_version_none;
271 passwd_rec.pepper = NULL;
272 passwd_rec.key.key_type = sec_passwd_plain;
273 passwd_rec.key.tagged_union.plain = (idl_char *) password;
275 sec_login_validate_identity(my_dce_sec_context,
276 &passwd_rec, &password_reset,
278 if (err != error_status_ok)
280 dce_error_inq_text(err, dce_errstr, &err2);
282 ("DCE Identity Validation failed for principal %s: %s\n",
287 sec_login_certify_identity(my_dce_sec_context, &err);
288 if (err != error_status_ok)
290 dce_error_inq_text(err, dce_errstr, &err2);
291 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
295 if (auth_src != sec_login_auth_src_network)
297 DEBUG(0, ("DCE context has no network credentials.\n"));
300 sec_login_set_context(my_dce_sec_context, &err);
301 if (err != error_status_ok)
303 dce_error_inq_text(err, dce_errstr, &err2);
305 ("DCE login failed for principal %s, cant set context: %s\n",
308 sec_login_purge_context(&my_dce_sec_context, &err);
312 sec_login_get_pwent(my_dce_sec_context,
313 (sec_login_passwd_t *) & pw, &err);
314 if (err != error_status_ok)
316 dce_error_inq_text(err, dce_errstr, &err2);
317 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
321 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
322 user, sys_getpid()));
324 DEBUG(3, ("DCE principal: %s\n"
327 pw->pw_name, pw->pw_uid, pw->pw_gid));
328 DEBUG(3, (" info: %s\n"
331 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
333 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
334 if (err != error_status_ok)
336 dce_error_inq_text(err, dce_errstr, &err2);
337 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
341 set_effective_uid(0);
342 set_effective_gid(0);
345 ("DCE context expires: %s", asctime(localtime(&expire_time))));
347 dcelogin_atmost_once = 1;
352 /* Go back to root, JRA. */
353 set_effective_uid(0);
354 set_effective_gid(egid);
358 void dfs_unlogin(void)
362 unsigned char dce_errstr[dce_c_error_string_len];
364 sec_login_purge_context(&my_dce_sec_context, &err);
365 if (err != error_status_ok)
367 dce_error_inq_text(err, dce_errstr, &err2);
369 ("DCE purge login context failed for server instance %d: %s\n",
370 sys_getpid(), dce_errstr));
379 /*******************************************************************
380 check on Kerberos authentication
381 ********************************************************************/
382 static BOOL krb5_auth(char *user, char *password)
384 krb5_data tgtname = {
389 krb5_context kcontext;
390 krb5_principal kprinc;
391 krb5_principal server;
394 krb5_address **addrs = (krb5_address **) 0;
395 krb5_preauthtype *preauth = NULL;
396 krb5_keytab keytab = NULL;
398 krb5_ccache ccache = NULL;
402 if (retval = krb5_init_context(&kcontext))
407 if (retval = krb5_timeofday(kcontext, &now))
412 if (retval = krb5_cc_default(kcontext, &ccache))
417 if (retval = krb5_parse_name(kcontext, user, &kprinc))
424 kcreds.client = kprinc;
426 if ((retval = krb5_build_principal_ext(kcontext, &server,
427 krb5_princ_realm(kcontext,
430 krb5_princ_realm(kcontext,
432 tgtname.length, tgtname.data,
433 krb5_princ_realm(kcontext,
436 krb5_princ_realm(kcontext,
443 kcreds.server = server;
445 retval = krb5_get_in_tkt_with_password(kcontext,
450 password, 0, &kcreds, 0);
459 #endif /* KRB5_AUTH */
464 /*******************************************************************
465 check on Kerberos authentication
466 ********************************************************************/
467 static BOOL krb4_auth(char *user, char *password)
469 char realm[REALM_SZ];
470 char tkfile[MAXPATHLEN];
472 if (krb_get_lrealm(realm, 1) != KSUCCESS)
474 (void)safe_strcpy(realm, KRB_REALM, sizeof(realm) - 1);
477 (void)slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
480 krb_set_tkt_string(tkfile);
481 if (krb_verify_user(user, "", realm, password, 0, "rmcd") == KSUCCESS)
489 #endif /* KRB4_AUTH */
491 #ifdef LINUX_BIGCRYPT
492 /****************************************************************************
493 an enhanced crypt for Linux to handle password longer than 8 characters
494 ****************************************************************************/
495 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
497 #define LINUX_PASSWORD_SEG_CHARS 8
501 StrnCpy(salt, salt1, 2);
504 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
505 char *p = crypt(password, salt) + 2;
506 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
508 password += LINUX_PASSWORD_SEG_CHARS;
509 crypted += strlen(p);
517 /****************************************************************************
518 an enhanced crypt for OSF1
519 ****************************************************************************/
520 static char *osf1_bigcrypt(char *password, char *salt1)
522 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
527 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
528 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
531 StrnCpy(salt, salt1, 2);
532 StrnCpy(result, salt1, 2);
535 for (i = 0; i < parts; i++) {
536 p1 = crypt(p2, salt);
537 strncat(result, p1 + 2,
538 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
539 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
540 p2 += AUTH_CLEARTEXT_SEG_CHARS;
548 /****************************************************************************
549 apply a function to upper/lower case combinations
550 of a string and return true if one of them returns true.
551 try all combinations with N uppercase letters.
552 offset is the first char to try and change (start with 0)
553 it assumes the string starts lowercased
554 ****************************************************************************/
555 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (char *),
562 #ifdef PASSWORD_LENGTH
563 len = MIN(len, PASSWORD_LENGTH);
566 if (N <= 0 || offset >= len)
569 for (i = offset; i < (len - (N - 1)); i++) {
574 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
579 return (NT_STATUS_WRONG_PASSWORD);
582 /****************************************************************************
583 apply a function to upper/lower case combinations
584 of a string and return true if one of them returns true.
585 try all combinations with up to N uppercase letters.
586 offset is the first char to try and change (start with 0)
587 it assumes the string starts lowercased
588 ****************************************************************************/
589 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (char *), int N)
593 for (n = 1; n <= N; n++)
594 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
596 return NT_STATUS_WRONG_PASSWORD;
600 /****************************************************************************
601 core of password checking routine
602 ****************************************************************************/
603 static NTSTATUS password_check(char *password)
606 return smb_pam_passcheck(this_user, password);
607 #elif defined(KRB5_AUTH)
608 return krb5_auth(this_user, password) ? NT_STATUS_WRONG_PASSWORD : NT_STATUS_OK;
609 #elif defined(KRB4_AUTH)
610 return krb4_auth(this_user, password) ? NT_STATUS_WRONG_PASSWORD : NT_STATUS_OK;
616 if (afs_auth(this_user, password))
618 #endif /* WITH_AFS */
621 if (dfs_auth(this_user, password))
623 #endif /* WITH_DFS */
627 ret = (strcmp(osf1_bigcrypt(password, this_salt),
631 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
632 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
637 return NT_STATUS_WRONG_PASSWORD;
640 #endif /* OSF1_ENH_SEC */
643 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
647 return NT_STATUS_WRONG_PASSWORD;
650 #endif /* ULTRIX_AUTH */
652 #ifdef LINUX_BIGCRYPT
653 ret = (linux_bigcrypt(password, this_salt, this_crypted));
657 return NT_STATUS_WRONG_PASSWORD;
659 #endif /* LINUX_BIGCRYPT */
661 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
664 * Some systems have bigcrypt in the C library but might not
665 * actually use it for the password hashes (HPUX 10.20) is
666 * a noteable example. So we try bigcrypt first, followed
670 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
673 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
677 return NT_STATUS_WRONG_PASSWORD;
679 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
682 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
686 return NT_STATUS_WRONG_PASSWORD;
688 #endif /* HAVE_BIGCRYPT */
691 DEBUG(1, ("Warning - no crypt available\n"));
692 return NT_STATUS_LOGON_FAILURE;
693 #else /* HAVE_CRYPT */
694 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
698 return NT_STATUS_WRONG_PASSWORD;
700 #endif /* HAVE_CRYPT */
701 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
702 #endif /* WITH_PAM || KRB4_AUTH || KRB5_AUTH */
707 /****************************************************************************
708 CHECK if a username/password is OK
709 the function pointer fn() points to a function to call when a successful
710 match is found and is used to update the encrypted password file
711 return NT_STATUS_OK on correct match, appropriate error otherwise
712 ****************************************************************************/
714 NTSTATUS pass_check(struct passwd *pass, char *user, char *password,
715 int pwlen, BOOL (*fn) (char *, char *), BOOL run_cracker)
718 int level = lp_passwordlevel();
725 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
729 return NT_STATUS_LOGON_FAILURE;
731 if (((!*password) || (!pwlen)) && !lp_null_passwords())
732 return NT_STATUS_LOGON_FAILURE;
734 #if defined(WITH_PAM) || defined(KRB4_AUTH) || defined(KRB5_AUTH)
737 * If we're using PAM we want to short-circuit all the
738 * checks below and dive straight into the PAM code.
741 fstrcpy(this_user, user);
743 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
745 #else /* Not using PAM or Kerebos */
747 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
750 DEBUG(3, ("Couldn't find user %s\n", user));
751 return NT_STATUS_NO_SUCH_USER;
758 /* many shadow systems require you to be root to get
759 the password, in most cases this should already be
760 the case when this function is called, except
761 perhaps for IPC password changing requests */
763 spass = getspnam(pass->pw_name);
764 if (spass && spass->sp_pwdp)
765 pstrcpy(pass->pw_passwd, spass->sp_pwdp);
767 #elif defined(IA_UINFO)
769 /* Need to get password with SVR4.2's ia_ functions
770 instead of get{sp,pw}ent functions. Required by
771 UnixWare 2.x, tested on version
772 2.1. (tangent@cyberport.com) */
774 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
775 ia_get_logpwd(uinfo, &(pass->pw_passwd));
779 #ifdef HAVE_GETPRPWNAM
781 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
782 if (pr_pw && pr_pw->ufld.fd_encrypt)
783 pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt);
789 struct pr_passwd *mypasswd;
790 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
792 mypasswd = getprpwnam(user);
794 fstrcpy(pass->pw_name, mypasswd->ufld.fd_name);
795 fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt);
798 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
806 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
808 fstrcpy(pass->pw_passwd, ap->a_password);
814 /* extract relevant info */
815 fstrcpy(this_salt, pass->pw_passwd);
817 #if defined(HAVE_TRUNCATED_SALT)
818 /* crypt on some platforms (HPUX in particular)
819 won't work with more than 2 salt characters. */
823 fstrcpy(this_crypted, pass->pw_passwd);
825 if (!*this_crypted) {
826 if (!lp_null_passwords()) {
827 DEBUG(2, ("Disallowing %s with null password\n",
829 return NT_STATUS_LOGON_FAILURE;
833 ("Allowing access to %s with null password\n",
839 #endif /* defined(WITH_PAM) || defined(KRB4_AUTH) || defined(KRB5_AUTH) */
841 /* try it as it came to us */
842 nt_status = password_check(password);
843 if NT_STATUS_IS_OK(nt_status) {
848 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
849 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
857 /* if the password was given to us with mixed case then we don't
858 * need to proceed as we know it hasn't been case modified by the
860 if (strhasupper(password) && strhaslower(password)) {
864 /* make a copy of it */
865 StrnCpy(pass2, password, sizeof(pstring) - 1);
867 /* try all lowercase if it's currently all uppercase */
868 if (strhasupper(password)) {
870 if NT_STATUS_IS_OK(nt_status = password_check(password)) {
880 fstrcpy(password, pass2);
881 return NT_STATUS_WRONG_PASSWORD;
884 /* last chance - all combinations of up to level chars upper! */
888 if NT_STATUS_IS_OK(nt_status = string_combinations(password, password_check, level)) {
895 fstrcpy(password, pass2);
897 return NT_STATUS_WRONG_PASSWORD;