2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /* this module is for checking a username/password against a system
22 password database. The SMB encrypted password support is elsewhere */
26 /* these are kept here to keep the string_combinations function simple */
27 static fstring this_user;
28 #if !defined(WITH_PAM)
29 static fstring this_salt;
30 static fstring this_crypted;
36 #include <afs/kautils.h>
38 /*******************************************************************
39 check on AFS authentication
40 ********************************************************************/
41 static BOOL afs_auth(char *user, char *password)
43 long password_expires = 0;
46 /* For versions of AFS prior to 3.3, this routine has few arguments, */
47 /* but since I can't find the old documentation... :-) */
49 if (ka_UserAuthenticateGeneral
50 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
52 password, 0, /* lifetime, default */
53 &password_expires, /*days 'til it expires */
60 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
68 #include <dce/dce_error.h>
69 #include <dce/sec_login.h>
71 /*****************************************************************
72 This new version of the DFS_AUTH code was donated by Karsten Muuss
73 <muuss@or.uni-bonn.de>. It fixes the following problems with the
76 - Server credentials may expire
77 - Client credential cache files have wrong owner
78 - purge_context() function is called with invalid argument
80 This new code was modified to ensure that on exit the uid/gid is
81 still root, and the original directory is restored. JRA.
82 ******************************************************************/
84 sec_login_handle_t my_dce_sec_context;
85 int dcelogin_atmost_once = 0;
87 /*******************************************************************
88 check on a DCE/DFS authentication
89 ********************************************************************/
90 static BOOL dfs_auth(char *user, char *password)
95 signed32 expire_time, current_time;
96 boolean32 password_reset;
98 sec_passwd_rec_t passwd_rec;
99 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
100 unsigned char dce_errstr[dce_c_error_string_len];
103 if (dcelogin_atmost_once)
108 * We only go for a DCE login context if the given password
109 * matches that stored in the local password file..
110 * Assumes local passwd file is kept in sync w/ DCE RGY!
113 if (strcmp((char *)crypt(password, this_salt), this_crypted))
119 sec_login_get_current_context(&my_dce_sec_context, &err);
120 if (err != error_status_ok)
122 dce_error_inq_text(err, dce_errstr, &err2);
123 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
128 sec_login_certify_identity(my_dce_sec_context, &err);
129 if (err != error_status_ok)
131 dce_error_inq_text(err, dce_errstr, &err2);
132 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
137 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
138 if (err != error_status_ok)
140 dce_error_inq_text(err, dce_errstr, &err2);
141 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
148 if (expire_time < (current_time + 60))
151 sec_passwd_rec_t *key;
153 sec_login_get_pwent(my_dce_sec_context,
154 (sec_login_passwd_t *) & pw, &err);
155 if (err != error_status_ok)
157 dce_error_inq_text(err, dce_errstr, &err2);
158 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
163 sec_login_refresh_identity(my_dce_sec_context, &err);
164 if (err != error_status_ok)
166 dce_error_inq_text(err, dce_errstr, &err2);
167 DEBUG(0, ("DCE can't refresh identity. %s\n",
173 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
174 (unsigned char *)pw->pw_name,
175 sec_c_key_version_none,
176 (void **)&key, &err);
177 if (err != error_status_ok)
179 dce_error_inq_text(err, dce_errstr, &err2);
180 DEBUG(0, ("DCE can't get key for %s. %s\n",
181 pw->pw_name, dce_errstr));
186 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
187 &password_reset, &auth_src,
189 if (err != error_status_ok)
191 dce_error_inq_text(err, dce_errstr, &err2);
193 ("DCE can't validate and certify identity for %s. %s\n",
194 pw->pw_name, dce_errstr));
197 sec_key_mgmt_free_key(key, &err);
198 if (err != error_status_ok)
200 dce_error_inq_text(err, dce_errstr, &err2);
201 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
205 if (sec_login_setup_identity((unsigned char *)user,
207 &my_dce_sec_context, &err) == 0)
209 dce_error_inq_text(err, dce_errstr, &err2);
210 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
215 sec_login_get_pwent(my_dce_sec_context,
216 (sec_login_passwd_t *) & pw, &err);
217 if (err != error_status_ok)
219 dce_error_inq_text(err, dce_errstr, &err2);
220 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
225 sec_login_purge_context(&my_dce_sec_context, &err);
226 if (err != error_status_ok)
228 dce_error_inq_text(err, dce_errstr, &err2);
229 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
235 * NB. I'd like to change these to call something like change_to_user()
236 * instead but currently we don't have a connection
237 * context to become the correct user. This is already
238 * fairly platform specific code however, so I think
239 * this should be ok. I have added code to go
240 * back to being root on error though. JRA.
245 set_effective_gid(pw->pw_gid);
246 set_effective_uid(pw->pw_uid);
248 if (sec_login_setup_identity((unsigned char *)user,
250 &my_dce_sec_context, &err) == 0)
252 dce_error_inq_text(err, dce_errstr, &err2);
253 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
258 sec_login_get_pwent(my_dce_sec_context,
259 (sec_login_passwd_t *) & pw, &err);
260 if (err != error_status_ok)
262 dce_error_inq_text(err, dce_errstr, &err2);
263 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
267 passwd_rec.version_number = sec_passwd_c_version_none;
268 passwd_rec.pepper = NULL;
269 passwd_rec.key.key_type = sec_passwd_plain;
270 passwd_rec.key.tagged_union.plain = (idl_char *) password;
272 sec_login_validate_identity(my_dce_sec_context,
273 &passwd_rec, &password_reset,
275 if (err != error_status_ok)
277 dce_error_inq_text(err, dce_errstr, &err2);
279 ("DCE Identity Validation failed for principal %s: %s\n",
284 sec_login_certify_identity(my_dce_sec_context, &err);
285 if (err != error_status_ok)
287 dce_error_inq_text(err, dce_errstr, &err2);
288 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
292 if (auth_src != sec_login_auth_src_network)
294 DEBUG(0, ("DCE context has no network credentials.\n"));
297 sec_login_set_context(my_dce_sec_context, &err);
298 if (err != error_status_ok)
300 dce_error_inq_text(err, dce_errstr, &err2);
302 ("DCE login failed for principal %s, cant set context: %s\n",
305 sec_login_purge_context(&my_dce_sec_context, &err);
309 sec_login_get_pwent(my_dce_sec_context,
310 (sec_login_passwd_t *) & pw, &err);
311 if (err != error_status_ok)
313 dce_error_inq_text(err, dce_errstr, &err2);
314 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
318 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
319 user, sys_getpid()));
321 DEBUG(3, ("DCE principal: %s\n"
324 pw->pw_name, pw->pw_uid, pw->pw_gid));
325 DEBUG(3, (" info: %s\n"
328 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
330 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
331 if (err != error_status_ok)
333 dce_error_inq_text(err, dce_errstr, &err2);
334 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
338 set_effective_uid(0);
339 set_effective_gid(0);
342 ("DCE context expires: %s", asctime(localtime(&expire_time))));
344 dcelogin_atmost_once = 1;
349 /* Go back to root, JRA. */
350 set_effective_uid(0);
351 set_effective_gid(egid);
355 void dfs_unlogin(void)
359 unsigned char dce_errstr[dce_c_error_string_len];
361 sec_login_purge_context(&my_dce_sec_context, &err);
362 if (err != error_status_ok)
364 dce_error_inq_text(err, dce_errstr, &err2);
366 ("DCE purge login context failed for server instance %d: %s\n",
367 sys_getpid(), dce_errstr));
372 #ifdef LINUX_BIGCRYPT
373 /****************************************************************************
374 an enhanced crypt for Linux to handle password longer than 8 characters
375 ****************************************************************************/
376 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
378 #define LINUX_PASSWORD_SEG_CHARS 8
382 StrnCpy(salt, salt1, 2);
385 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
386 char *p = crypt(password, salt) + 2;
387 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
389 password += LINUX_PASSWORD_SEG_CHARS;
390 crypted += strlen(p);
398 /****************************************************************************
399 an enhanced crypt for OSF1
400 ****************************************************************************/
401 static char *osf1_bigcrypt(char *password, char *salt1)
403 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
408 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
409 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
412 StrnCpy(salt, salt1, 2);
413 StrnCpy(result, salt1, 2);
416 for (i = 0; i < parts; i++) {
417 p1 = crypt(p2, salt);
418 strncat(result, p1 + 2,
419 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
420 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
421 p2 += AUTH_CLEARTEXT_SEG_CHARS;
429 /****************************************************************************
430 apply a function to upper/lower case combinations
431 of a string and return true if one of them returns true.
432 try all combinations with N uppercase letters.
433 offset is the first char to try and change (start with 0)
434 it assumes the string starts lowercased
435 ****************************************************************************/
436 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (char *),
443 #ifdef PASSWORD_LENGTH
444 len = MIN(len, PASSWORD_LENGTH);
447 if (N <= 0 || offset >= len)
450 for (i = offset; i < (len - (N - 1)); i++) {
455 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
460 return (NT_STATUS_WRONG_PASSWORD);
463 /****************************************************************************
464 apply a function to upper/lower case combinations
465 of a string and return true if one of them returns true.
466 try all combinations with up to N uppercase letters.
467 offset is the first char to try and change (start with 0)
468 it assumes the string starts lowercased
469 ****************************************************************************/
470 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (char *), int N)
474 for (n = 1; n <= N; n++)
475 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
477 return NT_STATUS_WRONG_PASSWORD;
481 /****************************************************************************
482 core of password checking routine
483 ****************************************************************************/
484 static NTSTATUS password_check(char *password)
487 return smb_pam_passcheck(this_user, password);
493 if (afs_auth(this_user, password))
495 #endif /* WITH_AFS */
498 if (dfs_auth(this_user, password))
500 #endif /* WITH_DFS */
504 ret = (strcmp(osf1_bigcrypt(password, this_salt),
508 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
509 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
514 return NT_STATUS_WRONG_PASSWORD;
517 #endif /* OSF1_ENH_SEC */
520 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
524 return NT_STATUS_WRONG_PASSWORD;
527 #endif /* ULTRIX_AUTH */
529 #ifdef LINUX_BIGCRYPT
530 ret = (linux_bigcrypt(password, this_salt, this_crypted));
534 return NT_STATUS_WRONG_PASSWORD;
536 #endif /* LINUX_BIGCRYPT */
538 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
541 * Some systems have bigcrypt in the C library but might not
542 * actually use it for the password hashes (HPUX 10.20) is
543 * a noteable example. So we try bigcrypt first, followed
547 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
550 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
554 return NT_STATUS_WRONG_PASSWORD;
556 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
559 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
563 return NT_STATUS_WRONG_PASSWORD;
565 #endif /* HAVE_BIGCRYPT */
568 DEBUG(1, ("Warning - no crypt available\n"));
569 return NT_STATUS_LOGON_FAILURE;
570 #else /* HAVE_CRYPT */
571 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
575 return NT_STATUS_WRONG_PASSWORD;
577 #endif /* HAVE_CRYPT */
578 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
579 #endif /* WITH_PAM || KRB4_AUTH || KRB5_AUTH */
584 /****************************************************************************
585 CHECK if a username/password is OK
586 the function pointer fn() points to a function to call when a successful
587 match is found and is used to update the encrypted password file
588 return NT_STATUS_OK on correct match, appropriate error otherwise
589 ****************************************************************************/
591 NTSTATUS pass_check(const struct passwd *input_pass, char *user, char *password,
592 int pwlen, BOOL (*fn) (char *, char *), BOOL run_cracker)
596 int level = lp_passwordlevel();
603 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, 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 */
625 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
628 DEBUG(3, ("Couldn't find user %s\n", user));
629 return NT_STATUS_NO_SUCH_USER;
632 pass = make_modifyable_passwd(input_pass);
638 /* many shadow systems require you to be root to get
639 the password, in most cases this should already be
640 the case when this function is called, except
641 perhaps for IPC password changing requests */
643 spass = getspnam(pass->pw_name);
644 if (spass && spass->sp_pwdp)
645 pstrcpy(pass->pw_passwd, spass->sp_pwdp);
647 #elif defined(IA_UINFO)
649 /* Need to get password with SVR4.2's ia_ functions
650 instead of get{sp,pw}ent functions. Required by
651 UnixWare 2.x, tested on version
652 2.1. (tangent@cyberport.com) */
654 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
655 ia_get_logpwd(uinfo, &(pass->pw_passwd));
659 #ifdef HAVE_GETPRPWNAM
661 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
662 if (pr_pw && pr_pw->ufld.fd_encrypt)
663 pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt);
667 #ifdef HAVE_GETPWANAM
669 struct passwd_adjunct *pwret;
670 pwret = getpwanam(s);
671 if (pwret && pwret->pwa_passwd)
672 pstrcpy(pass->pw_passwd,pwret->pwa_passwd);
678 struct pr_passwd *mypasswd;
679 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
681 mypasswd = getprpwnam(user);
683 fstrcpy(pass->pw_name, mypasswd->ufld.fd_name);
684 fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt);
687 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
695 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
697 fstrcpy(pass->pw_passwd, ap->a_password);
703 /* extract relevant info */
704 fstrcpy(this_salt, pass->pw_passwd);
706 #if defined(HAVE_TRUNCATED_SALT)
707 /* crypt on some platforms (HPUX in particular)
708 won't work with more than 2 salt characters. */
712 /* Copy into global for the convenience of looping code */
713 fstrcpy(this_crypted, pass->pw_passwd);
715 if (!*this_crypted) {
716 if (!lp_null_passwords()) {
717 DEBUG(2, ("Disallowing %s with null password\n",
720 return NT_STATUS_LOGON_FAILURE;
724 ("Allowing access to %s with null password\n",
733 #endif /* defined(WITH_PAM) */
735 /* try it as it came to us */
736 nt_status = password_check(password);
737 if NT_STATUS_IS_OK(nt_status) {
742 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
743 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
751 /* if the password was given to us with mixed case then we don't
752 * need to proceed as we know it hasn't been case modified by the
754 if (strhasupper(password) && strhaslower(password)) {
759 /* make a copy of it */
760 StrnCpy(pass2, password, sizeof(pstring) - 1);
762 /* try all lowercase if it's currently all uppercase */
763 if (strhasupper(password)) {
765 if NT_STATUS_IS_OK(nt_status = password_check(password)) {
775 fstrcpy(password, pass2);
776 return NT_STATUS_WRONG_PASSWORD;
779 /* last chance - all combinations of up to level chars upper! */
783 if NT_STATUS_IS_OK(nt_status = string_combinations(password, password_check, level)) {
790 fstrcpy(password, pass2);
792 return NT_STATUS_WRONG_PASSWORD;