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 3 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, see <http://www.gnu.org/licenses/>.
20 /* this module is for checking a username/password against a system
21 password database. The SMB encrypted password support is elsewhere */
26 #define DBGC_CLASS DBGC_AUTH
28 /* these are kept here to keep the string_combinations function simple */
29 static fstring this_user;
30 #if !defined(WITH_PAM)
31 static fstring this_salt;
32 static fstring this_crypted;
38 #include <afs/kautils.h>
40 /*******************************************************************
41 check on AFS authentication
42 ********************************************************************/
43 static BOOL afs_auth(char *user, char *password)
45 long password_expires = 0;
48 /* For versions of AFS prior to 3.3, this routine has few arguments, */
49 /* but since I can't find the old documentation... :-) */
51 if (ka_UserAuthenticateGeneral
52 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
54 password, 0, /* lifetime, default */
55 &password_expires, /*days 'til it expires */
62 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
70 #include <dce/dce_error.h>
71 #include <dce/sec_login.h>
73 /*****************************************************************
74 This new version of the DFS_AUTH code was donated by Karsten Muuss
75 <muuss@or.uni-bonn.de>. It fixes the following problems with the
78 - Server credentials may expire
79 - Client credential cache files have wrong owner
80 - purge_context() function is called with invalid argument
82 This new code was modified to ensure that on exit the uid/gid is
83 still root, and the original directory is restored. JRA.
84 ******************************************************************/
86 sec_login_handle_t my_dce_sec_context;
87 int dcelogin_atmost_once = 0;
89 /*******************************************************************
90 check on a DCE/DFS authentication
91 ********************************************************************/
92 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 change_to_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);
344 t = localtime(&expire_time);
346 const char *asct = asctime(t);
348 DEBUG(0,("DCE context expires: %s", asct));
352 dcelogin_atmost_once = 1;
357 /* Go back to root, JRA. */
358 set_effective_uid(0);
359 set_effective_gid(egid);
363 void dfs_unlogin(void)
367 unsigned char dce_errstr[dce_c_error_string_len];
369 sec_login_purge_context(&my_dce_sec_context, &err);
370 if (err != error_status_ok)
372 dce_error_inq_text(err, dce_errstr, &err2);
374 ("DCE purge login context failed for server instance %d: %s\n",
375 sys_getpid(), dce_errstr));
380 #ifdef LINUX_BIGCRYPT
381 /****************************************************************************
382 an enhanced crypt for Linux to handle password longer than 8 characters
383 ****************************************************************************/
384 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
386 #define LINUX_PASSWORD_SEG_CHARS 8
390 StrnCpy(salt, salt1, 2);
393 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
394 char *p = crypt(password, salt) + 2;
395 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
397 password += LINUX_PASSWORD_SEG_CHARS;
398 crypted += strlen(p);
406 /****************************************************************************
407 an enhanced crypt for OSF1
408 ****************************************************************************/
409 static char *osf1_bigcrypt(char *password, char *salt1)
411 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
416 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
417 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
420 StrnCpy(salt, salt1, 2);
421 StrnCpy(result, salt1, 2);
424 for (i = 0; i < parts; i++) {
425 p1 = crypt(p2, salt);
426 strncat(result, p1 + 2,
427 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
428 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
429 p2 += AUTH_CLEARTEXT_SEG_CHARS;
437 /****************************************************************************
438 apply a function to upper/lower case combinations
439 of a string and return true if one of them returns true.
440 try all combinations with N uppercase letters.
441 offset is the first char to try and change (start with 0)
442 it assumes the string starts lowercased
443 ****************************************************************************/
444 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
451 #ifdef PASSWORD_LENGTH
452 len = MIN(len, PASSWORD_LENGTH);
455 if (N <= 0 || offset >= len)
458 for (i = offset; i < (len - (N - 1)); i++) {
460 if (!islower_ascii(c))
462 s[i] = toupper_ascii(c);
463 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
468 return (NT_STATUS_WRONG_PASSWORD);
471 /****************************************************************************
472 apply a function to upper/lower case combinations
473 of a string and return true if one of them returns true.
474 try all combinations with up to N uppercase letters.
475 offset is the first char to try and change (start with 0)
476 it assumes the string starts lowercased
477 ****************************************************************************/
478 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
482 for (n = 1; n <= N; n++)
483 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
485 return NT_STATUS_WRONG_PASSWORD;
489 /****************************************************************************
490 core of password checking routine
491 ****************************************************************************/
492 static NTSTATUS password_check(const char *password)
495 return smb_pam_passcheck(this_user, password);
501 if (afs_auth(this_user, password))
503 #endif /* WITH_AFS */
506 if (dfs_auth(this_user, password))
508 #endif /* WITH_DFS */
512 ret = (strcmp(osf1_bigcrypt(password, this_salt),
516 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
517 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
522 return NT_STATUS_WRONG_PASSWORD;
525 #endif /* OSF1_ENH_SEC */
528 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
532 return NT_STATUS_WRONG_PASSWORD;
535 #endif /* ULTRIX_AUTH */
537 #ifdef LINUX_BIGCRYPT
538 ret = (linux_bigcrypt(password, this_salt, this_crypted));
542 return NT_STATUS_WRONG_PASSWORD;
544 #endif /* LINUX_BIGCRYPT */
546 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
549 * Some systems have bigcrypt in the C library but might not
550 * actually use it for the password hashes (HPUX 10.20) is
551 * a noteable example. So we try bigcrypt first, followed
555 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
558 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
562 return NT_STATUS_WRONG_PASSWORD;
564 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
567 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
571 return NT_STATUS_WRONG_PASSWORD;
573 #endif /* HAVE_BIGCRYPT */
576 DEBUG(1, ("Warning - no crypt available\n"));
577 return NT_STATUS_LOGON_FAILURE;
578 #else /* HAVE_CRYPT */
579 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
583 return NT_STATUS_WRONG_PASSWORD;
585 #endif /* HAVE_CRYPT */
586 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
587 #endif /* WITH_PAM */
592 /****************************************************************************
593 CHECK if a username/password is OK
594 the function pointer fn() points to a function to call when a successful
595 match is found and is used to update the encrypted password file
596 return NT_STATUS_OK on correct match, appropriate error otherwise
597 ****************************************************************************/
599 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
600 int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
603 int level = lp_passwordlevel();
607 #ifdef DEBUG_PASSWORD
608 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
612 return NT_STATUS_LOGON_FAILURE;
614 if (((!*password) || (!pwlen)) && !lp_null_passwords())
615 return NT_STATUS_LOGON_FAILURE;
617 #if defined(WITH_PAM)
620 * If we're using PAM we want to short-circuit all the
621 * checks below and dive straight into the PAM code.
624 fstrcpy(this_user, user);
626 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
628 #else /* Not using PAM */
630 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
633 DEBUG(3, ("Couldn't find user %s\n", user));
634 return NT_STATUS_NO_SUCH_USER;
638 /* Copy into global for the convenience of looping code */
639 /* Also the place to keep the 'password' no matter what
640 crazy struct it started in... */
641 fstrcpy(this_crypted, pass->pw_passwd);
642 fstrcpy(this_salt, pass->pw_passwd);
648 /* many shadow systems require you to be root to get
649 the password, in most cases this should already be
650 the case when this function is called, except
651 perhaps for IPC password changing requests */
653 spass = getspnam(pass->pw_name);
654 if (spass && spass->sp_pwdp) {
655 fstrcpy(this_crypted, spass->sp_pwdp);
656 fstrcpy(this_salt, spass->sp_pwdp);
659 #elif defined(IA_UINFO)
661 /* Need to get password with SVR4.2's ia_ functions
662 instead of get{sp,pw}ent functions. Required by
663 UnixWare 2.x, tested on version
664 2.1. (tangent@cyberport.com) */
666 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
667 ia_get_logpwd(uinfo, &(pass->pw_passwd));
671 #ifdef HAVE_GETPRPWNAM
673 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
674 if (pr_pw && pr_pw->ufld.fd_encrypt)
675 fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
679 #ifdef HAVE_GETPWANAM
681 struct passwd_adjunct *pwret;
682 pwret = getpwanam(s);
683 if (pwret && pwret->pwa_passwd)
684 fstrcpy(this_crypted, pwret->pwa_passwd);
690 struct pr_passwd *mypasswd;
691 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
693 mypasswd = getprpwnam(user);
695 fstrcpy(this_user, mypasswd->ufld.fd_name);
696 fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
699 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
707 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
709 fstrcpy(this_crypted, ap->a_password);
715 #if defined(HAVE_TRUNCATED_SALT)
716 /* crypt on some platforms (HPUX in particular)
717 won't work with more than 2 salt characters. */
721 if (!*this_crypted) {
722 if (!lp_null_passwords()) {
723 DEBUG(2, ("Disallowing %s with null password\n",
725 return NT_STATUS_LOGON_FAILURE;
729 ("Allowing access to %s with null password\n",
735 #endif /* defined(WITH_PAM) */
737 /* try it as it came to us */
738 nt_status = password_check(password);
739 if NT_STATUS_IS_OK(nt_status) {
744 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
745 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
753 /* if the password was given to us with mixed case then we don't
754 * need to proceed as we know it hasn't been case modified by the
756 if (strhasupper(password) && strhaslower(password)) {
760 /* make a copy of it */
761 pstrcpy(pass2, password);
763 /* try all lowercase if it's currently all uppercase */
764 if (strhasupper(pass2)) {
766 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
775 return NT_STATUS_WRONG_PASSWORD;
778 /* last chance - all combinations of up to level chars upper! */
781 if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
787 return NT_STATUS_WRONG_PASSWORD;