DEBUG(0, -> DEBUG(10, Caught by Andrew Bartlett (thanks !).
[Samba.git] / source / auth / pass_check.c
blob68f0566aee74b1af10b585fa9d2847ff68dad9d3
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 /* these are kept here to keep the string_combinations function simple */
28 static fstring this_user;
29 static fstring this_salt;
30 static fstring this_crypted;
32 #ifdef WITH_AFS
34 #include <afs/stds.h>
35 #include <afs/kautils.h>
37 /*******************************************************************
38 check on AFS authentication
39 ********************************************************************/
40 static BOOL afs_auth(char *user, char *password)
42 long password_expires = 0;
43 char *reason;
45 /* For versions of AFS prior to 3.3, this routine has few arguments, */
46 /* but since I can't find the old documentation... :-) */
47 setpag();
48 if (ka_UserAuthenticateGeneral
49 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
50 (char *)0, /* cell */
51 password, 0, /* lifetime, default */
52 &password_expires, /*days 'til it expires */
53 0, /* spare 2 */
54 &reason) == 0)
56 return (True);
58 DEBUG(1,
59 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
60 return (False);
62 #endif
65 #ifdef WITH_DFS
67 #include <dce/dce_error.h>
68 #include <dce/sec_login.h>
70 /*****************************************************************
71 This new version of the DFS_AUTH code was donated by Karsten Muuss
72 <muuss@or.uni-bonn.de>. It fixes the following problems with the
73 old code :
75 - Server credentials may expire
76 - Client credential cache files have wrong owner
77 - purge_context() function is called with invalid argument
79 This new code was modified to ensure that on exit the uid/gid is
80 still root, and the original directory is restored. JRA.
81 ******************************************************************/
83 sec_login_handle_t my_dce_sec_context;
84 int dcelogin_atmost_once = 0;
86 /*******************************************************************
87 check on a DCE/DFS authentication
88 ********************************************************************/
89 static BOOL dfs_auth(char *user, char *password)
91 error_status_t err;
92 int err2;
93 int prterr;
94 signed32 expire_time, current_time;
95 boolean32 password_reset;
96 struct passwd *pw;
97 sec_passwd_rec_t passwd_rec;
98 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
99 unsigned char dce_errstr[dce_c_error_string_len];
100 gid_t egid;
102 if (dcelogin_atmost_once)
103 return (False);
105 #ifdef HAVE_CRYPT
107 * We only go for a DCE login context if the given password
108 * matches that stored in the local password file..
109 * Assumes local passwd file is kept in sync w/ DCE RGY!
112 if (strcmp((char *)crypt(password, this_salt), this_crypted))
114 return (False);
116 #endif
118 sec_login_get_current_context(&my_dce_sec_context, &err);
119 if (err != error_status_ok)
121 dce_error_inq_text(err, dce_errstr, &err2);
122 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
124 return (False);
127 sec_login_certify_identity(my_dce_sec_context, &err);
128 if (err != error_status_ok)
130 dce_error_inq_text(err, dce_errstr, &err2);
131 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
133 return (False);
136 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
137 if (err != error_status_ok)
139 dce_error_inq_text(err, dce_errstr, &err2);
140 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
142 return (False);
145 time(&current_time);
147 if (expire_time < (current_time + 60))
149 struct passwd *pw;
150 sec_passwd_rec_t *key;
152 sec_login_get_pwent(my_dce_sec_context,
153 (sec_login_passwd_t *) & pw, &err);
154 if (err != error_status_ok)
156 dce_error_inq_text(err, dce_errstr, &err2);
157 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
159 return (False);
162 sec_login_refresh_identity(my_dce_sec_context, &err);
163 if (err != error_status_ok)
165 dce_error_inq_text(err, dce_errstr, &err2);
166 DEBUG(0, ("DCE can't refresh identity. %s\n",
167 dce_errstr));
169 return (False);
172 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
173 (unsigned char *)pw->pw_name,
174 sec_c_key_version_none,
175 (void **)&key, &err);
176 if (err != error_status_ok)
178 dce_error_inq_text(err, dce_errstr, &err2);
179 DEBUG(0, ("DCE can't get key for %s. %s\n",
180 pw->pw_name, dce_errstr));
182 return (False);
185 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
186 &password_reset, &auth_src,
187 &err);
188 if (err != error_status_ok)
190 dce_error_inq_text(err, dce_errstr, &err2);
191 DEBUG(0,
192 ("DCE can't validate and certify identity for %s. %s\n",
193 pw->pw_name, dce_errstr));
196 sec_key_mgmt_free_key(key, &err);
197 if (err != error_status_ok)
199 dce_error_inq_text(err, dce_errstr, &err2);
200 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
204 if (sec_login_setup_identity((unsigned char *)user,
205 sec_login_no_flags,
206 &my_dce_sec_context, &err) == 0)
208 dce_error_inq_text(err, dce_errstr, &err2);
209 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
210 user, dce_errstr));
211 return (False);
214 sec_login_get_pwent(my_dce_sec_context,
215 (sec_login_passwd_t *) & pw, &err);
216 if (err != error_status_ok)
218 dce_error_inq_text(err, dce_errstr, &err2);
219 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
221 return (False);
224 sec_login_purge_context(&my_dce_sec_context, &err);
225 if (err != error_status_ok)
227 dce_error_inq_text(err, dce_errstr, &err2);
228 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
230 return (False);
234 * NB. I'd like to change these to call something like change_to_user()
235 * instead but currently we don't have a connection
236 * context to become the correct user. This is already
237 * fairly platform specific code however, so I think
238 * this should be ok. I have added code to go
239 * back to being root on error though. JRA.
242 egid = getegid();
244 set_effective_gid(pw->pw_gid);
245 set_effective_uid(pw->pw_uid);
247 if (sec_login_setup_identity((unsigned char *)user,
248 sec_login_no_flags,
249 &my_dce_sec_context, &err) == 0)
251 dce_error_inq_text(err, dce_errstr, &err2);
252 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
253 user, dce_errstr));
254 goto err;
257 sec_login_get_pwent(my_dce_sec_context,
258 (sec_login_passwd_t *) & pw, &err);
259 if (err != error_status_ok)
261 dce_error_inq_text(err, dce_errstr, &err2);
262 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
263 goto err;
266 passwd_rec.version_number = sec_passwd_c_version_none;
267 passwd_rec.pepper = NULL;
268 passwd_rec.key.key_type = sec_passwd_plain;
269 passwd_rec.key.tagged_union.plain = (idl_char *) password;
271 sec_login_validate_identity(my_dce_sec_context,
272 &passwd_rec, &password_reset,
273 &auth_src, &err);
274 if (err != error_status_ok)
276 dce_error_inq_text(err, dce_errstr, &err2);
277 DEBUG(0,
278 ("DCE Identity Validation failed for principal %s: %s\n",
279 user, dce_errstr));
280 goto err;
283 sec_login_certify_identity(my_dce_sec_context, &err);
284 if (err != error_status_ok)
286 dce_error_inq_text(err, dce_errstr, &err2);
287 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
288 goto err;
291 if (auth_src != sec_login_auth_src_network)
293 DEBUG(0, ("DCE context has no network credentials.\n"));
296 sec_login_set_context(my_dce_sec_context, &err);
297 if (err != error_status_ok)
299 dce_error_inq_text(err, dce_errstr, &err2);
300 DEBUG(0,
301 ("DCE login failed for principal %s, cant set context: %s\n",
302 user, dce_errstr));
304 sec_login_purge_context(&my_dce_sec_context, &err);
305 goto err;
308 sec_login_get_pwent(my_dce_sec_context,
309 (sec_login_passwd_t *) & pw, &err);
310 if (err != error_status_ok)
312 dce_error_inq_text(err, dce_errstr, &err2);
313 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
314 goto err;
317 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
318 user, sys_getpid()));
320 DEBUG(3, ("DCE principal: %s\n"
321 " uid: %d\n"
322 " gid: %d\n",
323 pw->pw_name, pw->pw_uid, pw->pw_gid));
324 DEBUG(3, (" info: %s\n"
325 " dir: %s\n"
326 " shell: %s\n",
327 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
329 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
330 if (err != error_status_ok)
332 dce_error_inq_text(err, dce_errstr, &err2);
333 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
334 goto err;
337 set_effective_uid(0);
338 set_effective_gid(0);
340 DEBUG(0,
341 ("DCE context expires: %s", asctime(localtime(&expire_time))));
343 dcelogin_atmost_once = 1;
344 return (True);
346 err:
348 /* Go back to root, JRA. */
349 set_effective_uid(0);
350 set_effective_gid(egid);
351 return (False);
354 void dfs_unlogin(void)
356 error_status_t err;
357 int err2;
358 unsigned char dce_errstr[dce_c_error_string_len];
360 sec_login_purge_context(&my_dce_sec_context, &err);
361 if (err != error_status_ok)
363 dce_error_inq_text(err, dce_errstr, &err2);
364 DEBUG(0,
365 ("DCE purge login context failed for server instance %d: %s\n",
366 sys_getpid(), dce_errstr));
369 #endif
371 #ifdef KRB5_AUTH
373 #include <krb5.h>
375 /*******************************************************************
376 check on Kerberos authentication
377 ********************************************************************/
378 static BOOL krb5_auth(char *user, char *password)
380 krb5_data tgtname = {
382 KRB5_TGS_NAME_SIZE,
383 KRB5_TGS_NAME
385 krb5_context kcontext;
386 krb5_principal kprinc;
387 krb5_principal server;
388 krb5_creds kcreds;
389 int options = 0;
390 krb5_address **addrs = (krb5_address **) 0;
391 krb5_preauthtype *preauth = NULL;
392 krb5_keytab keytab = NULL;
393 krb5_timestamp now;
394 krb5_ccache ccache = NULL;
395 int retval;
396 char *name;
398 if (retval = krb5_init_context(&kcontext))
400 return (False);
403 if (retval = krb5_timeofday(kcontext, &now))
405 return (False);
408 if (retval = krb5_cc_default(kcontext, &ccache))
410 return (False);
413 if (retval = krb5_parse_name(kcontext, user, &kprinc))
415 return (False);
418 ZERO_STRUCT(kcreds);
420 kcreds.client = kprinc;
422 if ((retval = krb5_build_principal_ext(kcontext, &server,
423 krb5_princ_realm(kcontext,
424 kprinc)->
425 length,
426 krb5_princ_realm(kcontext,
427 kprinc)->data,
428 tgtname.length, tgtname.data,
429 krb5_princ_realm(kcontext,
430 kprinc)->
431 length,
432 krb5_princ_realm(kcontext,
433 kprinc)->data,
434 0)))
436 return (False);
439 kcreds.server = server;
441 retval = krb5_get_in_tkt_with_password(kcontext,
442 options,
443 addrs,
444 NULL,
445 preauth,
446 password, 0, &kcreds, 0);
448 if (retval)
450 return (False);
453 return (True);
455 #endif /* KRB5_AUTH */
457 #ifdef KRB4_AUTH
458 #include <krb.h>
460 /*******************************************************************
461 check on Kerberos authentication
462 ********************************************************************/
463 static BOOL krb4_auth(char *user, char *password)
465 char realm[REALM_SZ];
466 char tkfile[MAXPATHLEN];
468 if (krb_get_lrealm(realm, 1) != KSUCCESS)
470 (void)safe_strcpy(realm, KRB_REALM, sizeof(realm) - 1);
473 (void)slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
474 (int)sys_getpid());
476 krb_set_tkt_string(tkfile);
477 if (krb_verify_user(user, "", realm, password, 0, "rmcd") == KSUCCESS)
479 unlink(tkfile);
480 return 1;
482 unlink(tkfile);
483 return 0;
485 #endif /* KRB4_AUTH */
487 #ifdef LINUX_BIGCRYPT
488 /****************************************************************************
489 an enhanced crypt for Linux to handle password longer than 8 characters
490 ****************************************************************************/
491 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
493 #define LINUX_PASSWORD_SEG_CHARS 8
494 char salt[3];
495 int i;
497 StrnCpy(salt, salt1, 2);
498 crypted += 2;
500 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
501 char *p = crypt(password, salt) + 2;
502 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
503 return (0);
504 password += LINUX_PASSWORD_SEG_CHARS;
505 crypted += strlen(p);
508 return (1);
510 #endif
512 #ifdef OSF1_ENH_SEC
513 /****************************************************************************
514 an enhanced crypt for OSF1
515 ****************************************************************************/
516 static char *osf1_bigcrypt(char *password, char *salt1)
518 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
519 char *p1;
520 char *p2 = password;
521 char salt[3];
522 int i;
523 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
524 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
525 parts++;
527 StrnCpy(salt, salt1, 2);
528 StrnCpy(result, salt1, 2);
529 result[2] = '\0';
531 for (i = 0; i < parts; i++) {
532 p1 = crypt(p2, salt);
533 strncat(result, p1 + 2,
534 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
535 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
536 p2 += AUTH_CLEARTEXT_SEG_CHARS;
539 return (result);
541 #endif
544 /****************************************************************************
545 apply a function to upper/lower case combinations
546 of a string and return true if one of them returns true.
547 try all combinations with N uppercase letters.
548 offset is the first char to try and change (start with 0)
549 it assumes the string starts lowercased
550 ****************************************************************************/
551 static BOOL string_combinations2(char *s, int offset, BOOL (*fn) (char *),
552 int N)
554 int len = strlen(s);
555 int i;
557 #ifdef PASSWORD_LENGTH
558 len = MIN(len, PASSWORD_LENGTH);
559 #endif
561 if (N <= 0 || offset >= len)
562 return (fn(s));
564 for (i = offset; i < (len - (N - 1)); i++) {
565 char c = s[i];
566 if (!islower(c))
567 continue;
568 s[i] = toupper(c);
569 if (string_combinations2(s, i + 1, fn, N - 1))
570 return (True);
571 s[i] = c;
573 return (False);
576 /****************************************************************************
577 apply a function to upper/lower case combinations
578 of a string and return true if one of them returns true.
579 try all combinations with up to N uppercase letters.
580 offset is the first char to try and change (start with 0)
581 it assumes the string starts lowercased
582 ****************************************************************************/
583 static BOOL string_combinations(char *s, BOOL (*fn) (char *), int N)
585 int n;
586 for (n = 1; n <= N; n++)
587 if (string_combinations2(s, 0, fn, n))
588 return (True);
589 return (False);
593 /****************************************************************************
594 core of password checking routine
595 ****************************************************************************/
596 static BOOL password_check(char *password)
599 #ifdef WITH_PAM
600 return (NT_STATUS_IS_OK(smb_pam_passcheck(this_user, password)));
601 #endif /* WITH_PAM */
603 #ifdef WITH_AFS
604 if (afs_auth(this_user, password))
605 return (True);
606 #endif /* WITH_AFS */
608 #ifdef WITH_DFS
609 if (dfs_auth(this_user, password))
610 return (True);
611 #endif /* WITH_DFS */
613 #ifdef KRB5_AUTH
614 if (krb5_auth(this_user, password))
615 return (True);
616 #endif /* KRB5_AUTH */
618 #ifdef KRB4_AUTH
619 if (krb4_auth(this_user, password))
620 return (True);
621 #endif /* KRB4_AUTH */
623 #ifdef OSF1_ENH_SEC
625 BOOL ret =
626 (strcmp
627 (osf1_bigcrypt(password, this_salt),
628 this_crypted) == 0);
629 if (!ret) {
630 DEBUG(2,
631 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
632 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
634 return ret;
636 #endif /* OSF1_ENH_SEC */
638 #ifdef ULTRIX_AUTH
639 return (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
640 #endif /* ULTRIX_AUTH */
642 #ifdef LINUX_BIGCRYPT
643 return (linux_bigcrypt(password, this_salt, this_crypted));
644 #endif /* LINUX_BIGCRYPT */
646 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
649 * Some systems have bigcrypt in the C library but might not
650 * actually use it for the password hashes (HPUX 10.20) is
651 * a noteable example. So we try bigcrypt first, followed
652 * by crypt.
655 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
656 return True;
657 else
658 return (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
659 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
661 #ifdef HAVE_BIGCRYPT
662 return (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
663 #endif /* HAVE_BIGCRYPT */
665 #ifndef HAVE_CRYPT
666 DEBUG(1, ("Warning - no crypt available\n"));
667 return (False);
668 #else /* HAVE_CRYPT */
669 return (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
670 #endif /* HAVE_CRYPT */
671 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
676 /****************************************************************************
677 check if a username/password is OK
678 the function pointer fn() points to a function to call when a successful
679 match is found and is used to update the encrypted password file
680 return True on correct match, False otherwise
681 ****************************************************************************/
683 BOOL pass_check(char *user, char *password, int pwlen, struct passwd *pwd,
684 BOOL (*fn) (char *, char *))
686 pstring pass2;
687 int level = lp_passwordlevel();
688 struct passwd *pass = NULL;
690 if (password)
691 password[pwlen] = 0;
693 #if DEBUG_PASSWORD
694 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
695 #endif
697 if (!password)
698 return (False);
700 if (((!*password) || (!pwlen)) && !lp_null_passwords())
701 return (False);
703 if (pwd && !user) {
704 pass = (struct passwd *)pwd;
705 user = pass->pw_name;
706 } else {
707 pass = Get_Pwnam(user, True);
710 #ifdef WITH_PAM
713 * If we're using PAM we want to short-circuit all the
714 * checks below and dive straight into the PAM code.
717 fstrcpy(this_user, user);
719 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
721 #else /* Not using PAM */
723 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
725 if (!pass) {
726 DEBUG(3, ("Couldn't find user %s\n", user));
727 return (False);
730 #ifdef HAVE_GETSPNAM
732 struct spwd *spass;
734 /* many shadow systems require you to be root to get
735 the password, in most cases this should already be
736 the case when this function is called, except
737 perhaps for IPC password changing requests */
739 spass = getspnam(pass->pw_name);
740 if (spass && spass->sp_pwdp)
741 pstrcpy(pass->pw_passwd, spass->sp_pwdp);
743 #elif defined(IA_UINFO)
745 /* Need to get password with SVR4.2's ia_ functions
746 instead of get{sp,pw}ent functions. Required by
747 UnixWare 2.x, tested on version
748 2.1. (tangent@cyberport.com) */
749 uinfo_t uinfo;
750 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
751 ia_get_logpwd(uinfo, &(pass->pw_passwd));
753 #endif
755 #ifdef HAVE_GETPRPWNAM
757 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
758 if (pr_pw && pr_pw->ufld.fd_encrypt)
759 pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt);
761 #endif
763 #ifdef OSF1_ENH_SEC
765 struct pr_passwd *mypasswd;
766 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
767 user));
768 mypasswd = getprpwnam(user);
769 if (mypasswd) {
770 fstrcpy(pass->pw_name, mypasswd->ufld.fd_name);
771 fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt);
772 } else {
773 DEBUG(5,
774 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
775 user));
778 #endif
780 #ifdef ULTRIX_AUTH
782 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
783 if (ap) {
784 fstrcpy(pass->pw_passwd, ap->a_password);
785 endauthent();
788 #endif
790 /* extract relevant info */
791 fstrcpy(this_user, pass->pw_name);
792 fstrcpy(this_salt, pass->pw_passwd);
794 #if defined(HAVE_TRUNCATED_SALT)
795 /* crypt on some platforms (HPUX in particular)
796 won't work with more than 2 salt characters. */
797 this_salt[2] = 0;
798 #endif
800 fstrcpy(this_crypted, pass->pw_passwd);
802 if (!*this_crypted) {
803 if (!lp_null_passwords()) {
804 DEBUG(2, ("Disallowing %s with null password\n",
805 this_user));
806 return (False);
808 if (!*password) {
809 DEBUG(3,
810 ("Allowing access to %s with null password\n",
811 this_user));
812 return (True);
816 #endif /* WITH_PAM */
818 /* try it as it came to us */
819 if (password_check(password)) {
820 if (fn)
821 fn(user, password);
822 return (True);
825 /* if the password was given to us with mixed case then we don't
826 need to proceed as we know it hasn't been case modified by the
827 client */
828 if (strhasupper(password) && strhaslower(password)) {
829 return (False);
832 /* make a copy of it */
833 StrnCpy(pass2, password, sizeof(pass2) - 1);
835 /* try all lowercase if it's currently all uppercase */
836 if (strhasupper(password)) {
837 strlower(password);
838 if (password_check(password)) {
839 if (fn)
840 fn(user, password);
841 return (True);
845 /* give up? */
846 if (level < 1) {
847 /* restore it */
848 fstrcpy(password, pass2);
849 return (False);
852 /* last chance - all combinations of up to level chars upper! */
853 strlower(password);
855 if (string_combinations(password, password_check, level)) {
856 if (fn)
857 fn(user, password);
858 return (True);
861 /* restore it */
862 fstrcpy(password, pass2);
864 return (False);