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 */
27 #define DBGC_CLASS DBGC_AUTH
29 /* these are kept here to keep the string_combinations function simple */
30 static fstring this_user
;
31 #if !defined(WITH_PAM)
32 static fstring this_salt
;
33 static fstring this_crypted
;
39 #include <afs/kautils.h>
41 /*******************************************************************
42 check on AFS authentication
43 ********************************************************************/
44 static BOOL
afs_auth(char *user
, char *password
)
46 long password_expires
= 0;
49 /* For versions of AFS prior to 3.3, this routine has few arguments, */
50 /* but since I can't find the old documentation... :-) */
52 if (ka_UserAuthenticateGeneral
53 (KA_USERAUTH_VERSION
+ KA_USERAUTH_DOSETPAG
, user
, (char *)0, /* instance */
55 password
, 0, /* lifetime, default */
56 &password_expires
, /*days 'til it expires */
63 ("AFS authentication for \"%s\" failed (%s)\n", user
, reason
));
71 #include <dce/dce_error.h>
72 #include <dce/sec_login.h>
74 /*****************************************************************
75 This new version of the DFS_AUTH code was donated by Karsten Muuss
76 <muuss@or.uni-bonn.de>. It fixes the following problems with the
79 - Server credentials may expire
80 - Client credential cache files have wrong owner
81 - purge_context() function is called with invalid argument
83 This new code was modified to ensure that on exit the uid/gid is
84 still root, and the original directory is restored. JRA.
85 ******************************************************************/
87 sec_login_handle_t my_dce_sec_context
;
88 int dcelogin_atmost_once
= 0;
90 /*******************************************************************
91 check on a DCE/DFS authentication
92 ********************************************************************/
93 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);
345 ("DCE context expires: %s", asctime(localtime(&expire_time
))));
347 dcelogin_atmost_once
= 1;
352 /* Go back to root, JRA. */
353 set_effective_uid(0);
354 set_effective_gid(egid
);
358 void dfs_unlogin(void)
362 unsigned char dce_errstr
[dce_c_error_string_len
];
364 sec_login_purge_context(&my_dce_sec_context
, &err
);
365 if (err
!= error_status_ok
)
367 dce_error_inq_text(err
, dce_errstr
, &err2
);
369 ("DCE purge login context failed for server instance %d: %s\n",
370 sys_getpid(), dce_errstr
));
375 #ifdef LINUX_BIGCRYPT
376 /****************************************************************************
377 an enhanced crypt for Linux to handle password longer than 8 characters
378 ****************************************************************************/
379 static int linux_bigcrypt(char *password
, char *salt1
, char *crypted
)
381 #define LINUX_PASSWORD_SEG_CHARS 8
385 StrnCpy(salt
, salt1
, 2);
388 for (i
= strlen(password
); i
> 0; i
-= LINUX_PASSWORD_SEG_CHARS
) {
389 char *p
= crypt(password
, salt
) + 2;
390 if (strncmp(p
, crypted
, LINUX_PASSWORD_SEG_CHARS
) != 0)
392 password
+= LINUX_PASSWORD_SEG_CHARS
;
393 crypted
+= strlen(p
);
401 /****************************************************************************
402 an enhanced crypt for OSF1
403 ****************************************************************************/
404 static char *osf1_bigcrypt(char *password
, char *salt1
)
406 static char result
[AUTH_MAX_PASSWD_LENGTH
] = "";
411 int parts
= strlen(password
) / AUTH_CLEARTEXT_SEG_CHARS
;
412 if (strlen(password
) % AUTH_CLEARTEXT_SEG_CHARS
)
415 StrnCpy(salt
, salt1
, 2);
416 StrnCpy(result
, salt1
, 2);
419 for (i
= 0; i
< parts
; i
++) {
420 p1
= crypt(p2
, salt
);
421 strncat(result
, p1
+ 2,
422 AUTH_MAX_PASSWD_LENGTH
- strlen(p1
+ 2) - 1);
423 StrnCpy(salt
, &result
[2 + i
* AUTH_CIPHERTEXT_SEG_CHARS
], 2);
424 p2
+= AUTH_CLEARTEXT_SEG_CHARS
;
432 /****************************************************************************
433 apply a function to upper/lower case combinations
434 of a string and return true if one of them returns true.
435 try all combinations with N uppercase letters.
436 offset is the first char to try and change (start with 0)
437 it assumes the string starts lowercased
438 ****************************************************************************/
439 static NTSTATUS
string_combinations2(char *s
, int offset
, NTSTATUS (*fn
) (const char *),
446 #ifdef PASSWORD_LENGTH
447 len
= MIN(len
, PASSWORD_LENGTH
);
450 if (N
<= 0 || offset
>= len
)
453 for (i
= offset
; i
< (len
- (N
- 1)); i
++) {
458 if (!NT_STATUS_EQUAL(nt_status
= string_combinations2(s
, i
+ 1, fn
, N
- 1),NT_STATUS_WRONG_PASSWORD
)) {
463 return (NT_STATUS_WRONG_PASSWORD
);
466 /****************************************************************************
467 apply a function to upper/lower case combinations
468 of a string and return true if one of them returns true.
469 try all combinations with up to N uppercase letters.
470 offset is the first char to try and change (start with 0)
471 it assumes the string starts lowercased
472 ****************************************************************************/
473 static NTSTATUS
string_combinations(char *s
, NTSTATUS (*fn
) (const char *), int N
)
477 for (n
= 1; n
<= N
; n
++)
478 if (!NT_STATUS_EQUAL(nt_status
= string_combinations2(s
, 0, fn
, n
), NT_STATUS_WRONG_PASSWORD
))
480 return NT_STATUS_WRONG_PASSWORD
;
484 /****************************************************************************
485 core of password checking routine
486 ****************************************************************************/
487 static NTSTATUS
password_check(const char *password
)
490 return smb_pam_passcheck(this_user
, password
);
496 if (afs_auth(this_user
, password
))
498 #endif /* WITH_AFS */
501 if (dfs_auth(this_user
, password
))
503 #endif /* WITH_DFS */
507 ret
= (strcmp(osf1_bigcrypt(password
, this_salt
),
511 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
512 ret
= (strcmp((char *)crypt(password
, this_salt
), this_crypted
) == 0);
517 return NT_STATUS_WRONG_PASSWORD
;
520 #endif /* OSF1_ENH_SEC */
523 ret
= (strcmp((char *)crypt16(password
, this_salt
), this_crypted
) == 0);
527 return NT_STATUS_WRONG_PASSWORD
;
530 #endif /* ULTRIX_AUTH */
532 #ifdef LINUX_BIGCRYPT
533 ret
= (linux_bigcrypt(password
, this_salt
, this_crypted
));
537 return NT_STATUS_WRONG_PASSWORD
;
539 #endif /* LINUX_BIGCRYPT */
541 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
544 * Some systems have bigcrypt in the C library but might not
545 * actually use it for the password hashes (HPUX 10.20) is
546 * a noteable example. So we try bigcrypt first, followed
550 if (strcmp(bigcrypt(password
, this_salt
), this_crypted
) == 0)
553 ret
= (strcmp((char *)crypt(password
, this_salt
), this_crypted
) == 0);
557 return NT_STATUS_WRONG_PASSWORD
;
559 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
562 ret
= (strcmp(bigcrypt(password
, this_salt
), this_crypted
) == 0);
566 return NT_STATUS_WRONG_PASSWORD
;
568 #endif /* HAVE_BIGCRYPT */
571 DEBUG(1, ("Warning - no crypt available\n"));
572 return NT_STATUS_LOGON_FAILURE
;
573 #else /* HAVE_CRYPT */
574 ret
= (strcmp((char *)crypt(password
, this_salt
), this_crypted
) == 0);
578 return NT_STATUS_WRONG_PASSWORD
;
580 #endif /* HAVE_CRYPT */
581 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
582 #endif /* WITH_PAM */
587 /****************************************************************************
588 CHECK if a username/password is OK
589 the function pointer fn() points to a function to call when a successful
590 match is found and is used to update the encrypted password file
591 return NT_STATUS_OK on correct match, appropriate error otherwise
592 ****************************************************************************/
594 NTSTATUS
pass_check(const struct passwd
*pass
, const char *user
, const char *password
,
595 int pwlen
, BOOL (*fn
) (const char *, const char *), BOOL run_cracker
)
598 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
;
633 /* Copy into global for the convenience of looping code */
634 /* Also the place to keep the 'password' no matter what
635 crazy struct it started in... */
636 fstrcpy(this_crypted
, pass
->pw_passwd
);
637 fstrcpy(this_salt
, pass
->pw_passwd
);
643 /* many shadow systems require you to be root to get
644 the password, in most cases this should already be
645 the case when this function is called, except
646 perhaps for IPC password changing requests */
648 spass
= getspnam(pass
->pw_name
);
649 if (spass
&& spass
->sp_pwdp
) {
650 fstrcpy(this_crypted
, spass
->sp_pwdp
);
651 fstrcpy(this_salt
, spass
->sp_pwdp
);
654 #elif defined(IA_UINFO)
656 /* Need to get password with SVR4.2's ia_ functions
657 instead of get{sp,pw}ent functions. Required by
658 UnixWare 2.x, tested on version
659 2.1. (tangent@cyberport.com) */
661 if (ia_openinfo(pass
->pw_name
, &uinfo
) != -1)
662 ia_get_logpwd(uinfo
, &(pass
->pw_passwd
));
666 #ifdef HAVE_GETPRPWNAM
668 struct pr_passwd
*pr_pw
= getprpwnam(pass
->pw_name
);
669 if (pr_pw
&& pr_pw
->ufld
.fd_encrypt
)
670 fstrcpy(this_crypted
, pr_pw
->ufld
.fd_encrypt
);
674 #ifdef HAVE_GETPWANAM
676 struct passwd_adjunct
*pwret
;
677 pwret
= getpwanam(s
);
678 if (pwret
&& pwret
->pwa_passwd
)
679 fstrcpy(this_crypted
, pwret
->pwa_passwd
);
685 struct pr_passwd
*mypasswd
;
686 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
688 mypasswd
= getprpwnam(user
);
690 fstrcpy(this_user
, mypasswd
->ufld
.fd_name
);
691 fstrcpy(this_crypted
, mypasswd
->ufld
.fd_encrypt
);
694 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
702 AUTHORIZATION
*ap
= getauthuid(pass
->pw_uid
);
704 fstrcpy(this_crypted
, ap
->a_password
);
710 #if defined(HAVE_TRUNCATED_SALT)
711 /* crypt on some platforms (HPUX in particular)
712 won't work with more than 2 salt characters. */
716 if (!*this_crypted
) {
717 if (!lp_null_passwords()) {
718 DEBUG(2, ("Disallowing %s with null password\n",
720 return NT_STATUS_LOGON_FAILURE
;
724 ("Allowing access to %s with null password\n",
730 #endif /* defined(WITH_PAM) */
732 /* try it as it came to us */
733 nt_status
= password_check(password
);
734 if NT_STATUS_IS_OK(nt_status
) {
739 } else if (!NT_STATUS_EQUAL(nt_status
, NT_STATUS_WRONG_PASSWORD
)) {
740 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
748 /* if the password was given to us with mixed case then we don't
749 * need to proceed as we know it hasn't been case modified by the
751 if (strhasupper(password
) && strhaslower(password
)) {
755 /* make a copy of it */
756 pstrcpy(pass2
, password
);
758 /* try all lowercase if it's currently all uppercase */
759 if (strhasupper(pass2
)) {
761 if NT_STATUS_IS_OK(nt_status
= password_check(pass2
)) {
770 return NT_STATUS_WRONG_PASSWORD
;
773 /* last chance - all combinations of up to level chars upper! */
776 if (NT_STATUS_IS_OK(nt_status
= string_combinations(pass2
, password_check
, level
))) {
782 return NT_STATUS_WRONG_PASSWORD
;