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"
26 #include "system/iconv.h"
29 #define DBGC_CLASS DBGC_AUTH
31 /* these are kept here to keep the string_combinations function simple */
32 static fstring this_user;
33 #if !defined(WITH_PAM)
34 static fstring this_salt;
35 static fstring this_crypted;
41 #include <afs/kautils.h>
43 /*******************************************************************
44 check on AFS authentication
45 ********************************************************************/
46 static BOOL afs_auth(char *user, char *password)
48 long password_expires = 0;
51 /* For versions of AFS prior to 3.3, this routine has few arguments, */
52 /* but since I can't find the old documentation... :-) */
54 if (ka_UserAuthenticateGeneral
55 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
57 password, 0, /* lifetime, default */
58 &password_expires, /*days 'til it expires */
65 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
73 #include <dce/dce_error.h>
74 #include <dce/sec_login.h>
76 /*****************************************************************
77 This new version of the DFS_AUTH code was donated by Karsten Muuss
78 <muuss@or.uni-bonn.de>. It fixes the following problems with the
81 - Server credentials may expire
82 - Client credential cache files have wrong owner
83 - purge_context() function is called with invalid argument
85 This new code was modified to ensure that on exit the uid/gid is
86 still root, and the original directory is restored. JRA.
87 ******************************************************************/
89 sec_login_handle_t my_dce_sec_context;
90 int dcelogin_atmost_once = 0;
92 /*******************************************************************
93 check on a DCE/DFS authentication
94 ********************************************************************/
95 static BOOL dfs_auth(char *user, char *password)
100 signed32 expire_time, current_time;
101 boolean32 password_reset;
103 sec_passwd_rec_t passwd_rec;
104 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
105 uint8_t dce_errstr[dce_c_error_string_len];
108 if (dcelogin_atmost_once)
113 * We only go for a DCE login context if the given password
114 * matches that stored in the local password file..
115 * Assumes local passwd file is kept in sync w/ DCE RGY!
118 if (strcmp((char *)crypt(password, this_salt), this_crypted))
124 sec_login_get_current_context(&my_dce_sec_context, &err);
125 if (err != error_status_ok)
127 dce_error_inq_text(err, dce_errstr, &err2);
128 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
133 sec_login_certify_identity(my_dce_sec_context, &err);
134 if (err != error_status_ok)
136 dce_error_inq_text(err, dce_errstr, &err2);
137 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
142 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
143 if (err != error_status_ok)
145 dce_error_inq_text(err, dce_errstr, &err2);
146 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
153 if (expire_time < (current_time + 60))
156 sec_passwd_rec_t *key;
158 sec_login_get_pwent(my_dce_sec_context,
159 (sec_login_passwd_t *) & pw, &err);
160 if (err != error_status_ok)
162 dce_error_inq_text(err, dce_errstr, &err2);
163 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
168 sec_login_refresh_identity(my_dce_sec_context, &err);
169 if (err != error_status_ok)
171 dce_error_inq_text(err, dce_errstr, &err2);
172 DEBUG(0, ("DCE can't refresh identity. %s\n",
178 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
179 (uint8_t *)pw->pw_name,
180 sec_c_key_version_none,
181 (void **)&key, &err);
182 if (err != error_status_ok)
184 dce_error_inq_text(err, dce_errstr, &err2);
185 DEBUG(0, ("DCE can't get key for %s. %s\n",
186 pw->pw_name, dce_errstr));
191 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
192 &password_reset, &auth_src,
194 if (err != error_status_ok)
196 dce_error_inq_text(err, dce_errstr, &err2);
198 ("DCE can't validate and certify identity for %s. %s\n",
199 pw->pw_name, dce_errstr));
202 sec_key_mgmt_free_key(key, &err);
203 if (err != error_status_ok)
205 dce_error_inq_text(err, dce_errstr, &err2);
206 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
210 if (sec_login_setup_identity((uint8_t *)user,
212 &my_dce_sec_context, &err) == 0)
214 dce_error_inq_text(err, dce_errstr, &err2);
215 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
220 sec_login_get_pwent(my_dce_sec_context,
221 (sec_login_passwd_t *) & pw, &err);
222 if (err != error_status_ok)
224 dce_error_inq_text(err, dce_errstr, &err2);
225 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
230 sec_login_purge_context(&my_dce_sec_context, &err);
231 if (err != error_status_ok)
233 dce_error_inq_text(err, dce_errstr, &err2);
234 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
240 * NB. I'd like to change these to call something like change_to_user()
241 * instead but currently we don't have a connection
242 * context to become the correct user. This is already
243 * fairly platform specific code however, so I think
244 * this should be ok. I have added code to go
245 * back to being root on error though. JRA.
250 set_effective_gid(pw->pw_gid);
251 set_effective_uid(pw->pw_uid);
253 if (sec_login_setup_identity((uint8_t *)user,
255 &my_dce_sec_context, &err) == 0)
257 dce_error_inq_text(err, dce_errstr, &err2);
258 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
263 sec_login_get_pwent(my_dce_sec_context,
264 (sec_login_passwd_t *) & pw, &err);
265 if (err != error_status_ok)
267 dce_error_inq_text(err, dce_errstr, &err2);
268 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
272 passwd_rec.version_number = sec_passwd_c_version_none;
273 passwd_rec.pepper = NULL;
274 passwd_rec.key.key_type = sec_passwd_plain;
275 passwd_rec.key.tagged_union.plain = (idl_char *) password;
277 sec_login_validate_identity(my_dce_sec_context,
278 &passwd_rec, &password_reset,
280 if (err != error_status_ok)
282 dce_error_inq_text(err, dce_errstr, &err2);
284 ("DCE Identity Validation failed for principal %s: %s\n",
289 sec_login_certify_identity(my_dce_sec_context, &err);
290 if (err != error_status_ok)
292 dce_error_inq_text(err, dce_errstr, &err2);
293 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
297 if (auth_src != sec_login_auth_src_network)
299 DEBUG(0, ("DCE context has no network credentials.\n"));
302 sec_login_set_context(my_dce_sec_context, &err);
303 if (err != error_status_ok)
305 dce_error_inq_text(err, dce_errstr, &err2);
307 ("DCE login failed for principal %s, cant set context: %s\n",
310 sec_login_purge_context(&my_dce_sec_context, &err);
314 sec_login_get_pwent(my_dce_sec_context,
315 (sec_login_passwd_t *) & pw, &err);
316 if (err != error_status_ok)
318 dce_error_inq_text(err, dce_errstr, &err2);
319 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
323 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
324 user, sys_getpid()));
326 DEBUG(3, ("DCE principal: %s\n"
329 pw->pw_name, pw->pw_uid, pw->pw_gid));
330 DEBUG(3, (" info: %s\n"
333 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
335 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
336 if (err != error_status_ok)
338 dce_error_inq_text(err, dce_errstr, &err2);
339 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
343 set_effective_uid(0);
344 set_effective_gid(0);
347 ("DCE context expires: %s", asctime(localtime(&expire_time))));
349 dcelogin_atmost_once = 1;
354 /* Go back to root, JRA. */
355 set_effective_uid(0);
356 set_effective_gid(egid);
360 void dfs_unlogin(void)
364 uint8_t dce_errstr[dce_c_error_string_len];
366 sec_login_purge_context(&my_dce_sec_context, &err);
367 if (err != error_status_ok)
369 dce_error_inq_text(err, dce_errstr, &err2);
371 ("DCE purge login context failed for server instance %d: %s\n",
372 sys_getpid(), dce_errstr));
377 #ifdef LINUX_BIGCRYPT
378 /****************************************************************************
379 an enhanced crypt for Linux to handle password longer than 8 characters
380 ****************************************************************************/
381 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
383 #define LINUX_PASSWORD_SEG_CHARS 8
387 StrnCpy(salt, salt1, 2);
390 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
391 char *p = crypt(password, salt) + 2;
392 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
394 password += LINUX_PASSWORD_SEG_CHARS;
395 crypted += strlen(p);
403 /****************************************************************************
404 an enhanced crypt for OSF1
405 ****************************************************************************/
406 static char *osf1_bigcrypt(char *password, char *salt1)
408 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
413 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
414 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
417 StrnCpy(salt, salt1, 2);
418 StrnCpy(result, salt1, 2);
421 for (i = 0; i < parts; i++) {
422 p1 = crypt(p2, salt);
423 strncat(result, p1 + 2,
424 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
425 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
426 p2 += AUTH_CLEARTEXT_SEG_CHARS;
434 /****************************************************************************
435 apply a function to upper/lower case combinations
436 of a string and return true if one of them returns true.
437 try all combinations with N uppercase letters.
438 offset is the first char to try and change (start with 0)
439 it assumes the string starts lowercased
440 ****************************************************************************/
441 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
448 #ifdef PASSWORD_LENGTH
449 len = MIN(len, PASSWORD_LENGTH);
452 if (N <= 0 || offset >= len)
455 for (i = offset; i < (len - (N - 1)); i++) {
460 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
465 return (NT_STATUS_WRONG_PASSWORD);
468 /****************************************************************************
469 apply a function to upper/lower case combinations
470 of a string and return true if one of them returns true.
471 try all combinations with up to N uppercase letters.
472 offset is the first char to try and change (start with 0)
473 it assumes the string starts lowercased
474 ****************************************************************************/
475 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
479 for (n = 1; n <= N; n++)
480 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
482 return NT_STATUS_WRONG_PASSWORD;
486 /****************************************************************************
487 core of password checking routine
488 ****************************************************************************/
489 static NTSTATUS password_check(const char *password)
492 return smb_pam_passcheck(this_user, password);
498 if (afs_auth(this_user, password))
500 #endif /* WITH_AFS */
503 if (dfs_auth(this_user, password))
505 #endif /* WITH_DFS */
509 ret = (strcmp(osf1_bigcrypt(password, this_salt),
513 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
514 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
519 return NT_STATUS_WRONG_PASSWORD;
522 #endif /* OSF1_ENH_SEC */
525 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
529 return NT_STATUS_WRONG_PASSWORD;
532 #endif /* ULTRIX_AUTH */
534 #ifdef LINUX_BIGCRYPT
535 ret = (linux_bigcrypt(password, this_salt, this_crypted));
539 return NT_STATUS_WRONG_PASSWORD;
541 #endif /* LINUX_BIGCRYPT */
543 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
546 * Some systems have bigcrypt in the C library but might not
547 * actually use it for the password hashes (HPUX 10.20) is
548 * a noteable example. So we try bigcrypt first, followed
552 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
555 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
559 return NT_STATUS_WRONG_PASSWORD;
561 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
564 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
568 return NT_STATUS_WRONG_PASSWORD;
570 #endif /* HAVE_BIGCRYPT */
573 DEBUG(1, ("Warning - no crypt available\n"));
574 return NT_STATUS_LOGON_FAILURE;
575 #else /* HAVE_CRYPT */
576 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
580 return NT_STATUS_WRONG_PASSWORD;
582 #endif /* HAVE_CRYPT */
583 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
584 #endif /* WITH_PAM */
589 Does a string have any lowercase chars in it?
591 static BOOL strhaslower(const char *s)
594 if (islower(*s)) return True;
601 Does a string have any uppercase chars in it?
603 static BOOL strhasupper(const char *s)
606 if (isupper(*s)) return True;
613 /****************************************************************************
614 CHECK if a username/password is OK
615 the function pointer fn() points to a function to call when a successful
616 match is found and is used to update the encrypted password file
617 return NT_STATUS_OK on correct match, appropriate error otherwise
618 ****************************************************************************/
620 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
621 int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
624 int level = lp_passwordlevel();
629 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
633 return NT_STATUS_LOGON_FAILURE;
635 if (((!*password) || (!pwlen)) && !lp_null_passwords())
636 return NT_STATUS_LOGON_FAILURE;
638 #if defined(WITH_PAM)
641 * If we're using PAM we want to short-circuit all the
642 * checks below and dive straight into the PAM code.
645 fstrcpy(this_user, user);
647 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
649 #else /* Not using PAM */
651 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
654 DEBUG(3, ("Couldn't find user %s\n", user));
655 return NT_STATUS_NO_SUCH_USER;
659 /* Copy into global for the convenience of looping code */
660 /* Also the place to keep the 'password' no matter what
661 crazy struct it started in... */
662 fstrcpy(this_crypted, pass->pw_passwd);
663 fstrcpy(this_salt, pass->pw_passwd);
669 /* many shadow systems require you to be root to get
670 the password, in most cases this should already be
671 the case when this function is called, except
672 perhaps for IPC password changing requests */
674 spass = getspnam(pass->pw_name);
675 if (spass && spass->sp_pwdp) {
676 fstrcpy(this_crypted, spass->sp_pwdp);
677 fstrcpy(this_salt, spass->sp_pwdp);
680 #elif defined(IA_UINFO)
682 /* Need to get password with SVR4.2's ia_ functions
683 instead of get{sp,pw}ent functions. Required by
684 UnixWare 2.x, tested on version
685 2.1. (tangent@cyberport.com) */
687 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
688 ia_get_logpwd(uinfo, &(pass->pw_passwd));
692 #ifdef HAVE_GETPRPWNAM
694 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
695 if (pr_pw && pr_pw->ufld.fd_encrypt)
696 fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
700 #ifdef HAVE_GETPWANAM
702 struct passwd_adjunct *pwret;
703 pwret = getpwanam(s);
704 if (pwret && pwret->pwa_passwd)
705 fstrcpy(this_crypted, pwret->pwa_passwd);
711 struct pr_passwd *mypasswd;
712 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
714 mypasswd = getprpwnam(user);
716 fstrcpy(this_user, mypasswd->ufld.fd_name);
717 fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
720 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
728 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
730 fstrcpy(this_crypted, ap->a_password);
736 #if defined(HAVE_TRUNCATED_SALT)
737 /* crypt on some platforms (HPUX in particular)
738 won't work with more than 2 salt characters. */
742 if (!*this_crypted) {
743 if (!lp_null_passwords()) {
744 DEBUG(2, ("Disallowing %s with null password\n",
746 return NT_STATUS_LOGON_FAILURE;
750 ("Allowing access to %s with null password\n",
756 #endif /* defined(WITH_PAM) */
758 /* try it as it came to us */
759 nt_status = password_check(password);
760 if NT_STATUS_IS_OK(nt_status) {
765 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
766 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
774 /* if the password was given to us with mixed case then we don't
775 * need to proceed as we know it hasn't been case modified by the
777 if (strhasupper(password) && strhaslower(password)) {
781 /* make a copy of it */
782 pstrcpy(pass2, password);
784 /* try all lowercase if it's currently all uppercase */
785 if (strhasupper(pass2)) {
787 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
796 return NT_STATUS_WRONG_PASSWORD;
799 /* last chance - all combinations of up to level chars upper! */
803 if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
809 return NT_STATUS_WRONG_PASSWORD;