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 */
25 #include "system/passwd.h"
28 #define DBGC_CLASS DBGC_AUTH
30 /* these are kept here to keep the string_combinations function simple */
31 static fstring this_user;
32 #if !defined(WITH_PAM)
33 static fstring this_salt;
34 static fstring this_crypted;
40 #include <afs/kautils.h>
42 /*******************************************************************
43 check on AFS authentication
44 ********************************************************************/
45 static BOOL afs_auth(char *user, char *password)
47 long password_expires = 0;
50 /* For versions of AFS prior to 3.3, this routine has few arguments, */
51 /* but since I can't find the old documentation... :-) */
53 if (ka_UserAuthenticateGeneral
54 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
56 password, 0, /* lifetime, default */
57 &password_expires, /*days 'til it expires */
64 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
72 #include <dce/dce_error.h>
73 #include <dce/sec_login.h>
75 /*****************************************************************
76 This new version of the DFS_AUTH code was donated by Karsten Muuss
77 <muuss@or.uni-bonn.de>. It fixes the following problems with the
80 - Server credentials may expire
81 - Client credential cache files have wrong owner
82 - purge_context() function is called with invalid argument
84 This new code was modified to ensure that on exit the uid/gid is
85 still root, and the original directory is restored. JRA.
86 ******************************************************************/
88 sec_login_handle_t my_dce_sec_context;
89 int dcelogin_atmost_once = 0;
91 /*******************************************************************
92 check on a DCE/DFS authentication
93 ********************************************************************/
94 static BOOL dfs_auth(char *user, char *password)
99 signed32 expire_time, current_time;
100 boolean32 password_reset;
102 sec_passwd_rec_t passwd_rec;
103 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
104 uint8_t dce_errstr[dce_c_error_string_len];
107 if (dcelogin_atmost_once)
112 * We only go for a DCE login context if the given password
113 * matches that stored in the local password file..
114 * Assumes local passwd file is kept in sync w/ DCE RGY!
117 if (strcmp((char *)crypt(password, this_salt), this_crypted))
123 sec_login_get_current_context(&my_dce_sec_context, &err);
124 if (err != error_status_ok)
126 dce_error_inq_text(err, dce_errstr, &err2);
127 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
132 sec_login_certify_identity(my_dce_sec_context, &err);
133 if (err != error_status_ok)
135 dce_error_inq_text(err, dce_errstr, &err2);
136 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
141 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
142 if (err != error_status_ok)
144 dce_error_inq_text(err, dce_errstr, &err2);
145 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
152 if (expire_time < (current_time + 60))
155 sec_passwd_rec_t *key;
157 sec_login_get_pwent(my_dce_sec_context,
158 (sec_login_passwd_t *) & pw, &err);
159 if (err != error_status_ok)
161 dce_error_inq_text(err, dce_errstr, &err2);
162 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
167 sec_login_refresh_identity(my_dce_sec_context, &err);
168 if (err != error_status_ok)
170 dce_error_inq_text(err, dce_errstr, &err2);
171 DEBUG(0, ("DCE can't refresh identity. %s\n",
177 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
178 (uint8_t *)pw->pw_name,
179 sec_c_key_version_none,
180 (void **)&key, &err);
181 if (err != error_status_ok)
183 dce_error_inq_text(err, dce_errstr, &err2);
184 DEBUG(0, ("DCE can't get key for %s. %s\n",
185 pw->pw_name, dce_errstr));
190 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
191 &password_reset, &auth_src,
193 if (err != error_status_ok)
195 dce_error_inq_text(err, dce_errstr, &err2);
197 ("DCE can't validate and certify identity for %s. %s\n",
198 pw->pw_name, dce_errstr));
201 sec_key_mgmt_free_key(key, &err);
202 if (err != error_status_ok)
204 dce_error_inq_text(err, dce_errstr, &err2);
205 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
209 if (sec_login_setup_identity((uint8_t *)user,
211 &my_dce_sec_context, &err) == 0)
213 dce_error_inq_text(err, dce_errstr, &err2);
214 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
219 sec_login_get_pwent(my_dce_sec_context,
220 (sec_login_passwd_t *) & pw, &err);
221 if (err != error_status_ok)
223 dce_error_inq_text(err, dce_errstr, &err2);
224 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
229 sec_login_purge_context(&my_dce_sec_context, &err);
230 if (err != error_status_ok)
232 dce_error_inq_text(err, dce_errstr, &err2);
233 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
239 * NB. I'd like to change these to call something like change_to_user()
240 * instead but currently we don't have a connection
241 * context to become the correct user. This is already
242 * fairly platform specific code however, so I think
243 * this should be ok. I have added code to go
244 * back to being root on error though. JRA.
249 set_effective_gid(pw->pw_gid);
250 set_effective_uid(pw->pw_uid);
252 if (sec_login_setup_identity((uint8_t *)user,
254 &my_dce_sec_context, &err) == 0)
256 dce_error_inq_text(err, dce_errstr, &err2);
257 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
262 sec_login_get_pwent(my_dce_sec_context,
263 (sec_login_passwd_t *) & pw, &err);
264 if (err != error_status_ok)
266 dce_error_inq_text(err, dce_errstr, &err2);
267 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
271 passwd_rec.version_number = sec_passwd_c_version_none;
272 passwd_rec.pepper = NULL;
273 passwd_rec.key.key_type = sec_passwd_plain;
274 passwd_rec.key.tagged_union.plain = (idl_char *) password;
276 sec_login_validate_identity(my_dce_sec_context,
277 &passwd_rec, &password_reset,
279 if (err != error_status_ok)
281 dce_error_inq_text(err, dce_errstr, &err2);
283 ("DCE Identity Validation failed for principal %s: %s\n",
288 sec_login_certify_identity(my_dce_sec_context, &err);
289 if (err != error_status_ok)
291 dce_error_inq_text(err, dce_errstr, &err2);
292 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
296 if (auth_src != sec_login_auth_src_network)
298 DEBUG(0, ("DCE context has no network credentials.\n"));
301 sec_login_set_context(my_dce_sec_context, &err);
302 if (err != error_status_ok)
304 dce_error_inq_text(err, dce_errstr, &err2);
306 ("DCE login failed for principal %s, cant set context: %s\n",
309 sec_login_purge_context(&my_dce_sec_context, &err);
313 sec_login_get_pwent(my_dce_sec_context,
314 (sec_login_passwd_t *) & pw, &err);
315 if (err != error_status_ok)
317 dce_error_inq_text(err, dce_errstr, &err2);
318 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
322 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
323 user, sys_getpid()));
325 DEBUG(3, ("DCE principal: %s\n"
328 pw->pw_name, pw->pw_uid, pw->pw_gid));
329 DEBUG(3, (" info: %s\n"
332 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
334 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
335 if (err != error_status_ok)
337 dce_error_inq_text(err, dce_errstr, &err2);
338 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
342 set_effective_uid(0);
343 set_effective_gid(0);
346 ("DCE context expires: %s", asctime(localtime(&expire_time))));
348 dcelogin_atmost_once = 1;
353 /* Go back to root, JRA. */
354 set_effective_uid(0);
355 set_effective_gid(egid);
359 void dfs_unlogin(void)
363 uint8_t dce_errstr[dce_c_error_string_len];
365 sec_login_purge_context(&my_dce_sec_context, &err);
366 if (err != error_status_ok)
368 dce_error_inq_text(err, dce_errstr, &err2);
370 ("DCE purge login context failed for server instance %d: %s\n",
371 sys_getpid(), dce_errstr));
376 #ifdef LINUX_BIGCRYPT
377 /****************************************************************************
378 an enhanced crypt for Linux to handle password longer than 8 characters
379 ****************************************************************************/
380 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
382 #define LINUX_PASSWORD_SEG_CHARS 8
386 StrnCpy(salt, salt1, 2);
389 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
390 char *p = crypt(password, salt) + 2;
391 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
393 password += LINUX_PASSWORD_SEG_CHARS;
394 crypted += strlen(p);
402 /****************************************************************************
403 an enhanced crypt for OSF1
404 ****************************************************************************/
405 static char *osf1_bigcrypt(char *password, char *salt1)
407 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
412 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
413 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
416 StrnCpy(salt, salt1, 2);
417 StrnCpy(result, salt1, 2);
420 for (i = 0; i < parts; i++) {
421 p1 = crypt(p2, salt);
422 strncat(result, p1 + 2,
423 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
424 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
425 p2 += AUTH_CLEARTEXT_SEG_CHARS;
433 /****************************************************************************
434 apply a function to upper/lower case combinations
435 of a string and return true if one of them returns true.
436 try all combinations with N uppercase letters.
437 offset is the first char to try and change (start with 0)
438 it assumes the string starts lowercased
439 ****************************************************************************/
440 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
447 #ifdef PASSWORD_LENGTH
448 len = MIN(len, PASSWORD_LENGTH);
451 if (N <= 0 || offset >= len)
454 for (i = offset; i < (len - (N - 1)); i++) {
459 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
464 return (NT_STATUS_WRONG_PASSWORD);
467 /****************************************************************************
468 apply a function to upper/lower case combinations
469 of a string and return true if one of them returns true.
470 try all combinations with up to N uppercase letters.
471 offset is the first char to try and change (start with 0)
472 it assumes the string starts lowercased
473 ****************************************************************************/
474 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
478 for (n = 1; n <= N; n++)
479 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
481 return NT_STATUS_WRONG_PASSWORD;
485 /****************************************************************************
486 core of password checking routine
487 ****************************************************************************/
488 static NTSTATUS password_check(const char *password)
491 return smb_pam_passcheck(this_user, password);
497 if (afs_auth(this_user, password))
499 #endif /* WITH_AFS */
502 if (dfs_auth(this_user, password))
504 #endif /* WITH_DFS */
508 ret = (strcmp(osf1_bigcrypt(password, this_salt),
512 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
513 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
518 return NT_STATUS_WRONG_PASSWORD;
521 #endif /* OSF1_ENH_SEC */
524 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
528 return NT_STATUS_WRONG_PASSWORD;
531 #endif /* ULTRIX_AUTH */
533 #ifdef LINUX_BIGCRYPT
534 ret = (linux_bigcrypt(password, this_salt, this_crypted));
538 return NT_STATUS_WRONG_PASSWORD;
540 #endif /* LINUX_BIGCRYPT */
542 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
545 * Some systems have bigcrypt in the C library but might not
546 * actually use it for the password hashes (HPUX 10.20) is
547 * a noteable example. So we try bigcrypt first, followed
551 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
554 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
558 return NT_STATUS_WRONG_PASSWORD;
560 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
563 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
567 return NT_STATUS_WRONG_PASSWORD;
569 #endif /* HAVE_BIGCRYPT */
572 DEBUG(1, ("Warning - no crypt available\n"));
573 return NT_STATUS_LOGON_FAILURE;
574 #else /* HAVE_CRYPT */
575 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
579 return NT_STATUS_WRONG_PASSWORD;
581 #endif /* HAVE_CRYPT */
582 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
583 #endif /* WITH_PAM */
588 Does a string have any lowercase chars in it?
590 static BOOL strhaslower(const char *s)
593 if (islower(*s)) return True;
600 Does a string have any uppercase chars in it?
602 static BOOL strhasupper(const char *s)
605 if (isupper(*s)) return True;
612 /****************************************************************************
613 CHECK if a username/password is OK
614 the function pointer fn() points to a function to call when a successful
615 match is found and is used to update the encrypted password file
616 return NT_STATUS_OK on correct match, appropriate error otherwise
617 ****************************************************************************/
619 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
620 int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
623 int level = lp_passwordlevel();
628 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
632 return NT_STATUS_LOGON_FAILURE;
634 if (((!*password) || (!pwlen)) && !lp_null_passwords())
635 return NT_STATUS_LOGON_FAILURE;
637 #if defined(WITH_PAM)
640 * If we're using PAM we want to short-circuit all the
641 * checks below and dive straight into the PAM code.
644 fstrcpy(this_user, user);
646 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
648 #else /* Not using PAM */
650 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
653 DEBUG(3, ("Couldn't find user %s\n", user));
654 return NT_STATUS_NO_SUCH_USER;
658 /* Copy into global for the convenience of looping code */
659 /* Also the place to keep the 'password' no matter what
660 crazy struct it started in... */
661 fstrcpy(this_crypted, pass->pw_passwd);
662 fstrcpy(this_salt, pass->pw_passwd);
668 /* many shadow systems require you to be root to get
669 the password, in most cases this should already be
670 the case when this function is called, except
671 perhaps for IPC password changing requests */
673 spass = getspnam(pass->pw_name);
674 if (spass && spass->sp_pwdp) {
675 fstrcpy(this_crypted, spass->sp_pwdp);
676 fstrcpy(this_salt, spass->sp_pwdp);
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 fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
699 #ifdef HAVE_GETPWANAM
701 struct passwd_adjunct *pwret;
702 pwret = getpwanam(s);
703 if (pwret && pwret->pwa_passwd)
704 fstrcpy(this_crypted, pwret->pwa_passwd);
710 struct pr_passwd *mypasswd;
711 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
713 mypasswd = getprpwnam(user);
715 fstrcpy(this_user, mypasswd->ufld.fd_name);
716 fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
719 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
727 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
729 fstrcpy(this_crypted, ap->a_password);
735 #if defined(HAVE_TRUNCATED_SALT)
736 /* crypt on some platforms (HPUX in particular)
737 won't work with more than 2 salt characters. */
741 if (!*this_crypted) {
742 if (!lp_null_passwords()) {
743 DEBUG(2, ("Disallowing %s with null password\n",
745 return NT_STATUS_LOGON_FAILURE;
749 ("Allowing access to %s with null password\n",
755 #endif /* defined(WITH_PAM) */
757 /* try it as it came to us */
758 nt_status = password_check(password);
759 if NT_STATUS_IS_OK(nt_status) {
764 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
765 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
773 /* if the password was given to us with mixed case then we don't
774 * need to proceed as we know it hasn't been case modified by the
776 if (strhasupper(password) && strhaslower(password)) {
780 /* make a copy of it */
781 pstrcpy(pass2, password);
783 /* try all lowercase if it's currently all uppercase */
784 if (strhasupper(pass2)) {
786 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
795 return NT_STATUS_WRONG_PASSWORD;
798 /* last chance - all combinations of up to level chars upper! */
802 if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
808 return NT_STATUS_WRONG_PASSWORD;