sync this function with 2.2 (single check for NULL parameter)
[Samba/gbeck.git] / source / passdb / pass_check.c
blob9424189b2366ad53152545d2329e12e73553e2ed
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 Password checking
5 Copyright (C) Andrew Tridgell 1992-1998
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* this module is for checking a username/password against a system
23 password database. The SMB encrypted password support is elsewhere */
25 #include "includes.h"
27 extern int DEBUGLEVEL;
29 /* these are kept here to keep the string_combinations function simple */
30 static char this_user[100] = "";
31 static char this_salt[100] = "";
32 static char this_crypted[100] = "";
34 #ifdef WITH_AFS
36 #include <afs/stds.h>
37 #include <afs/kautils.h>
39 /*******************************************************************
40 check on AFS authentication
41 ********************************************************************/
42 static BOOL afs_auth(char *user, char *password)
44 long password_expires = 0;
45 char *reason;
47 /* For versions of AFS prior to 3.3, this routine has few arguments, */
48 /* but since I can't find the old documentation... :-) */
49 setpag();
50 if (ka_UserAuthenticateGeneral
51 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
52 (char *)0, /* cell */
53 password, 0, /* lifetime, default */
54 &password_expires, /*days 'til it expires */
55 0, /* spare 2 */
56 &reason) == 0)
58 return (True);
60 DEBUG(1,
61 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
62 return (False);
64 #endif
67 #ifdef WITH_DFS
69 #include <dce/dce_error.h>
70 #include <dce/sec_login.h>
72 /*****************************************************************
73 This new version of the DFS_AUTH code was donated by Karsten Muuss
74 <muuss@or.uni-bonn.de>. It fixes the following problems with the
75 old code :
77 - Server credentials may expire
78 - Client credential cache files have wrong owner
79 - purge_context() function is called with invalid argument
81 This new code was modified to ensure that on exit the uid/gid is
82 still root, and the original directory is restored. JRA.
83 ******************************************************************/
85 sec_login_handle_t my_dce_sec_context;
86 int dcelogin_atmost_once = 0;
88 /*******************************************************************
89 check on a DCE/DFS authentication
90 ********************************************************************/
91 static BOOL dfs_auth(char *user, char *password)
93 error_status_t err;
94 int err2;
95 int prterr;
96 signed32 expire_time, current_time;
97 boolean32 password_reset;
98 struct passwd *pw;
99 sec_passwd_rec_t passwd_rec;
100 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
101 unsigned char dce_errstr[dce_c_error_string_len];
102 gid_t egid;
104 if (dcelogin_atmost_once)
105 return (False);
107 #ifdef HAVE_CRYPT
109 * We only go for a DCE login context if the given password
110 * matches that stored in the local password file..
111 * Assumes local passwd file is kept in sync w/ DCE RGY!
114 if (strcmp((char *)crypt(password, this_salt), this_crypted))
116 return (False);
118 #endif
120 sec_login_get_current_context(&my_dce_sec_context, &err);
121 if (err != error_status_ok)
123 dce_error_inq_text(err, dce_errstr, &err2);
124 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
126 return (False);
129 sec_login_certify_identity(my_dce_sec_context, &err);
130 if (err != error_status_ok)
132 dce_error_inq_text(err, dce_errstr, &err2);
133 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
135 return (False);
138 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
139 if (err != error_status_ok)
141 dce_error_inq_text(err, dce_errstr, &err2);
142 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
144 return (False);
147 time(&current_time);
149 if (expire_time < (current_time + 60))
151 struct passwd *pw;
152 sec_passwd_rec_t *key;
154 sec_login_get_pwent(my_dce_sec_context,
155 (sec_login_passwd_t *) & pw, &err);
156 if (err != error_status_ok)
158 dce_error_inq_text(err, dce_errstr, &err2);
159 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
161 return (False);
164 sec_login_refresh_identity(my_dce_sec_context, &err);
165 if (err != error_status_ok)
167 dce_error_inq_text(err, dce_errstr, &err2);
168 DEBUG(0, ("DCE can't refresh identity. %s\n",
169 dce_errstr));
171 return (False);
174 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
175 (unsigned char *)pw->pw_name,
176 sec_c_key_version_none,
177 (void **)&key, &err);
178 if (err != error_status_ok)
180 dce_error_inq_text(err, dce_errstr, &err2);
181 DEBUG(0, ("DCE can't get key for %s. %s\n",
182 pw->pw_name, dce_errstr));
184 return (False);
187 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
188 &password_reset, &auth_src,
189 &err);
190 if (err != error_status_ok)
192 dce_error_inq_text(err, dce_errstr, &err2);
193 DEBUG(0,
194 ("DCE can't validate and certify identity for %s. %s\n",
195 pw->pw_name, dce_errstr));
198 sec_key_mgmt_free_key(key, &err);
199 if (err != error_status_ok)
201 dce_error_inq_text(err, dce_errstr, &err2);
202 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
206 if (sec_login_setup_identity((unsigned char *)user,
207 sec_login_no_flags,
208 &my_dce_sec_context, &err) == 0)
210 dce_error_inq_text(err, dce_errstr, &err2);
211 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
212 user, dce_errstr));
213 return (False);
216 sec_login_get_pwent(my_dce_sec_context,
217 (sec_login_passwd_t *) & pw, &err);
218 if (err != error_status_ok)
220 dce_error_inq_text(err, dce_errstr, &err2);
221 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
223 return (False);
226 sec_login_purge_context(&my_dce_sec_context, &err);
227 if (err != error_status_ok)
229 dce_error_inq_text(err, dce_errstr, &err2);
230 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
232 return (False);
236 * NB. I'd like to change these to call something like become_user()
237 * instead but currently we don't have a connection
238 * context to become the correct user. This is already
239 * fairly platform specific code however, so I think
240 * this should be ok. I have added code to go
241 * back to being root on error though. JRA.
244 egid = getegid();
246 set_effective_gid(pw->pw_gid);
247 set_effective_uid(pw->pw_uid);
249 if (sec_login_setup_identity((unsigned char *)user,
250 sec_login_no_flags,
251 &my_dce_sec_context, &err) == 0)
253 dce_error_inq_text(err, dce_errstr, &err2);
254 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
255 user, dce_errstr));
256 goto err;
259 sec_login_get_pwent(my_dce_sec_context,
260 (sec_login_passwd_t *) & pw, &err);
261 if (err != error_status_ok)
263 dce_error_inq_text(err, dce_errstr, &err2);
264 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
265 goto err;
268 passwd_rec.version_number = sec_passwd_c_version_none;
269 passwd_rec.pepper = NULL;
270 passwd_rec.key.key_type = sec_passwd_plain;
271 passwd_rec.key.tagged_union.plain = (idl_char *) password;
273 sec_login_validate_identity(my_dce_sec_context,
274 &passwd_rec, &password_reset,
275 &auth_src, &err);
276 if (err != error_status_ok)
278 dce_error_inq_text(err, dce_errstr, &err2);
279 DEBUG(0,
280 ("DCE Identity Validation failed for principal %s: %s\n",
281 user, dce_errstr));
282 goto err;
285 sec_login_certify_identity(my_dce_sec_context, &err);
286 if (err != error_status_ok)
288 dce_error_inq_text(err, dce_errstr, &err2);
289 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
290 goto err;
293 if (auth_src != sec_login_auth_src_network)
295 DEBUG(0, ("DCE context has no network credentials.\n"));
298 sec_login_set_context(my_dce_sec_context, &err);
299 if (err != error_status_ok)
301 dce_error_inq_text(err, dce_errstr, &err2);
302 DEBUG(0,
303 ("DCE login failed for principal %s, cant set context: %s\n",
304 user, dce_errstr));
306 sec_login_purge_context(&my_dce_sec_context, &err);
307 goto err;
310 sec_login_get_pwent(my_dce_sec_context,
311 (sec_login_passwd_t *) & pw, &err);
312 if (err != error_status_ok)
314 dce_error_inq_text(err, dce_errstr, &err2);
315 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
316 goto err;
319 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
320 user, sys_getpid()));
322 DEBUG(3, ("DCE principal: %s\n"
323 " uid: %d\n"
324 " gid: %d\n",
325 pw->pw_name, pw->pw_uid, pw->pw_gid));
326 DEBUG(3, (" info: %s\n"
327 " dir: %s\n"
328 " shell: %s\n",
329 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
331 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
332 if (err != error_status_ok)
334 dce_error_inq_text(err, dce_errstr, &err2);
335 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
336 goto err;
339 set_effective_uid(0);
340 set_effective_gid(0);
342 DEBUG(0,
343 ("DCE context expires: %s", asctime(localtime(&expire_time))));
345 dcelogin_atmost_once = 1;
346 return (True);
348 err:
350 /* Go back to root, JRA. */
351 set_effective_uid(0);
352 set_effective_gid(egid);
353 return (False);
356 void dfs_unlogin(void)
358 error_status_t err;
359 int err2;
360 unsigned char dce_errstr[dce_c_error_string_len];
362 sec_login_purge_context(&my_dce_sec_context, &err);
363 if (err != error_status_ok)
365 dce_error_inq_text(err, dce_errstr, &err2);
366 DEBUG(0,
367 ("DCE purge login context failed for server instance %d: %s\n",
368 sys_getpid(), dce_errstr));
371 #endif
373 #ifdef KRB5_AUTH
375 #include <krb5.h>
377 /*******************************************************************
378 check on Kerberos authentication
379 ********************************************************************/
380 static BOOL krb5_auth(char *user, char *password)
382 krb5_data tgtname = {
384 KRB5_TGS_NAME_SIZE,
385 KRB5_TGS_NAME
387 krb5_context kcontext;
388 krb5_principal kprinc;
389 krb5_principal server;
390 krb5_creds kcreds;
391 int options = 0;
392 krb5_address **addrs = (krb5_address **) 0;
393 krb5_preauthtype *preauth = NULL;
394 krb5_keytab keytab = NULL;
395 krb5_timestamp now;
396 krb5_ccache ccache = NULL;
397 int retval;
398 char *name;
400 if (retval = krb5_init_context(&kcontext))
402 return (False);
405 if (retval = krb5_timeofday(kcontext, &now))
407 return (False);
410 if (retval = krb5_cc_default(kcontext, &ccache))
412 return (False);
415 if (retval = krb5_parse_name(kcontext, user, &kprinc))
417 return (False);
420 ZERO_STRUCT(kcreds);
422 kcreds.client = kprinc;
424 if ((retval = krb5_build_principal_ext(kcontext, &server,
425 krb5_princ_realm(kcontext,
426 kprinc)->
427 length,
428 krb5_princ_realm(kcontext,
429 kprinc)->data,
430 tgtname.length, tgtname.data,
431 krb5_princ_realm(kcontext,
432 kprinc)->
433 length,
434 krb5_princ_realm(kcontext,
435 kprinc)->data,
436 0)))
438 return (False);
441 kcreds.server = server;
443 retval = krb5_get_in_tkt_with_password(kcontext,
444 options,
445 addrs,
446 NULL,
447 preauth,
448 password, 0, &kcreds, 0);
450 if (retval)
452 return (False);
455 return (True);
457 #endif /* KRB5_AUTH */
459 #ifdef KRB4_AUTH
460 #include <krb.h>
462 /*******************************************************************
463 check on Kerberos authentication
464 ********************************************************************/
465 static BOOL krb4_auth(char *user, char *password)
467 char realm[REALM_SZ];
468 char tkfile[MAXPATHLEN];
470 if (krb_get_lrealm(realm, 1) != KSUCCESS)
472 (void)safe_strcpy(realm, KRB_REALM, sizeof(realm) - 1);
475 (void)slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
476 (int)sys_getpid());
478 krb_set_tkt_string(tkfile);
479 if (krb_verify_user(user, "", realm, password, 0, "rmcd") == KSUCCESS)
481 unlink(tkfile);
482 return 1;
484 unlink(tkfile);
485 return 0;
487 #endif /* KRB4_AUTH */
489 #ifdef LINUX_BIGCRYPT
490 /****************************************************************************
491 an enhanced crypt for Linux to handle password longer than 8 characters
492 ****************************************************************************/
493 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
495 #define LINUX_PASSWORD_SEG_CHARS 8
496 char salt[3];
497 int i;
499 StrnCpy(salt, salt1, 2);
500 crypted += 2;
502 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
503 char *p = crypt(password, salt) + 2;
504 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
505 return (0);
506 password += LINUX_PASSWORD_SEG_CHARS;
507 crypted += strlen(p);
510 return (1);
512 #endif
514 #ifdef OSF1_ENH_SEC
515 /****************************************************************************
516 an enhanced crypt for OSF1
517 ****************************************************************************/
518 static char *osf1_bigcrypt(char *password, char *salt1)
520 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
521 char *p1;
522 char *p2 = password;
523 char salt[3];
524 int i;
525 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
526 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
527 parts++;
529 StrnCpy(salt, salt1, 2);
530 StrnCpy(result, salt1, 2);
531 result[2] = '\0';
533 for (i = 0; i < parts; i++) {
534 p1 = crypt(p2, salt);
535 strncat(result, p1 + 2,
536 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
537 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
538 p2 += AUTH_CLEARTEXT_SEG_CHARS;
541 return (result);
543 #endif
546 /****************************************************************************
547 apply a function to upper/lower case combinations
548 of a string and return true if one of them returns true.
549 try all combinations with N uppercase letters.
550 offset is the first char to try and change (start with 0)
551 it assumes the string starts lowercased
552 ****************************************************************************/
553 static BOOL string_combinations2(char *s, int offset, BOOL (*fn) (char *),
554 int N)
556 int len = strlen(s);
557 int i;
559 #ifdef PASSWORD_LENGTH
560 len = MIN(len, PASSWORD_LENGTH);
561 #endif
563 if (N <= 0 || offset >= len)
564 return (fn(s));
566 for (i = offset; i < (len - (N - 1)); i++) {
567 char c = s[i];
568 if (!islower(c))
569 continue;
570 s[i] = toupper(c);
571 if (string_combinations2(s, i + 1, fn, N - 1))
572 return (True);
573 s[i] = c;
575 return (False);
578 /****************************************************************************
579 apply a function to upper/lower case combinations
580 of a string and return true if one of them returns true.
581 try all combinations with up to N uppercase letters.
582 offset is the first char to try and change (start with 0)
583 it assumes the string starts lowercased
584 ****************************************************************************/
585 static BOOL string_combinations(char *s, BOOL (*fn) (char *), int N)
587 int n;
588 for (n = 1; n <= N; n++)
589 if (string_combinations2(s, 0, fn, n))
590 return (True);
591 return (False);
595 /****************************************************************************
596 core of password checking routine
597 ****************************************************************************/
598 static BOOL password_check(char *password)
601 #ifdef WITH_PAM
602 return (smb_pam_passcheck(this_user, password) == NT_STATUS_NOPROBLEMO);
603 #endif /* WITH_PAM */
605 #ifdef WITH_AFS
606 if (afs_auth(this_user, password))
607 return (True);
608 #endif /* WITH_AFS */
610 #ifdef WITH_DFS
611 if (dfs_auth(this_user, password))
612 return (True);
613 #endif /* WITH_DFS */
615 #ifdef KRB5_AUTH
616 if (krb5_auth(this_user, password))
617 return (True);
618 #endif /* KRB5_AUTH */
620 #ifdef KRB4_AUTH
621 if (krb4_auth(this_user, password))
622 return (True);
623 #endif /* KRB4_AUTH */
625 #ifdef OSF1_ENH_SEC
627 BOOL ret =
628 (strcmp
629 (osf1_bigcrypt(password, this_salt),
630 this_crypted) == 0);
631 if (!ret) {
632 DEBUG(2,
633 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
634 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
636 return ret;
638 #endif /* OSF1_ENH_SEC */
640 #ifdef ULTRIX_AUTH
641 return (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
642 #endif /* ULTRIX_AUTH */
644 #ifdef LINUX_BIGCRYPT
645 return (linux_bigcrypt(password, this_salt, this_crypted));
646 #endif /* LINUX_BIGCRYPT */
648 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
651 * Some systems have bigcrypt in the C library but might not
652 * actually use it for the password hashes (HPUX 10.20) is
653 * a noteable example. So we try bigcrypt first, followed
654 * by crypt.
657 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
658 return True;
659 else
660 return (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
661 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
663 #ifdef HAVE_BIGCRYPT
664 return (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
665 #endif /* HAVE_BIGCRYPT */
667 #ifndef HAVE_CRYPT
668 DEBUG(1, ("Warning - no crypt available\n"));
669 return (False);
670 #else /* HAVE_CRYPT */
671 return (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
672 #endif /* HAVE_CRYPT */
673 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
678 /****************************************************************************
679 check if a username/password is OK
680 the function pointer fn() points to a function to call when a successful
681 match is found and is used to update the encrypted password file
682 return True on correct match, False otherwise
683 ****************************************************************************/
685 BOOL pass_check(char *user, char *password, int pwlen, struct passwd *pwd,
686 BOOL (*fn) (char *, char *))
688 pstring pass2;
689 int level = lp_passwordlevel();
690 struct passwd *pass = NULL;
692 if (password)
693 password[pwlen] = 0;
695 #if DEBUG_PASSWORD
696 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
697 #endif
699 if (!password)
700 return (False);
702 if (((!*password) || (!pwlen)) && !lp_null_passwords())
703 return (False);
705 if (pwd && !user) {
706 pass = (struct passwd *)pwd;
707 user = pass->pw_name;
708 } else {
709 pass = Get_Pwnam(user, True);
712 #ifdef WITH_PAM
715 * If we're using PAM we want to short-circuit all the
716 * checks below and dive straight into the PAM code.
719 fstrcpy(this_user, user);
721 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
723 #else /* Not using PAM */
725 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
727 if (!pass) {
728 DEBUG(3, ("Couldn't find user %s\n", user));
729 return (False);
732 #ifdef HAVE_GETSPNAM
734 struct spwd *spass;
736 /* many shadow systems require you to be root to get
737 the password, in most cases this should already be
738 the case when this function is called, except
739 perhaps for IPC password changing requests */
741 spass = getspnam(pass->pw_name);
742 if (spass && spass->sp_pwdp)
743 pstrcpy(pass->pw_passwd, spass->sp_pwdp);
745 #elif defined(IA_UINFO)
747 /* Need to get password with SVR4.2's ia_ functions
748 instead of get{sp,pw}ent functions. Required by
749 UnixWare 2.x, tested on version
750 2.1. (tangent@cyberport.com) */
751 uinfo_t uinfo;
752 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
753 ia_get_logpwd(uinfo, &(pass->pw_passwd));
755 #endif
757 #ifdef HAVE_GETPRPWNAM
759 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
760 if (pr_pw && pr_pw->ufld.fd_encrypt)
761 pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt);
763 #endif
765 #ifdef OSF1_ENH_SEC
767 struct pr_passwd *mypasswd;
768 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
769 user));
770 mypasswd = getprpwnam(user);
771 if (mypasswd) {
772 fstrcpy(pass->pw_name, mypasswd->ufld.fd_name);
773 fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt);
774 } else {
775 DEBUG(5,
776 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
777 user));
780 #endif
782 #ifdef ULTRIX_AUTH
784 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
785 if (ap) {
786 fstrcpy(pass->pw_passwd, ap->a_password);
787 endauthent();
790 #endif
792 /* extract relevant info */
793 fstrcpy(this_user, pass->pw_name);
794 fstrcpy(this_salt, pass->pw_passwd);
796 #if defined(HAVE_TRUNCATED_SALT)
797 /* crypt on some platforms (HPUX in particular)
798 won't work with more than 2 salt characters. */
799 this_salt[2] = 0;
800 #endif
802 fstrcpy(this_crypted, pass->pw_passwd);
804 if (!*this_crypted) {
805 if (!lp_null_passwords()) {
806 DEBUG(2, ("Disallowing %s with null password\n",
807 this_user));
808 return (False);
810 if (!*password) {
811 DEBUG(3,
812 ("Allowing access to %s with null password\n",
813 this_user));
814 return (True);
818 #endif /* WITH_PAM */
820 /* try it as it came to us */
821 if (password_check(password)) {
822 if (fn)
823 fn(user, password);
824 return (True);
827 /* if the password was given to us with mixed case then we don't
828 need to proceed as we know it hasn't been case modified by the
829 client */
830 if (strhasupper(password) && strhaslower(password)) {
831 return (False);
834 /* make a copy of it */
835 StrnCpy(pass2, password, sizeof(pstring) - 1);
837 /* try all lowercase if it's currently all uppercase */
838 if (strhasupper(password)) {
839 strlower(password);
840 if (password_check(password)) {
841 if (fn)
842 fn(user, password);
843 return (True);
847 /* give up? */
848 if (level < 1) {
849 /* restore it */
850 fstrcpy(password, pass2);
851 return (False);
854 /* last chance - all combinations of up to level chars upper! */
855 strlower(password);
857 if (string_combinations(password, password_check, level)) {
858 if (fn)
859 fn(user, password);
860 return (True);
863 /* restore it */
864 fstrcpy(password, pass2);
866 return (False);