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 /* these are kept here to keep the string_combinations function simple */
28 static fstring this_user;
29 #if !(defined(WITH_PAM) || defined(KRB4_AUTH) || defined(KRB5_AUTH))
30 static fstring this_salt;
31 static fstring this_crypted;
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;
47 /* For versions of AFS prior to 3.3, this routine has few arguments, */
48 /* but since I can't find the old documentation... :-) */
50 if (ka_UserAuthenticateGeneral
51 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
53 password, 0, /* lifetime, default */
54 &password_expires, /*days 'til it expires */
61 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
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
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)
96 signed32 expire_time, current_time;
97 boolean32 password_reset;
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];
104 if (dcelogin_atmost_once)
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))
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));
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));
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));
149 if (expire_time < (current_time + 60))
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));
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",
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));
187 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
188 &password_reset, &auth_src,
190 if (err != error_status_ok)
192 dce_error_inq_text(err, dce_errstr, &err2);
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,
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",
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));
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));
236 * NB. I'd like to change these to call something like become_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.
246 set_effective_gid(pw->pw_gid);
247 set_effective_uid(pw->pw_uid);
249 if (sec_login_setup_identity((unsigned char *)user,
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",
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));
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,
276 if (err != error_status_ok)
278 dce_error_inq_text(err, dce_errstr, &err2);
280 ("DCE Identity Validation failed for principal %s: %s\n",
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));
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);
303 ("DCE login failed for principal %s, cant set context: %s\n",
306 sec_login_purge_context(&my_dce_sec_context, &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));
319 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
320 user, sys_getpid()));
322 DEBUG(3, ("DCE principal: %s\n"
325 pw->pw_name, pw->pw_uid, pw->pw_gid));
326 DEBUG(3, (" info: %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));
339 set_effective_uid(0);
340 set_effective_gid(0);
343 ("DCE context expires: %s", asctime(localtime(&expire_time))));
345 dcelogin_atmost_once = 1;
350 /* Go back to root, JRA. */
351 set_effective_uid(0);
352 set_effective_gid(egid);
356 void dfs_unlogin(void)
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);
367 ("DCE purge login context failed for server instance %d: %s\n",
368 sys_getpid(), dce_errstr));
377 /*******************************************************************
378 check on Kerberos authentication
379 ********************************************************************/
380 static BOOL krb5_auth(char *user, char *password)
382 krb5_data tgtname = {
387 krb5_context kcontext;
388 krb5_principal kprinc;
389 krb5_principal server;
392 krb5_address **addrs = (krb5_address **) 0;
393 krb5_preauthtype *preauth = NULL;
394 krb5_keytab keytab = NULL;
396 krb5_ccache ccache = NULL;
400 if (retval = krb5_init_context(&kcontext))
405 if (retval = krb5_timeofday(kcontext, &now))
410 if (retval = krb5_cc_default(kcontext, &ccache))
415 if (retval = krb5_parse_name(kcontext, user, &kprinc))
422 kcreds.client = kprinc;
424 if ((retval = krb5_build_principal_ext(kcontext, &server,
425 krb5_princ_realm(kcontext,
428 krb5_princ_realm(kcontext,
430 tgtname.length, tgtname.data,
431 krb5_princ_realm(kcontext,
434 krb5_princ_realm(kcontext,
441 kcreds.server = server;
443 retval = krb5_get_in_tkt_with_password(kcontext,
448 password, 0, &kcreds, 0);
457 #endif /* KRB5_AUTH */
462 /*******************************************************************
463 check on Kerberos authentication
464 ********************************************************************/
465 static BOOL krb4_auth(char *user, char *password)
467 char realm[REALM_SZ];
468 char tkfile[MAXPATHLEN];
470 if (krb_get_lrealm(realm, 1) != KSUCCESS)
472 (void)safe_strcpy(realm, KRB_REALM, sizeof(realm) - 1);
475 (void)slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
478 krb_set_tkt_string(tkfile);
479 if (krb_verify_user(user, "", realm, password, 0, "rmcd") == KSUCCESS)
487 #endif /* KRB4_AUTH */
489 #ifdef LINUX_BIGCRYPT
490 /****************************************************************************
491 an enhanced crypt for Linux to handle password longer than 8 characters
492 ****************************************************************************/
493 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
495 #define LINUX_PASSWORD_SEG_CHARS 8
499 StrnCpy(salt, salt1, 2);
502 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
503 char *p = crypt(password, salt) + 2;
504 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
506 password += LINUX_PASSWORD_SEG_CHARS;
507 crypted += strlen(p);
515 /****************************************************************************
516 an enhanced crypt for OSF1
517 ****************************************************************************/
518 static char *osf1_bigcrypt(char *password, char *salt1)
520 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
525 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
526 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
529 StrnCpy(salt, salt1, 2);
530 StrnCpy(result, salt1, 2);
533 for (i = 0; i < parts; i++) {
534 p1 = crypt(p2, salt);
535 strncat(result, p1 + 2,
536 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
537 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
538 p2 += AUTH_CLEARTEXT_SEG_CHARS;
546 /****************************************************************************
547 apply a function to upper/lower case combinations
548 of a string and return true if one of them returns true.
549 try all combinations with N uppercase letters.
550 offset is the first char to try and change (start with 0)
551 it assumes the string starts lowercased
552 ****************************************************************************/
553 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (char *),
560 #ifdef PASSWORD_LENGTH
561 len = MIN(len, PASSWORD_LENGTH);
564 if (N <= 0 || offset >= len)
567 for (i = offset; i < (len - (N - 1)); i++) {
572 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
577 return (NT_STATUS_WRONG_PASSWORD);
580 /****************************************************************************
581 apply a function to upper/lower case combinations
582 of a string and return true if one of them returns true.
583 try all combinations with up to N uppercase letters.
584 offset is the first char to try and change (start with 0)
585 it assumes the string starts lowercased
586 ****************************************************************************/
587 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (char *), int N)
591 for (n = 1; n <= N; n++)
592 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
594 return NT_STATUS_WRONG_PASSWORD;
598 /****************************************************************************
599 core of password checking routine
600 ****************************************************************************/
601 static NTSTATUS password_check(char *password)
604 return smb_pam_passcheck(this_user, password);
605 #elif defined(KRB5_AUTH)
606 return krb5_auth(this_user, password) ? NT_STATUS_WRONG_PASSWORD : NT_STATUS_OK;
607 #elif defined(KRB4_AUTH)
608 return krb4_auth(this_user, password) ? NT_STATUS_WRONG_PASSWORD : NT_STATUS_OK;
614 if (afs_auth(this_user, password))
616 #endif /* WITH_AFS */
619 if (dfs_auth(this_user, password))
621 #endif /* WITH_DFS */
625 ret = (strcmp(osf1_bigcrypt(password, this_salt),
629 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
630 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
635 return NT_STATUS_WRONG_PASSWORD;
638 #endif /* OSF1_ENH_SEC */
641 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
645 return NT_STATUS_WRONG_PASSWORD;
648 #endif /* ULTRIX_AUTH */
650 #ifdef LINUX_BIGCRYPT
651 ret = (linux_bigcrypt(password, this_salt, this_crypted));
655 return NT_STATUS_WRONG_PASSWORD;
657 #endif /* LINUX_BIGCRYPT */
659 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
662 * Some systems have bigcrypt in the C library but might not
663 * actually use it for the password hashes (HPUX 10.20) is
664 * a noteable example. So we try bigcrypt first, followed
668 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
671 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
675 return NT_STATUS_WRONG_PASSWORD;
677 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
680 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
684 return NT_STATUS_WRONG_PASSWORD;
686 #endif /* HAVE_BIGCRYPT */
689 DEBUG(1, ("Warning - no crypt available\n"));
690 return NT_STATUS_LOGON_FAILURE;
691 #else /* HAVE_CRYPT */
692 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
696 return NT_STATUS_WRONG_PASSWORD;
698 #endif /* HAVE_CRYPT */
699 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
700 #endif /* WITH_PAM || KRB4_AUTH || KRB5_AUTH */
705 /****************************************************************************
706 CHECK if a username/password is OK
707 the function pointer fn() points to a function to call when a successful
708 match is found and is used to update the encrypted password file
709 return NT_STATUS_OK on correct match, appropriate error otherwise
710 ****************************************************************************/
712 NTSTATUS pass_check(struct passwd *pass, char *user, char *password,
713 int pwlen, BOOL (*fn) (char *, char *), BOOL run_cracker)
716 int level = lp_passwordlevel();
723 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
727 return NT_STATUS_LOGON_FAILURE;
729 if (((!*password) || (!pwlen)) && !lp_null_passwords())
730 return NT_STATUS_LOGON_FAILURE;
732 #if defined(WITH_PAM) || defined(KRB4_AUTH) || defined(KRB5_AUTH)
735 * If we're using PAM we want to short-circuit all the
736 * checks below and dive straight into the PAM code.
739 fstrcpy(this_user, user);
741 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
743 #else /* Not using PAM or Kerebos */
745 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
748 DEBUG(3, ("Couldn't find user %s\n", user));
749 return NT_STATUS_NO_SUCH_USER;
756 /* many shadow systems require you to be root to get
757 the password, in most cases this should already be
758 the case when this function is called, except
759 perhaps for IPC password changing requests */
761 spass = getspnam(pass->pw_name);
762 if (spass && spass->sp_pwdp)
763 pstrcpy(pass->pw_passwd, spass->sp_pwdp);
765 #elif defined(IA_UINFO)
767 /* Need to get password with SVR4.2's ia_ functions
768 instead of get{sp,pw}ent functions. Required by
769 UnixWare 2.x, tested on version
770 2.1. (tangent@cyberport.com) */
772 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
773 ia_get_logpwd(uinfo, &(pass->pw_passwd));
777 #ifdef HAVE_GETPRPWNAM
779 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
780 if (pr_pw && pr_pw->ufld.fd_encrypt)
781 pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt);
787 struct pr_passwd *mypasswd;
788 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
790 mypasswd = getprpwnam(user);
792 fstrcpy(pass->pw_name, mypasswd->ufld.fd_name);
793 fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt);
796 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
804 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
806 fstrcpy(pass->pw_passwd, ap->a_password);
812 /* extract relevant info */
813 fstrcpy(this_salt, pass->pw_passwd);
815 #if defined(HAVE_TRUNCATED_SALT)
816 /* crypt on some platforms (HPUX in particular)
817 won't work with more than 2 salt characters. */
821 fstrcpy(this_crypted, pass->pw_passwd);
823 if (!*this_crypted) {
824 if (!lp_null_passwords()) {
825 DEBUG(2, ("Disallowing %s with null password\n",
827 return NT_STATUS_LOGON_FAILURE;
831 ("Allowing access to %s with null password\n",
837 #endif /* defined(WITH_PAM) || defined(KRB4_AUTH) || defined(KRB5_AUTH) */
839 /* try it as it came to us */
840 nt_status = password_check(password);
841 if NT_STATUS_IS_OK(nt_status) {
846 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
847 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
855 /* if the password was given to us with mixed case then we don't
856 * need to proceed as we know it hasn't been case modified by the
858 if (strhasupper(password) && strhaslower(password)) {
862 /* make a copy of it */
863 StrnCpy(pass2, password, sizeof(pstring) - 1);
865 /* try all lowercase if it's currently all uppercase */
866 if (strhasupper(password)) {
868 if NT_STATUS_IS_OK(nt_status = password_check(password)) {
878 fstrcpy(password, pass2);
879 return NT_STATUS_WRONG_PASSWORD;
882 /* last chance - all combinations of up to level chars upper! */
886 if NT_STATUS_IS_OK(nt_status = string_combinations(password, password_check, level)) {
893 fstrcpy(password, pass2);
895 return NT_STATUS_WRONG_PASSWORD;