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 */
24 #include "system/passwd.h"
28 #define DBGC_CLASS DBGC_AUTH
30 /* what is the longest significant password available on your system?
31 Knowing this speeds up password searches a lot */
32 #ifndef PASSWORD_LENGTH
33 #define PASSWORD_LENGTH 8
36 /* these are kept here to keep the string_combinations function simple */
37 static char *ths_user
;
39 static const char *get_this_user(void)
47 #if defined(WITH_PAM) || defined(OSF1_ENH_SEC)
48 static const char *set_this_user(const char *newuser
)
50 char *orig_user
= ths_user
;
51 ths_user
= SMB_STRDUP(newuser
);
57 #if !defined(WITH_PAM)
58 static char *ths_salt
;
59 /* This must be writable. */
60 static char *get_this_salt(void)
65 /* We may be setting a modified version of the same
66 * string, so don't free before use. */
68 static const char *set_this_salt(const char *newsalt
)
70 char *orig_salt
= ths_salt
;
71 ths_salt
= SMB_STRDUP(newsalt
);
76 static char *ths_crypted
;
77 static const char *get_this_crypted(void)
85 static const char *set_this_crypted(const char *newcrypted
)
87 char *orig_crypted
= ths_crypted
;
88 ths_crypted
= SMB_STRDUP(newcrypted
);
89 SAFE_FREE(orig_crypted
);
97 #include <afs/kautils.h>
99 /*******************************************************************
100 check on AFS authentication
101 ********************************************************************/
102 static bool afs_auth(char *user
, char *password
)
104 long password_expires
= 0;
107 /* For versions of AFS prior to 3.3, this routine has few arguments, */
108 /* but since I can't find the old documentation... :-) */
110 if (ka_UserAuthenticateGeneral
111 (KA_USERAUTH_VERSION
+ KA_USERAUTH_DOSETPAG
, user
, (char *)0, /* instance */
112 (char *)0, /* cell */
113 password
, 0, /* lifetime, default */
114 &password_expires
, /*days 'til it expires */
121 ("AFS authentication for \"%s\" failed (%s)\n", user
, reason
));
129 #include <dce/dce_error.h>
130 #include <dce/sec_login.h>
132 /*****************************************************************
133 This new version of the DFS_AUTH code was donated by Karsten Muuss
134 <muuss@or.uni-bonn.de>. It fixes the following problems with the
137 - Server credentials may expire
138 - Client credential cache files have wrong owner
139 - purge_context() function is called with invalid argument
141 This new code was modified to ensure that on exit the uid/gid is
142 still root, and the original directory is restored. JRA.
143 ******************************************************************/
145 sec_login_handle_t my_dce_sec_context
;
146 int dcelogin_atmost_once
= 0;
148 /*******************************************************************
149 check on a DCE/DFS authentication
150 ********************************************************************/
151 static bool dfs_auth(char *user
, char *password
)
157 signed32 expire_time
, current_time
;
158 boolean32 password_reset
;
160 sec_passwd_rec_t passwd_rec
;
161 sec_login_auth_src_t auth_src
= sec_login_auth_src_network
;
162 unsigned char dce_errstr
[dce_c_error_string_len
];
165 if (dcelogin_atmost_once
)
170 * We only go for a DCE login context if the given password
171 * matches that stored in the local password file..
172 * Assumes local passwd file is kept in sync w/ DCE RGY!
175 if (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()))
181 sec_login_get_current_context(&my_dce_sec_context
, &err
);
182 if (err
!= error_status_ok
)
184 dce_error_inq_text(err
, dce_errstr
, &err2
);
185 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr
));
190 sec_login_certify_identity(my_dce_sec_context
, &err
);
191 if (err
!= error_status_ok
)
193 dce_error_inq_text(err
, dce_errstr
, &err2
);
194 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr
));
199 sec_login_get_expiration(my_dce_sec_context
, &expire_time
, &err
);
200 if (err
!= error_status_ok
)
202 dce_error_inq_text(err
, dce_errstr
, &err2
);
203 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr
));
210 if (expire_time
< (current_time
+ 60))
213 sec_passwd_rec_t
*key
;
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_refresh_identity(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 refresh identity. %s\n",
235 sec_key_mgmt_get_key(rpc_c_authn_dce_secret
, NULL
,
236 (unsigned char *)pw
->pw_name
,
237 sec_c_key_version_none
,
238 (void **)&key
, &err
);
239 if (err
!= error_status_ok
)
241 dce_error_inq_text(err
, dce_errstr
, &err2
);
242 DEBUG(0, ("DCE can't get key for %s. %s\n",
243 pw
->pw_name
, dce_errstr
));
248 sec_login_valid_and_cert_ident(my_dce_sec_context
, key
,
249 &password_reset
, &auth_src
,
251 if (err
!= error_status_ok
)
253 dce_error_inq_text(err
, dce_errstr
, &err2
);
255 ("DCE can't validate and certify identity for %s. %s\n",
256 pw
->pw_name
, dce_errstr
));
259 sec_key_mgmt_free_key(key
, &err
);
260 if (err
!= error_status_ok
)
262 dce_error_inq_text(err
, dce_errstr
, &err2
);
263 DEBUG(0, ("DCE can't free key.\n", dce_errstr
));
267 if (sec_login_setup_identity((unsigned char *)user
,
269 &my_dce_sec_context
, &err
) == 0)
271 dce_error_inq_text(err
, dce_errstr
, &err2
);
272 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
277 sec_login_get_pwent(my_dce_sec_context
,
278 (sec_login_passwd_t
*) & pw
, &err
);
279 if (err
!= error_status_ok
)
281 dce_error_inq_text(err
, dce_errstr
, &err2
);
282 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr
));
287 sec_login_purge_context(&my_dce_sec_context
, &err
);
288 if (err
!= error_status_ok
)
290 dce_error_inq_text(err
, dce_errstr
, &err2
);
291 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr
));
297 * NB. I'd like to change these to call something like change_to_user()
298 * instead but currently we don't have a connection
299 * context to become the correct user. This is already
300 * fairly platform specific code however, so I think
301 * this should be ok. I have added code to go
302 * back to being root on error though. JRA.
307 set_effective_gid(pw
->pw_gid
);
308 set_effective_uid(pw
->pw_uid
);
310 if (sec_login_setup_identity((unsigned char *)user
,
312 &my_dce_sec_context
, &err
) == 0)
314 dce_error_inq_text(err
, dce_errstr
, &err2
);
315 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
320 sec_login_get_pwent(my_dce_sec_context
,
321 (sec_login_passwd_t
*) & pw
, &err
);
322 if (err
!= error_status_ok
)
324 dce_error_inq_text(err
, dce_errstr
, &err2
);
325 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr
));
329 passwd_rec
.version_number
= sec_passwd_c_version_none
;
330 passwd_rec
.pepper
= NULL
;
331 passwd_rec
.key
.key_type
= sec_passwd_plain
;
332 passwd_rec
.key
.tagged_union
.plain
= (idl_char
*) password
;
334 sec_login_validate_identity(my_dce_sec_context
,
335 &passwd_rec
, &password_reset
,
337 if (err
!= error_status_ok
)
339 dce_error_inq_text(err
, dce_errstr
, &err2
);
341 ("DCE Identity Validation failed for principal %s: %s\n",
346 sec_login_certify_identity(my_dce_sec_context
, &err
);
347 if (err
!= error_status_ok
)
349 dce_error_inq_text(err
, dce_errstr
, &err2
);
350 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr
));
354 if (auth_src
!= sec_login_auth_src_network
)
356 DEBUG(0, ("DCE context has no network credentials.\n"));
359 sec_login_set_context(my_dce_sec_context
, &err
);
360 if (err
!= error_status_ok
)
362 dce_error_inq_text(err
, dce_errstr
, &err2
);
364 ("DCE login failed for principal %s, cant set context: %s\n",
367 sec_login_purge_context(&my_dce_sec_context
, &err
);
371 sec_login_get_pwent(my_dce_sec_context
,
372 (sec_login_passwd_t
*) & pw
, &err
);
373 if (err
!= error_status_ok
)
375 dce_error_inq_text(err
, dce_errstr
, &err2
);
376 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr
));
380 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
383 DEBUG(3, ("DCE principal: %s\n"
386 pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
));
387 DEBUG(3, (" info: %s\n"
390 pw
->pw_gecos
, pw
->pw_dir
, pw
->pw_shell
));
392 sec_login_get_expiration(my_dce_sec_context
, &expire_time
, &err
);
393 if (err
!= error_status_ok
)
395 dce_error_inq_text(err
, dce_errstr
, &err2
);
396 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr
));
400 set_effective_uid(0);
401 set_effective_gid(0);
403 t
= localtime(&expire_time
);
405 const char *asct
= asctime(t
);
407 DEBUG(0,("DCE context expires: %s", asct
));
411 dcelogin_atmost_once
= 1;
416 /* Go back to root, JRA. */
417 set_effective_uid(0);
418 set_effective_gid(egid
);
422 void dfs_unlogin(void)
426 unsigned char dce_errstr
[dce_c_error_string_len
];
428 sec_login_purge_context(&my_dce_sec_context
, &err
);
429 if (err
!= error_status_ok
)
431 dce_error_inq_text(err
, dce_errstr
, &err2
);
433 ("DCE purge login context failed for server instance %d: %s\n",
434 getpid(), dce_errstr
));
439 #ifdef LINUX_BIGCRYPT
440 /****************************************************************************
441 an enhanced crypt for Linux to handle password longer than 8 characters
442 ****************************************************************************/
443 static int linux_bigcrypt(char *password
, char *salt1
, char *crypted
)
445 #define LINUX_PASSWORD_SEG_CHARS 8
449 StrnCpy(salt
, salt1
, 2);
452 for (i
= strlen(password
); i
> 0; i
-= LINUX_PASSWORD_SEG_CHARS
) {
453 char *p
= crypt(password
, salt
) + 2;
454 if (strncmp(p
, crypted
, LINUX_PASSWORD_SEG_CHARS
) != 0)
456 password
+= LINUX_PASSWORD_SEG_CHARS
;
457 crypted
+= strlen(p
);
465 /****************************************************************************
466 an enhanced crypt for OSF1
467 ****************************************************************************/
468 static char *osf1_bigcrypt(char *password
, char *salt1
)
470 static char result
[AUTH_MAX_PASSWD_LENGTH
] = "";
475 int parts
= strlen(password
) / AUTH_CLEARTEXT_SEG_CHARS
;
476 if (strlen(password
) % AUTH_CLEARTEXT_SEG_CHARS
)
479 StrnCpy(salt
, salt1
, 2);
480 StrnCpy(result
, salt1
, 2);
483 for (i
= 0; i
< parts
; i
++) {
484 p1
= crypt(p2
, salt
);
485 strncat(result
, p1
+ 2,
486 AUTH_MAX_PASSWD_LENGTH
- strlen(p1
+ 2) - 1);
487 StrnCpy(salt
, &result
[2 + i
* AUTH_CIPHERTEXT_SEG_CHARS
], 2);
488 p2
+= AUTH_CLEARTEXT_SEG_CHARS
;
496 /****************************************************************************
497 apply a function to upper/lower case combinations
498 of a string and return true if one of them returns true.
499 try all combinations with N uppercase letters.
500 offset is the first char to try and change (start with 0)
501 it assumes the string starts lowercased
502 ****************************************************************************/
503 static NTSTATUS
string_combinations2(char *s
, int offset
,
504 NTSTATUS (*fn
)(const char *s
,
505 const void *private_data
),
506 int N
, const void *private_data
)
512 #ifdef PASSWORD_LENGTH
513 len
= MIN(len
, PASSWORD_LENGTH
);
516 if (N
<= 0 || offset
>= len
)
517 return (fn(s
, private_data
));
519 for (i
= offset
; i
< (len
- (N
- 1)); i
++) {
524 nt_status
= string_combinations2(s
, i
+ 1, fn
, N
- 1,
526 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_WRONG_PASSWORD
)) {
531 return (NT_STATUS_WRONG_PASSWORD
);
534 /****************************************************************************
535 apply a function to upper/lower case combinations
536 of a string and return true if one of them returns true.
537 try all combinations with up to N uppercase letters.
538 offset is the first char to try and change (start with 0)
539 it assumes the string starts lowercased
540 ****************************************************************************/
541 static NTSTATUS
string_combinations(char *s
,
542 NTSTATUS (*fn
)(const char *s
,
543 const void *private_data
),
544 int N
, const void *private_data
)
548 for (n
= 1; n
<= N
; n
++) {
549 nt_status
= string_combinations2(s
, 0, fn
, n
, private_data
);
550 if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_WRONG_PASSWORD
)) {
554 return NT_STATUS_WRONG_PASSWORD
;
558 /****************************************************************************
559 core of password checking routine
560 ****************************************************************************/
561 static NTSTATUS
password_check(const char *password
, const void *private_data
)
564 const char *rhost
= (const char *)private_data
;
565 return smb_pam_passcheck(get_this_user(), rhost
, password
);
571 if (afs_auth(get_this_user(), password
))
573 #endif /* WITH_AFS */
576 if (dfs_auth(get_this_user(), password
))
578 #endif /* WITH_DFS */
582 ret
= (strcmp(osf1_bigcrypt(password
, get_this_salt()),
583 get_this_crypted()) == 0);
586 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
587 ret
= (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()) == 0);
592 return NT_STATUS_WRONG_PASSWORD
;
595 #endif /* OSF1_ENH_SEC */
598 ret
= (strcmp((char *)crypt16(password
, get_this_salt()), get_this_crypted()) == 0);
602 return NT_STATUS_WRONG_PASSWORD
;
605 #endif /* ULTRIX_AUTH */
607 #ifdef LINUX_BIGCRYPT
608 ret
= (linux_bigcrypt(password
, get_this_salt(), get_this_crypted()));
612 return NT_STATUS_WRONG_PASSWORD
;
614 #endif /* LINUX_BIGCRYPT */
616 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
619 * Some systems have bigcrypt in the C library but might not
620 * actually use it for the password hashes (HPUX 10.20) is
621 * a noteable example. So we try bigcrypt first, followed
625 if (strcmp(bigcrypt(password
, get_this_salt()), get_this_crypted()) == 0)
628 ret
= (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()) == 0);
632 return NT_STATUS_WRONG_PASSWORD
;
634 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
637 ret
= (strcmp(bigcrypt(password
, get_this_salt()), get_this_crypted()) == 0);
641 return NT_STATUS_WRONG_PASSWORD
;
643 #endif /* HAVE_BIGCRYPT */
646 DEBUG(1, ("Warning - no crypt available\n"));
647 return NT_STATUS_LOGON_FAILURE
;
648 #else /* HAVE_CRYPT */
649 ret
= (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()) == 0);
653 return NT_STATUS_WRONG_PASSWORD
;
655 #endif /* HAVE_CRYPT */
656 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
657 #endif /* WITH_PAM */
662 /****************************************************************************
663 CHECK if a username/password is OK
664 the function pointer fn() points to a function to call when a successful
665 match is found and is used to update the encrypted password file
666 return NT_STATUS_OK on correct match, appropriate error otherwise
667 ****************************************************************************/
669 NTSTATUS
pass_check(const struct passwd
*pass
,
672 const char *password
,
676 int level
= lp_passwordlevel();
680 #ifdef DEBUG_PASSWORD
681 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user
, password
));
685 return NT_STATUS_LOGON_FAILURE
;
687 if ((!*password
) && !lp_null_passwords())
688 return NT_STATUS_LOGON_FAILURE
;
690 #if defined(WITH_PAM)
693 * If we're using PAM we want to short-circuit all the
694 * checks below and dive straight into the PAM code.
697 if (set_this_user(user
) == NULL
) {
698 return NT_STATUS_NO_MEMORY
;
701 DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user
));
703 #else /* Not using PAM */
705 DEBUG(4, ("pass_check: Checking password for user %s\n", user
));
708 DEBUG(3, ("Couldn't find user %s\n", user
));
709 return NT_STATUS_NO_SUCH_USER
;
713 /* Copy into global for the convenience of looping code */
714 /* Also the place to keep the 'password' no matter what
715 crazy struct it started in... */
716 if (set_this_crypted(pass
->pw_passwd
) == NULL
) {
717 return NT_STATUS_NO_MEMORY
;
719 if (set_this_salt(pass
->pw_passwd
) == NULL
) {
720 return NT_STATUS_NO_MEMORY
;
727 /* many shadow systems require you to be root to get
728 the password, in most cases this should already be
729 the case when this function is called, except
730 perhaps for IPC password changing requests */
732 spass
= getspnam(pass
->pw_name
);
733 if (spass
&& spass
->sp_pwdp
) {
734 if (set_this_crypted(spass
->sp_pwdp
) == NULL
) {
735 return NT_STATUS_NO_MEMORY
;
737 if (set_this_salt(spass
->sp_pwdp
) == NULL
) {
738 return NT_STATUS_NO_MEMORY
;
742 #elif defined(IA_UINFO)
744 /* Need to get password with SVR4.2's ia_ functions
745 instead of get{sp,pw}ent functions. Required by
746 UnixWare 2.x, tested on version
747 2.1. (tangent@cyberport.com) */
749 if (ia_openinfo(pass
->pw_name
, &uinfo
) != -1)
750 ia_get_logpwd(uinfo
, &(pass
->pw_passwd
));
754 #ifdef HAVE_GETPRPWNAM
756 struct pr_passwd
*pr_pw
= getprpwnam(pass
->pw_name
);
757 if (pr_pw
&& pr_pw
->ufld
.fd_encrypt
) {
758 if (set_this_crypted(pr_pw
->ufld
.fd_encrypt
) == NULL
) {
759 return NT_STATUS_NO_MEMORY
;
765 #ifdef HAVE_GETPWANAM
767 struct passwd_adjunct
*pwret
;
768 pwret
= getpwanam(s
);
769 if (pwret
&& pwret
->pwa_passwd
) {
770 if (set_this_crypted(pwret
->pwa_passwd
) == NULL
) {
771 return NT_STATUS_NO_MEMORY
;
779 struct pr_passwd
*mypasswd
;
780 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
782 mypasswd
= getprpwnam(user
);
784 if (set_this_user(mypasswd
->ufld
.fd_name
) == NULL
) {
785 return NT_STATUS_NO_MEMORY
;
787 if (set_this_crypted(mypasswd
->ufld
.fd_encrypt
) == NULL
) {
788 return NT_STATUS_NO_MEMORY
;
792 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
800 AUTHORIZATION
*ap
= getauthuid(pass
->pw_uid
);
802 if (set_this_crypted(ap
->a_password
) == NULL
) {
804 return NT_STATUS_NO_MEMORY
;
811 #if defined(HAVE_TRUNCATED_SALT)
812 /* crypt on some platforms (HPUX in particular)
813 won't work with more than 2 salt characters. */
815 char *trunc_salt
= get_this_salt();
816 if (!trunc_salt
|| strlen(trunc_salt
) < 2) {
817 return NT_STATUS_LOGON_FAILURE
;
820 if (set_this_salt(trunc_salt
) == NULL
) {
821 return NT_STATUS_NO_MEMORY
;
826 if (!get_this_crypted() || !*get_this_crypted()) {
827 if (!lp_null_passwords()) {
828 DEBUG(2, ("Disallowing %s with null password\n",
830 return NT_STATUS_LOGON_FAILURE
;
834 ("Allowing access to %s with null password\n",
840 #endif /* defined(WITH_PAM) */
842 /* try it as it came to us */
843 nt_status
= password_check(password
, (const void *)rhost
);
844 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 pass2
= talloc_strdup(talloc_tos(), password
);
865 return NT_STATUS_NO_MEMORY
;
868 /* try all lowercase if it's currently all uppercase */
869 if (strhasupper(pass2
)) {
870 if (!strlower_m(pass2
)) {
871 return NT_STATUS_INVALID_PARAMETER
;
873 nt_status
= password_check(pass2
, (const void *)rhost
);
874 if (NT_STATUS_IS_OK(nt_status
)) {
881 return NT_STATUS_WRONG_PASSWORD
;
884 /* last chance - all combinations of up to level chars upper! */
885 if (!strlower_m(pass2
)) {
886 return NT_STATUS_INVALID_PARAMETER
;
889 nt_status
= string_combinations(pass2
, password_check
, level
,
890 (const void *)rhost
);
891 if (NT_STATUS_IS_OK(nt_status
)) {
895 return NT_STATUS_WRONG_PASSWORD
;