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 /* what is the longest significant password available on your system?
29 Knowing this speeds up password searches a lot */
30 #ifndef PASSWORD_LENGTH
31 #define PASSWORD_LENGTH 8
34 /* these are kept here to keep the string_combinations function simple */
35 static char *ths_user;
37 static const char *get_this_user(void)
45 #if defined(WITH_PAM) || defined(OSF1_ENH_SEC)
46 static const char *set_this_user(const char *newuser)
48 char *orig_user = ths_user;
49 ths_user = SMB_STRDUP(newuser);
55 #if !defined(WITH_PAM)
56 static char *ths_salt;
57 /* This must be writable. */
58 static char *get_this_salt(void)
63 /* We may be setting a modified version of the same
64 * string, so don't free before use. */
66 static const char *set_this_salt(const char *newsalt)
68 char *orig_salt = ths_salt;
69 ths_salt = SMB_STRDUP(newsalt);
74 static char *ths_crypted;
75 static const char *get_this_crypted(void)
83 static const char *set_this_crypted(const char *newcrypted)
85 char *orig_crypted = ths_crypted;
86 ths_crypted = SMB_STRDUP(newcrypted);
87 SAFE_FREE(orig_crypted);
95 #include <afs/kautils.h>
97 /*******************************************************************
98 check on AFS authentication
99 ********************************************************************/
100 static bool afs_auth(char *user, char *password)
102 long password_expires = 0;
105 /* For versions of AFS prior to 3.3, this routine has few arguments, */
106 /* but since I can't find the old documentation... :-) */
108 if (ka_UserAuthenticateGeneral
109 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
110 (char *)0, /* cell */
111 password, 0, /* lifetime, default */
112 &password_expires, /*days 'til it expires */
119 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
127 #include <dce/dce_error.h>
128 #include <dce/sec_login.h>
130 /*****************************************************************
131 This new version of the DFS_AUTH code was donated by Karsten Muuss
132 <muuss@or.uni-bonn.de>. It fixes the following problems with the
135 - Server credentials may expire
136 - Client credential cache files have wrong owner
137 - purge_context() function is called with invalid argument
139 This new code was modified to ensure that on exit the uid/gid is
140 still root, and the original directory is restored. JRA.
141 ******************************************************************/
143 sec_login_handle_t my_dce_sec_context;
144 int dcelogin_atmost_once = 0;
146 /*******************************************************************
147 check on a DCE/DFS authentication
148 ********************************************************************/
149 static bool dfs_auth(char *user, char *password)
155 signed32 expire_time, current_time;
156 boolean32 password_reset;
158 sec_passwd_rec_t passwd_rec;
159 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
160 unsigned char dce_errstr[dce_c_error_string_len];
163 if (dcelogin_atmost_once)
168 * We only go for a DCE login context if the given password
169 * matches that stored in the local password file..
170 * Assumes local passwd file is kept in sync w/ DCE RGY!
173 if (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()))
179 sec_login_get_current_context(&my_dce_sec_context, &err);
180 if (err != error_status_ok)
182 dce_error_inq_text(err, dce_errstr, &err2);
183 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
188 sec_login_certify_identity(my_dce_sec_context, &err);
189 if (err != error_status_ok)
191 dce_error_inq_text(err, dce_errstr, &err2);
192 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
197 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
198 if (err != error_status_ok)
200 dce_error_inq_text(err, dce_errstr, &err2);
201 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
208 if (expire_time < (current_time + 60))
211 sec_passwd_rec_t *key;
213 sec_login_get_pwent(my_dce_sec_context,
214 (sec_login_passwd_t *) & pw, &err);
215 if (err != error_status_ok)
217 dce_error_inq_text(err, dce_errstr, &err2);
218 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
223 sec_login_refresh_identity(my_dce_sec_context, &err);
224 if (err != error_status_ok)
226 dce_error_inq_text(err, dce_errstr, &err2);
227 DEBUG(0, ("DCE can't refresh identity. %s\n",
233 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
234 (unsigned char *)pw->pw_name,
235 sec_c_key_version_none,
236 (void **)&key, &err);
237 if (err != error_status_ok)
239 dce_error_inq_text(err, dce_errstr, &err2);
240 DEBUG(0, ("DCE can't get key for %s. %s\n",
241 pw->pw_name, dce_errstr));
246 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
247 &password_reset, &auth_src,
249 if (err != error_status_ok)
251 dce_error_inq_text(err, dce_errstr, &err2);
253 ("DCE can't validate and certify identity for %s. %s\n",
254 pw->pw_name, dce_errstr));
257 sec_key_mgmt_free_key(key, &err);
258 if (err != error_status_ok)
260 dce_error_inq_text(err, dce_errstr, &err2);
261 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
265 if (sec_login_setup_identity((unsigned char *)user,
267 &my_dce_sec_context, &err) == 0)
269 dce_error_inq_text(err, dce_errstr, &err2);
270 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
275 sec_login_get_pwent(my_dce_sec_context,
276 (sec_login_passwd_t *) & pw, &err);
277 if (err != error_status_ok)
279 dce_error_inq_text(err, dce_errstr, &err2);
280 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
285 sec_login_purge_context(&my_dce_sec_context, &err);
286 if (err != error_status_ok)
288 dce_error_inq_text(err, dce_errstr, &err2);
289 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
295 * NB. I'd like to change these to call something like change_to_user()
296 * instead but currently we don't have a connection
297 * context to become the correct user. This is already
298 * fairly platform specific code however, so I think
299 * this should be ok. I have added code to go
300 * back to being root on error though. JRA.
305 set_effective_gid(pw->pw_gid);
306 set_effective_uid(pw->pw_uid);
308 if (sec_login_setup_identity((unsigned char *)user,
310 &my_dce_sec_context, &err) == 0)
312 dce_error_inq_text(err, dce_errstr, &err2);
313 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
318 sec_login_get_pwent(my_dce_sec_context,
319 (sec_login_passwd_t *) & pw, &err);
320 if (err != error_status_ok)
322 dce_error_inq_text(err, dce_errstr, &err2);
323 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
327 passwd_rec.version_number = sec_passwd_c_version_none;
328 passwd_rec.pepper = NULL;
329 passwd_rec.key.key_type = sec_passwd_plain;
330 passwd_rec.key.tagged_union.plain = (idl_char *) password;
332 sec_login_validate_identity(my_dce_sec_context,
333 &passwd_rec, &password_reset,
335 if (err != error_status_ok)
337 dce_error_inq_text(err, dce_errstr, &err2);
339 ("DCE Identity Validation failed for principal %s: %s\n",
344 sec_login_certify_identity(my_dce_sec_context, &err);
345 if (err != error_status_ok)
347 dce_error_inq_text(err, dce_errstr, &err2);
348 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
352 if (auth_src != sec_login_auth_src_network)
354 DEBUG(0, ("DCE context has no network credentials.\n"));
357 sec_login_set_context(my_dce_sec_context, &err);
358 if (err != error_status_ok)
360 dce_error_inq_text(err, dce_errstr, &err2);
362 ("DCE login failed for principal %s, cant set context: %s\n",
365 sec_login_purge_context(&my_dce_sec_context, &err);
369 sec_login_get_pwent(my_dce_sec_context,
370 (sec_login_passwd_t *) & pw, &err);
371 if (err != error_status_ok)
373 dce_error_inq_text(err, dce_errstr, &err2);
374 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
378 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
379 user, sys_getpid()));
381 DEBUG(3, ("DCE principal: %s\n"
384 pw->pw_name, pw->pw_uid, pw->pw_gid));
385 DEBUG(3, (" info: %s\n"
388 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
390 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
391 if (err != error_status_ok)
393 dce_error_inq_text(err, dce_errstr, &err2);
394 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
398 set_effective_uid(0);
399 set_effective_gid(0);
401 t = localtime(&expire_time);
403 const char *asct = asctime(t);
405 DEBUG(0,("DCE context expires: %s", asct));
409 dcelogin_atmost_once = 1;
414 /* Go back to root, JRA. */
415 set_effective_uid(0);
416 set_effective_gid(egid);
420 void dfs_unlogin(void)
424 unsigned char dce_errstr[dce_c_error_string_len];
426 sec_login_purge_context(&my_dce_sec_context, &err);
427 if (err != error_status_ok)
429 dce_error_inq_text(err, dce_errstr, &err2);
431 ("DCE purge login context failed for server instance %d: %s\n",
432 sys_getpid(), dce_errstr));
437 #ifdef LINUX_BIGCRYPT
438 /****************************************************************************
439 an enhanced crypt for Linux to handle password longer than 8 characters
440 ****************************************************************************/
441 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
443 #define LINUX_PASSWORD_SEG_CHARS 8
447 StrnCpy(salt, salt1, 2);
450 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
451 char *p = crypt(password, salt) + 2;
452 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
454 password += LINUX_PASSWORD_SEG_CHARS;
455 crypted += strlen(p);
463 /****************************************************************************
464 an enhanced crypt for OSF1
465 ****************************************************************************/
466 static char *osf1_bigcrypt(char *password, char *salt1)
468 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
473 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
474 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
477 StrnCpy(salt, salt1, 2);
478 StrnCpy(result, salt1, 2);
481 for (i = 0; i < parts; i++) {
482 p1 = crypt(p2, salt);
483 strncat(result, p1 + 2,
484 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
485 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
486 p2 += AUTH_CLEARTEXT_SEG_CHARS;
494 /****************************************************************************
495 apply a function to upper/lower case combinations
496 of a string and return true if one of them returns true.
497 try all combinations with N uppercase letters.
498 offset is the first char to try and change (start with 0)
499 it assumes the string starts lowercased
500 ****************************************************************************/
501 static NTSTATUS string_combinations2(char *s, int offset,
502 NTSTATUS (*fn)(const char *s,
504 int N, void *private_data)
510 #ifdef PASSWORD_LENGTH
511 len = MIN(len, PASSWORD_LENGTH);
514 if (N <= 0 || offset >= len)
515 return (fn(s, private_data));
517 for (i = offset; i < (len - (N - 1)); i++) {
519 if (!islower_ascii(c))
521 s[i] = toupper_ascii(c);
522 nt_status = string_combinations2(s, i + 1, fn, N - 1,
524 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
529 return (NT_STATUS_WRONG_PASSWORD);
532 /****************************************************************************
533 apply a function to upper/lower case combinations
534 of a string and return true if one of them returns true.
535 try all combinations with up to N uppercase letters.
536 offset is the first char to try and change (start with 0)
537 it assumes the string starts lowercased
538 ****************************************************************************/
539 static NTSTATUS string_combinations(char *s,
540 NTSTATUS (*fn)(const char *s,
542 int N, void *private_data)
546 for (n = 1; n <= N; n++) {
547 nt_status = string_combinations2(s, 0, fn, n, private_data);
548 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
552 return NT_STATUS_WRONG_PASSWORD;
556 /****************************************************************************
557 core of password checking routine
558 ****************************************************************************/
559 static NTSTATUS password_check(const char *password, void *private_data)
562 const char *rhost = (const char *)private_data;
563 return smb_pam_passcheck(get_this_user(), rhost, password);
569 if (afs_auth(get_this_user(), password))
571 #endif /* WITH_AFS */
574 if (dfs_auth(get_this_user(), password))
576 #endif /* WITH_DFS */
580 ret = (strcmp(osf1_bigcrypt(password, get_this_salt()),
581 get_this_crypted()) == 0);
584 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
585 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
590 return NT_STATUS_WRONG_PASSWORD;
593 #endif /* OSF1_ENH_SEC */
596 ret = (strcmp((char *)crypt16(password, get_this_salt()), get_this_crypted()) == 0);
600 return NT_STATUS_WRONG_PASSWORD;
603 #endif /* ULTRIX_AUTH */
605 #ifdef LINUX_BIGCRYPT
606 ret = (linux_bigcrypt(password, get_this_salt(), get_this_crypted()));
610 return NT_STATUS_WRONG_PASSWORD;
612 #endif /* LINUX_BIGCRYPT */
614 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
617 * Some systems have bigcrypt in the C library but might not
618 * actually use it for the password hashes (HPUX 10.20) is
619 * a noteable example. So we try bigcrypt first, followed
623 if (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0)
626 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
630 return NT_STATUS_WRONG_PASSWORD;
632 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
635 ret = (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0);
639 return NT_STATUS_WRONG_PASSWORD;
641 #endif /* HAVE_BIGCRYPT */
644 DEBUG(1, ("Warning - no crypt available\n"));
645 return NT_STATUS_LOGON_FAILURE;
646 #else /* HAVE_CRYPT */
647 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
651 return NT_STATUS_WRONG_PASSWORD;
653 #endif /* HAVE_CRYPT */
654 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
655 #endif /* WITH_PAM */
660 /****************************************************************************
661 CHECK if a username/password is OK
662 the function pointer fn() points to a function to call when a successful
663 match is found and is used to update the encrypted password file
664 return NT_STATUS_OK on correct match, appropriate error otherwise
665 ****************************************************************************/
667 NTSTATUS pass_check(const struct passwd *pass,
670 const char *password,
674 int level = lp_passwordlevel();
678 #ifdef DEBUG_PASSWORD
679 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
683 return NT_STATUS_LOGON_FAILURE;
685 if ((!*password) && !lp_null_passwords())
686 return NT_STATUS_LOGON_FAILURE;
688 #if defined(WITH_PAM)
691 * If we're using PAM we want to short-circuit all the
692 * checks below and dive straight into the PAM code.
695 if (set_this_user(user) == NULL) {
696 return NT_STATUS_NO_MEMORY;
699 DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user));
701 #else /* Not using PAM */
703 DEBUG(4, ("pass_check: Checking password for user %s\n", user));
706 DEBUG(3, ("Couldn't find user %s\n", user));
707 return NT_STATUS_NO_SUCH_USER;
711 /* Copy into global for the convenience of looping code */
712 /* Also the place to keep the 'password' no matter what
713 crazy struct it started in... */
714 if (set_this_crypted(pass->pw_passwd) == NULL) {
715 return NT_STATUS_NO_MEMORY;
717 if (set_this_salt(pass->pw_passwd) == NULL) {
718 return NT_STATUS_NO_MEMORY;
725 /* many shadow systems require you to be root to get
726 the password, in most cases this should already be
727 the case when this function is called, except
728 perhaps for IPC password changing requests */
730 spass = getspnam(pass->pw_name);
731 if (spass && spass->sp_pwdp) {
732 if (set_this_crypted(spass->sp_pwdp) == NULL) {
733 return NT_STATUS_NO_MEMORY;
735 if (set_this_salt(spass->sp_pwdp) == NULL) {
736 return NT_STATUS_NO_MEMORY;
740 #elif defined(IA_UINFO)
742 /* Need to get password with SVR4.2's ia_ functions
743 instead of get{sp,pw}ent functions. Required by
744 UnixWare 2.x, tested on version
745 2.1. (tangent@cyberport.com) */
747 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
748 ia_get_logpwd(uinfo, &(pass->pw_passwd));
752 #ifdef HAVE_GETPRPWNAM
754 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
755 if (pr_pw && pr_pw->ufld.fd_encrypt) {
756 if (set_this_crypted(pr_pw->ufld.fd_encrypt) == NULL) {
757 return NT_STATUS_NO_MEMORY;
763 #ifdef HAVE_GETPWANAM
765 struct passwd_adjunct *pwret;
766 pwret = getpwanam(s);
767 if (pwret && pwret->pwa_passwd) {
768 if (set_this_crypted(pwret->pwa_passwd) == NULL) {
769 return NT_STATUS_NO_MEMORY;
777 struct pr_passwd *mypasswd;
778 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
780 mypasswd = getprpwnam(user);
782 if (set_this_user(mypasswd->ufld.fd_name) == NULL) {
783 return NT_STATUS_NO_MEMORY;
785 if (set_this_crypted(mypasswd->ufld.fd_encrypt) == NULL) {
786 return NT_STATUS_NO_MEMORY;
790 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
798 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
800 if (set_this_crypted(ap->a_password) == NULL) {
802 return NT_STATUS_NO_MEMORY;
809 #if defined(HAVE_TRUNCATED_SALT)
810 /* crypt on some platforms (HPUX in particular)
811 won't work with more than 2 salt characters. */
813 char *trunc_salt = get_this_salt();
814 if (!trunc_salt || strlen(trunc_salt) < 2) {
815 return NT_STATUS_LOGON_FAILURE;
818 if (set_this_salt(trunc_salt) == NULL) {
819 return NT_STATUS_NO_MEMORY;
824 if (!get_this_crypted() || !*get_this_crypted()) {
825 if (!lp_null_passwords()) {
826 DEBUG(2, ("Disallowing %s with null password\n",
828 return NT_STATUS_LOGON_FAILURE;
832 ("Allowing access to %s with null password\n",
838 #endif /* defined(WITH_PAM) */
840 /* try it as it came to us */
841 nt_status = password_check(password, (void *)rhost);
842 if NT_STATUS_IS_OK(nt_status) {
844 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
845 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
853 /* if the password was given to us with mixed case then we don't
854 * need to proceed as we know it hasn't been case modified by the
856 if (strhasupper(password) && strhaslower(password)) {
860 /* make a copy of it */
861 pass2 = talloc_strdup(talloc_tos(), password);
863 return NT_STATUS_NO_MEMORY;
866 /* try all lowercase if it's currently all uppercase */
867 if (strhasupper(pass2)) {
869 nt_status = password_check(pass2, (void *)rhost);
870 if (NT_STATUS_IS_OK(nt_status)) {
877 return NT_STATUS_WRONG_PASSWORD;
880 /* last chance - all combinations of up to level chars upper! */
883 nt_status = string_combinations(pass2, password_check, level,
885 if (NT_STATUS_IS_OK(nt_status)) {
889 return NT_STATUS_WRONG_PASSWORD;