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 core of password checking routine
498 ****************************************************************************/
499 static NTSTATUS
password_check(const char *password
, const void *private_data
)
502 const char *rhost
= (const char *)private_data
;
503 return smb_pam_passcheck(get_this_user(), rhost
, password
);
509 if (afs_auth(get_this_user(), password
))
511 #endif /* WITH_AFS */
514 if (dfs_auth(get_this_user(), password
))
516 #endif /* WITH_DFS */
520 ret
= (strcmp(osf1_bigcrypt(password
, get_this_salt()),
521 get_this_crypted()) == 0);
524 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
525 ret
= (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()) == 0);
530 return NT_STATUS_WRONG_PASSWORD
;
533 #endif /* OSF1_ENH_SEC */
536 ret
= (strcmp((char *)crypt16(password
, get_this_salt()), get_this_crypted()) == 0);
540 return NT_STATUS_WRONG_PASSWORD
;
543 #endif /* ULTRIX_AUTH */
545 #ifdef LINUX_BIGCRYPT
546 ret
= (linux_bigcrypt(password
, get_this_salt(), get_this_crypted()));
550 return NT_STATUS_WRONG_PASSWORD
;
552 #endif /* LINUX_BIGCRYPT */
554 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
557 * Some systems have bigcrypt in the C library but might not
558 * actually use it for the password hashes (HPUX 10.20) is
559 * a noteable example. So we try bigcrypt first, followed
563 if (strcmp(bigcrypt(password
, get_this_salt()), get_this_crypted()) == 0)
566 ret
= (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()) == 0);
570 return NT_STATUS_WRONG_PASSWORD
;
572 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
575 ret
= (strcmp(bigcrypt(password
, get_this_salt()), get_this_crypted()) == 0);
579 return NT_STATUS_WRONG_PASSWORD
;
581 #endif /* HAVE_BIGCRYPT */
584 DEBUG(1, ("Warning - no crypt available\n"));
585 return NT_STATUS_LOGON_FAILURE
;
586 #else /* HAVE_CRYPT */
587 ret
= (strcmp((char *)crypt(password
, get_this_salt()), get_this_crypted()) == 0);
591 return NT_STATUS_WRONG_PASSWORD
;
593 #endif /* HAVE_CRYPT */
594 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
595 #endif /* WITH_PAM */
600 /****************************************************************************
601 CHECK if a username/password is OK
602 the function pointer fn() points to a function to call when a successful
603 match is found and is used to update the encrypted password file
604 return NT_STATUS_OK on correct match, appropriate error otherwise
605 ****************************************************************************/
607 NTSTATUS
pass_check(const struct passwd
*pass
,
610 const char *password
,
617 #ifdef DEBUG_PASSWORD
618 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user
, password
));
622 return NT_STATUS_LOGON_FAILURE
;
624 if ((!*password
) && !lp_null_passwords())
625 return NT_STATUS_LOGON_FAILURE
;
627 #if defined(WITH_PAM)
630 * If we're using PAM we want to short-circuit all the
631 * checks below and dive straight into the PAM code.
634 if (set_this_user(user
) == NULL
) {
635 return NT_STATUS_NO_MEMORY
;
638 DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user
));
640 #else /* Not using PAM */
642 DEBUG(4, ("pass_check: Checking password for user %s\n", user
));
645 DEBUG(3, ("Couldn't find user %s\n", user
));
646 return NT_STATUS_NO_SUCH_USER
;
650 /* Copy into global for the convenience of looping code */
651 /* Also the place to keep the 'password' no matter what
652 crazy struct it started in... */
653 if (set_this_crypted(pass
->pw_passwd
) == NULL
) {
654 return NT_STATUS_NO_MEMORY
;
656 if (set_this_salt(pass
->pw_passwd
) == NULL
) {
657 return NT_STATUS_NO_MEMORY
;
664 /* many shadow systems require you to be root to get
665 the password, in most cases this should already be
666 the case when this function is called, except
667 perhaps for IPC password changing requests */
669 spass
= getspnam(pass
->pw_name
);
670 if (spass
&& spass
->sp_pwdp
) {
671 if (set_this_crypted(spass
->sp_pwdp
) == NULL
) {
672 return NT_STATUS_NO_MEMORY
;
674 if (set_this_salt(spass
->sp_pwdp
) == NULL
) {
675 return NT_STATUS_NO_MEMORY
;
679 #elif defined(IA_UINFO)
681 /* Need to get password with SVR4.2's ia_ functions
682 instead of get{sp,pw}ent functions. Required by
683 UnixWare 2.x, tested on version
684 2.1. (tangent@cyberport.com) */
686 if (ia_openinfo(pass
->pw_name
, &uinfo
) != -1)
687 ia_get_logpwd(uinfo
, &(pass
->pw_passwd
));
691 #ifdef HAVE_GETPRPWNAM
693 struct pr_passwd
*pr_pw
= getprpwnam(pass
->pw_name
);
694 if (pr_pw
&& pr_pw
->ufld
.fd_encrypt
) {
695 if (set_this_crypted(pr_pw
->ufld
.fd_encrypt
) == NULL
) {
696 return NT_STATUS_NO_MEMORY
;
702 #ifdef HAVE_GETPWANAM
704 struct passwd_adjunct
*pwret
;
705 pwret
= getpwanam(s
);
706 if (pwret
&& pwret
->pwa_passwd
) {
707 if (set_this_crypted(pwret
->pwa_passwd
) == NULL
) {
708 return NT_STATUS_NO_MEMORY
;
716 struct pr_passwd
*mypasswd
;
717 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
719 mypasswd
= getprpwnam(user
);
721 if (set_this_user(mypasswd
->ufld
.fd_name
) == NULL
) {
722 return NT_STATUS_NO_MEMORY
;
724 if (set_this_crypted(mypasswd
->ufld
.fd_encrypt
) == NULL
) {
725 return NT_STATUS_NO_MEMORY
;
729 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
737 AUTHORIZATION
*ap
= getauthuid(pass
->pw_uid
);
739 if (set_this_crypted(ap
->a_password
) == NULL
) {
741 return NT_STATUS_NO_MEMORY
;
748 #if defined(HAVE_TRUNCATED_SALT)
749 /* crypt on some platforms (HPUX in particular)
750 won't work with more than 2 salt characters. */
752 char *trunc_salt
= get_this_salt();
753 if (!trunc_salt
|| strlen(trunc_salt
) < 2) {
754 return NT_STATUS_LOGON_FAILURE
;
757 if (set_this_salt(trunc_salt
) == NULL
) {
758 return NT_STATUS_NO_MEMORY
;
763 if (!get_this_crypted() || !*get_this_crypted()) {
764 if (!lp_null_passwords()) {
765 DEBUG(2, ("Disallowing %s with null password\n",
767 return NT_STATUS_LOGON_FAILURE
;
771 ("Allowing access to %s with null password\n",
777 #endif /* defined(WITH_PAM) */
779 /* try it as it came to us */
780 nt_status
= password_check(password
, (const void *)rhost
);
781 if NT_STATUS_IS_OK(nt_status
) {
783 } else if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_WRONG_PASSWORD
)) {
784 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
792 /* if the password was given to us with mixed case then we don't
793 * need to proceed as we know it hasn't been case modified by the
795 if (strhasupper(password
) && strhaslower(password
)) {
799 /* make a copy of it */
800 pass2
= talloc_strdup(talloc_tos(), password
);
802 return NT_STATUS_NO_MEMORY
;
805 /* try all lowercase if it's currently all uppercase */
806 if (strhasupper(pass2
)) {
807 if (!strlower_m(pass2
)) {
808 return NT_STATUS_INVALID_PARAMETER
;
810 nt_status
= password_check(pass2
, (const void *)rhost
);
811 if (NT_STATUS_IS_OK(nt_status
)) {
816 return NT_STATUS_WRONG_PASSWORD
;