1/*
2   Unix SMB/CIFS implementation.
3   Password checking
4   Copyright (C) Andrew Tridgell 1992-1998
5
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.
10
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.
15
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.
19*/
20
21/* this module is for checking a username/password against a system
22   password database. The SMB encrypted password support is elsewhere */
23
24#include "includes.h"
25
26#undef DBGC_CLASS
27#define DBGC_CLASS DBGC_AUTH
28
29/* these are kept here to keep the string_combinations function simple */
30static fstring this_user;
31#if !defined(WITH_PAM)
32static fstring this_salt;
33static fstring this_crypted;
34#endif
35
36#ifdef WITH_AFS
37
38#include <afs/stds.h>
39#include <afs/kautils.h>
40
41/*******************************************************************
42check on AFS authentication
43********************************************************************/
44static BOOL afs_auth(char *user, char *password)
45{
46	long password_expires = 0;
47	char *reason;
48
49	/* For versions of AFS prior to 3.3, this routine has few arguments, */
50	/* but since I can't find the old documentation... :-)               */
51	setpag();
52	if (ka_UserAuthenticateGeneral
53	    (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0,	/* instance */
54	     (char *)0,		/* cell */
55	     password, 0,	/* lifetime, default */
56	     &password_expires,	/*days 'til it expires */
57	     0,			/* spare 2 */
58	     &reason) == 0)
59	{
60		return (True);
61	}
62	DEBUG(1,
63	      ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
64	return (False);
65}
66#endif
67
68
69#ifdef WITH_DFS
70
71#include <dce/dce_error.h>
72#include <dce/sec_login.h>
73
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
77 old code :
78
79  - Server credentials may expire
80  - Client credential cache files have wrong owner
81  - purge_context() function is called with invalid argument
82
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******************************************************************/
86
87sec_login_handle_t my_dce_sec_context;
88int dcelogin_atmost_once = 0;
89
90/*******************************************************************
91check on a DCE/DFS authentication
92********************************************************************/
93static BOOL dfs_auth(char *user, char *password)
94{
95	error_status_t err;
96	int err2;
97	int prterr;
98	signed32 expire_time, current_time;
99	boolean32 password_reset;
100	struct passwd *pw;
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];
104	gid_t egid;
105
106	if (dcelogin_atmost_once)
107		return (False);
108
109#ifdef HAVE_CRYPT
110	/*
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!
114	 */
115
116	if (strcmp((char *)crypt(password, this_salt), this_crypted))
117	{
118		return (False);
119	}
120#endif
121
122	sec_login_get_current_context(&my_dce_sec_context, &err);
123	if (err != error_status_ok)
124	{
125		dce_error_inq_text(err, dce_errstr, &err2);
126		DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
127
128		return (False);
129	}
130
131	sec_login_certify_identity(my_dce_sec_context, &err);
132	if (err != error_status_ok)
133	{
134		dce_error_inq_text(err, dce_errstr, &err2);
135		DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
136
137		return (False);
138	}
139
140	sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
141	if (err != error_status_ok)
142	{
143		dce_error_inq_text(err, dce_errstr, &err2);
144		DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
145
146		return (False);
147	}
148
149	time(&current_time);
150
151	if (expire_time < (current_time + 60))
152	{
153		struct passwd *pw;
154		sec_passwd_rec_t *key;
155
156		sec_login_get_pwent(my_dce_sec_context,
157				    (sec_login_passwd_t *) & pw, &err);
158		if (err != error_status_ok)
159		{
160			dce_error_inq_text(err, dce_errstr, &err2);
161			DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
162
163			return (False);
164		}
165
166		sec_login_refresh_identity(my_dce_sec_context, &err);
167		if (err != error_status_ok)
168		{
169			dce_error_inq_text(err, dce_errstr, &err2);
170			DEBUG(0, ("DCE can't refresh identity. %s\n",
171				  dce_errstr));
172
173			return (False);
174		}
175
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)
181		{
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));
185
186			return (False);
187		}
188
189		sec_login_valid_and_cert_ident(my_dce_sec_context, key,
190					       &password_reset, &auth_src,
191					       &err);
192		if (err != error_status_ok)
193		{
194			dce_error_inq_text(err, dce_errstr, &err2);
195			DEBUG(0,
196			      ("DCE can't validate and certify identity for %s. %s\n",
197			       pw->pw_name, dce_errstr));
198		}
199
200		sec_key_mgmt_free_key(key, &err);
201		if (err != error_status_ok)
202		{
203			dce_error_inq_text(err, dce_errstr, &err2);
204			DEBUG(0, ("DCE can't free key.\n", dce_errstr));
205		}
206	}
207
208	if (sec_login_setup_identity((unsigned char *)user,
209				     sec_login_no_flags,
210				     &my_dce_sec_context, &err) == 0)
211	{
212		dce_error_inq_text(err, dce_errstr, &err2);
213		DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
214			  user, dce_errstr));
215		return (False);
216	}
217
218	sec_login_get_pwent(my_dce_sec_context,
219			    (sec_login_passwd_t *) & pw, &err);
220	if (err != error_status_ok)
221	{
222		dce_error_inq_text(err, dce_errstr, &err2);
223		DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
224
225		return (False);
226	}
227
228	sec_login_purge_context(&my_dce_sec_context, &err);
229	if (err != error_status_ok)
230	{
231		dce_error_inq_text(err, dce_errstr, &err2);
232		DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
233
234		return (False);
235	}
236
237	/*
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.
244	 */
245
246	egid = getegid();
247
248	set_effective_gid(pw->pw_gid);
249	set_effective_uid(pw->pw_uid);
250
251	if (sec_login_setup_identity((unsigned char *)user,
252				     sec_login_no_flags,
253				     &my_dce_sec_context, &err) == 0)
254	{
255		dce_error_inq_text(err, dce_errstr, &err2);
256		DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
257			  user, dce_errstr));
258		goto err;
259	}
260
261	sec_login_get_pwent(my_dce_sec_context,
262			    (sec_login_passwd_t *) & pw, &err);
263	if (err != error_status_ok)
264	{
265		dce_error_inq_text(err, dce_errstr, &err2);
266		DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
267		goto err;
268	}
269
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;
274
275	sec_login_validate_identity(my_dce_sec_context,
276				    &passwd_rec, &password_reset,
277				    &auth_src, &err);
278	if (err != error_status_ok)
279	{
280		dce_error_inq_text(err, dce_errstr, &err2);
281		DEBUG(0,
282		      ("DCE Identity Validation failed for principal %s: %s\n",
283		       user, dce_errstr));
284		goto err;
285	}
286
287	sec_login_certify_identity(my_dce_sec_context, &err);
288	if (err != error_status_ok)
289	{
290		dce_error_inq_text(err, dce_errstr, &err2);
291		DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
292		goto err;
293	}
294
295	if (auth_src != sec_login_auth_src_network)
296	{
297		DEBUG(0, ("DCE context has no network credentials.\n"));
298	}
299
300	sec_login_set_context(my_dce_sec_context, &err);
301	if (err != error_status_ok)
302	{
303		dce_error_inq_text(err, dce_errstr, &err2);
304		DEBUG(0,
305		      ("DCE login failed for principal %s, cant set context: %s\n",
306		       user, dce_errstr));
307
308		sec_login_purge_context(&my_dce_sec_context, &err);
309		goto err;
310	}
311
312	sec_login_get_pwent(my_dce_sec_context,
313			    (sec_login_passwd_t *) & pw, &err);
314	if (err != error_status_ok)
315	{
316		dce_error_inq_text(err, dce_errstr, &err2);
317		DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
318		goto err;
319	}
320
321	DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
322		  user, sys_getpid()));
323
324	DEBUG(3, ("DCE principal: %s\n"
325		  "          uid: %d\n"
326		  "          gid: %d\n",
327		  pw->pw_name, pw->pw_uid, pw->pw_gid));
328	DEBUG(3, ("         info: %s\n"
329		  "          dir: %s\n"
330		  "        shell: %s\n",
331		  pw->pw_gecos, pw->pw_dir, pw->pw_shell));
332
333	sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
334	if (err != error_status_ok)
335	{
336		dce_error_inq_text(err, dce_errstr, &err2);
337		DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
338		goto err;
339	}
340
341	set_effective_uid(0);
342	set_effective_gid(0);
343
344	DEBUG(0,
345	      ("DCE context expires: %s", asctime(localtime(&expire_time))));
346
347	dcelogin_atmost_once = 1;
348	return (True);
349
350      err:
351
352	/* Go back to root, JRA. */
353	set_effective_uid(0);
354	set_effective_gid(egid);
355	return (False);
356}
357
358void dfs_unlogin(void)
359{
360	error_status_t err;
361	int err2;
362	unsigned char dce_errstr[dce_c_error_string_len];
363
364	sec_login_purge_context(&my_dce_sec_context, &err);
365	if (err != error_status_ok)
366	{
367		dce_error_inq_text(err, dce_errstr, &err2);
368		DEBUG(0,
369		      ("DCE purge login context failed for server instance %d: %s\n",
370		       sys_getpid(), dce_errstr));
371	}
372}
373#endif
374
375#ifdef LINUX_BIGCRYPT
376/****************************************************************************
377an enhanced crypt for Linux to handle password longer than 8 characters
378****************************************************************************/
379static int linux_bigcrypt(char *password, char *salt1, char *crypted)
380{
381#define LINUX_PASSWORD_SEG_CHARS 8
382	char salt[3];
383	int i;
384
385	StrnCpy(salt, salt1, 2);
386	crypted += 2;
387
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)
391			return (0);
392		password += LINUX_PASSWORD_SEG_CHARS;
393		crypted += strlen(p);
394	}
395
396	return (1);
397}
398#endif
399
400#ifdef OSF1_ENH_SEC
401/****************************************************************************
402an enhanced crypt for OSF1
403****************************************************************************/
404static char *osf1_bigcrypt(char *password, char *salt1)
405{
406	static char result[AUTH_MAX_PASSWD_LENGTH] = "";
407	char *p1;
408	char *p2 = password;
409	char salt[3];
410	int i;
411	int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
412	if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
413		parts++;
414
415	StrnCpy(salt, salt1, 2);
416	StrnCpy(result, salt1, 2);
417	result[2] = '\0';
418
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;
425	}
426
427	return (result);
428}
429#endif
430
431
432/****************************************************************************
433apply a function to upper/lower case combinations
434of a string and return true if one of them returns true.
435try all combinations with N uppercase letters.
436offset is the first char to try and change (start with 0)
437it assumes the string starts lowercased
438****************************************************************************/
439static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
440				 int N)
441{
442	int len = strlen(s);
443	int i;
444	NTSTATUS nt_status;
445
446#ifdef PASSWORD_LENGTH
447	len = MIN(len, PASSWORD_LENGTH);
448#endif
449
450	if (N <= 0 || offset >= len)
451		return (fn(s));
452
453	for (i = offset; i < (len - (N - 1)); i++) {
454		char c = s[i];
455		if (!islower(c))
456			continue;
457		s[i] = toupper(c);
458		if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
459			return (nt_status);
460		}
461		s[i] = c;
462	}
463	return (NT_STATUS_WRONG_PASSWORD);
464}
465
466/****************************************************************************
467apply a function to upper/lower case combinations
468of a string and return true if one of them returns true.
469try all combinations with up to N uppercase letters.
470offset is the first char to try and change (start with 0)
471it assumes the string starts lowercased
472****************************************************************************/
473static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
474{
475	int n;
476	NTSTATUS nt_status;
477	for (n = 1; n <= N; n++)
478		if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
479			return nt_status;
480	return NT_STATUS_WRONG_PASSWORD;
481}
482
483
484/****************************************************************************
485core of password checking routine
486****************************************************************************/
487static NTSTATUS password_check(const char *password)
488{
489#ifdef WITH_PAM
490	return smb_pam_passcheck(this_user, password);
491#else
492
493	BOOL ret;
494
495#ifdef WITH_AFS
496	if (afs_auth(this_user, password))
497		return NT_STATUS_OK;
498#endif /* WITH_AFS */
499
500#ifdef WITH_DFS
501	if (dfs_auth(this_user, password))
502		return NT_STATUS_OK;
503#endif /* WITH_DFS */
504
505#ifdef OSF1_ENH_SEC
506
507	ret = (strcmp(osf1_bigcrypt(password, this_salt),
508		      this_crypted) == 0);
509	if (!ret) {
510		DEBUG(2,
511		      ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
512		ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
513	}
514	if (ret) {
515		return NT_STATUS_OK;
516	} else {
517		return NT_STATUS_WRONG_PASSWORD;
518	}
519
520#endif /* OSF1_ENH_SEC */
521
522#ifdef ULTRIX_AUTH
523	ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
524	if (ret) {
525		return NT_STATUS_OK;
526        } else {
527		return NT_STATUS_WRONG_PASSWORD;
528	}
529
530#endif /* ULTRIX_AUTH */
531
532#ifdef LINUX_BIGCRYPT
533	ret = (linux_bigcrypt(password, this_salt, this_crypted));
534        if (ret) {
535		return NT_STATUS_OK;
536	} else {
537		return NT_STATUS_WRONG_PASSWORD;
538	}
539#endif /* LINUX_BIGCRYPT */
540
541#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
542
543	/*
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
547	 * by crypt.
548	 */
549
550	if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
551		return NT_STATUS_OK;
552	else
553		ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
554	if (ret) {
555		return NT_STATUS_OK;
556	} else {
557		return NT_STATUS_WRONG_PASSWORD;
558	}
559#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
560
561#ifdef HAVE_BIGCRYPT
562	ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
563        if (ret) {
564		return NT_STATUS_OK;
565	} else {
566		return NT_STATUS_WRONG_PASSWORD;
567	}
568#endif /* HAVE_BIGCRYPT */
569
570#ifndef HAVE_CRYPT
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);
575        if (ret) {
576		return NT_STATUS_OK;
577	} else {
578		return NT_STATUS_WRONG_PASSWORD;
579	}
580#endif /* HAVE_CRYPT */
581#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
582#endif /* WITH_PAM */
583}
584
585
586
587/****************************************************************************
588CHECK if a username/password is OK
589the function pointer fn() points to a function to call when a successful
590match is found and is used to update the encrypted password file
591return NT_STATUS_OK on correct match, appropriate error otherwise
592****************************************************************************/
593
594NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
595		    int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
596{
597	pstring pass2;
598	int level = lp_passwordlevel();
599
600	NTSTATUS nt_status;
601
602#ifdef DEBUG_PASSWORD
603	DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
604#endif
605
606	if (!password)
607		return NT_STATUS_LOGON_FAILURE;
608
609	if (((!*password) || (!pwlen)) && !lp_null_passwords())
610		return NT_STATUS_LOGON_FAILURE;
611
612#if defined(WITH_PAM)
613
614	/*
615	 * If we're using PAM we want to short-circuit all the
616	 * checks below and dive straight into the PAM code.
617	 */
618
619	fstrcpy(this_user, user);
620
621	DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
622
623#else /* Not using PAM */
624
625	DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
626
627	if (!pass) {
628		DEBUG(3, ("Couldn't find user %s\n", user));
629		return NT_STATUS_NO_SUCH_USER;
630	}
631
632
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);
638
639#ifdef HAVE_GETSPNAM
640	{
641		struct spwd *spass;
642
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 */
647
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);
652		}
653	}
654#elif defined(IA_UINFO)
655	{
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) */
660		uinfo_t uinfo;
661		if (ia_openinfo(pass->pw_name, &uinfo) != -1)
662			ia_get_logpwd(uinfo, &(pass->pw_passwd));
663	}
664#endif
665
666#ifdef HAVE_GETPRPWNAM
667	{
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);
671	}
672#endif
673
674#ifdef HAVE_GETPWANAM
675	{
676		struct passwd_adjunct *pwret;
677		pwret = getpwanam(s);
678		if (pwret && pwret->pwa_passwd)
679			fstrcpy(this_crypted, pwret->pwa_passwd);
680	}
681#endif
682
683#ifdef OSF1_ENH_SEC
684	{
685		struct pr_passwd *mypasswd;
686		DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
687			  user));
688		mypasswd = getprpwnam(user);
689		if (mypasswd) {
690			fstrcpy(this_user, mypasswd->ufld.fd_name);
691			fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
692		} else {
693			DEBUG(5,
694			      ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
695			       user));
696		}
697	}
698#endif
699
700#ifdef ULTRIX_AUTH
701	{
702		AUTHORIZATION *ap = getauthuid(pass->pw_uid);
703		if (ap) {
704			fstrcpy(this_crypted, ap->a_password);
705			endauthent();
706		}
707	}
708#endif
709
710#if defined(HAVE_TRUNCATED_SALT)
711	/* crypt on some platforms (HPUX in particular)
712	   won't work with more than 2 salt characters. */
713	this_salt[2] = 0;
714#endif
715
716	if (!*this_crypted) {
717		if (!lp_null_passwords()) {
718			DEBUG(2, ("Disallowing %s with null password\n",
719				  this_user));
720			return NT_STATUS_LOGON_FAILURE;
721		}
722		if (!*password) {
723			DEBUG(3,
724			      ("Allowing access to %s with null password\n",
725			       this_user));
726			return NT_STATUS_OK;
727		}
728	}
729
730#endif /* defined(WITH_PAM) */
731
732	/* try it as it came to us */
733	nt_status = password_check(password);
734        if NT_STATUS_IS_OK(nt_status) {
735                if (fn) {
736                        fn(user, password);
737		}
738		return (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). */
741                return (nt_status);
742        }
743
744	if (!run_cracker) {
745		return (nt_status);
746	}
747
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
750	 * client */
751	if (strhasupper(password) && strhaslower(password)) {
752		return nt_status;
753	}
754
755	/* make a copy of it */
756	pstrcpy(pass2, password);
757
758	/* try all lowercase if it's currently all uppercase */
759	if (strhasupper(pass2)) {
760		strlower_m(pass2);
761		if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
762		        if (fn)
763				fn(user, pass2);
764			return (nt_status);
765		}
766	}
767
768	/* give up? */
769	if (level < 1) {
770		return NT_STATUS_WRONG_PASSWORD;
771	}
772
773	/* last chance - all combinations of up to level chars upper! */
774	strlower_m(pass2);
775
776        if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
777                if (fn)
778			fn(user, pass2);
779		return nt_status;
780	}
781
782	return NT_STATUS_WRONG_PASSWORD;
783}
784