8d16acd27c5342a6cd3d8fe9cb0708b2cb1fde72
[samba.git] / source3 / auth / pass_check.c
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 3 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 */
30 static fstring this_user;
31 #if !defined(WITH_PAM) 
32 static fstring this_salt;
33 static fstring this_crypted;
34 #endif
35
36 #ifdef WITH_AFS
37
38 #include <afs/stds.h>
39 #include <afs/kautils.h>
40
41 /*******************************************************************
42 check on AFS authentication
43 ********************************************************************/
44 static 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
87 sec_login_handle_t my_dce_sec_context;
88 int dcelogin_atmost_once = 0;
89
90 /*******************************************************************
91 check on a DCE/DFS authentication
92 ********************************************************************/
93 static BOOL dfs_auth(char *user, char *password)
94 {
95         struct tm *t;
96         error_status_t err;
97         int err2;
98         int prterr;
99         signed32 expire_time, current_time;
100         boolean32 password_reset;
101         struct passwd *pw;
102         sec_passwd_rec_t passwd_rec;
103         sec_login_auth_src_t auth_src = sec_login_auth_src_network;
104         unsigned char dce_errstr[dce_c_error_string_len];
105         gid_t egid;
106
107         if (dcelogin_atmost_once)
108                 return (False);
109
110 #ifdef HAVE_CRYPT
111         /*
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!
115          */
116
117         if (strcmp((char *)crypt(password, this_salt), this_crypted))
118         {
119                 return (False);
120         }
121 #endif
122
123         sec_login_get_current_context(&my_dce_sec_context, &err);
124         if (err != error_status_ok)
125         {
126                 dce_error_inq_text(err, dce_errstr, &err2);
127                 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
128
129                 return (False);
130         }
131
132         sec_login_certify_identity(my_dce_sec_context, &err);
133         if (err != error_status_ok)
134         {
135                 dce_error_inq_text(err, dce_errstr, &err2);
136                 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
137
138                 return (False);
139         }
140
141         sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
142         if (err != error_status_ok)
143         {
144                 dce_error_inq_text(err, dce_errstr, &err2);
145                 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
146
147                 return (False);
148         }
149
150         time(&current_time);
151
152         if (expire_time < (current_time + 60))
153         {
154                 struct passwd *pw;
155                 sec_passwd_rec_t *key;
156
157                 sec_login_get_pwent(my_dce_sec_context,
158                                     (sec_login_passwd_t *) & pw, &err);
159                 if (err != error_status_ok)
160                 {
161                         dce_error_inq_text(err, dce_errstr, &err2);
162                         DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
163
164                         return (False);
165                 }
166
167                 sec_login_refresh_identity(my_dce_sec_context, &err);
168                 if (err != error_status_ok)
169                 {
170                         dce_error_inq_text(err, dce_errstr, &err2);
171                         DEBUG(0, ("DCE can't refresh identity. %s\n",
172                                   dce_errstr));
173
174                         return (False);
175                 }
176
177                 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
178                                      (unsigned char *)pw->pw_name,
179                                      sec_c_key_version_none,
180                                      (void **)&key, &err);
181                 if (err != error_status_ok)
182                 {
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));
186
187                         return (False);
188                 }
189
190                 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
191                                                &password_reset, &auth_src,
192                                                &err);
193                 if (err != error_status_ok)
194                 {
195                         dce_error_inq_text(err, dce_errstr, &err2);
196                         DEBUG(0,
197                               ("DCE can't validate and certify identity for %s. %s\n",
198                                pw->pw_name, dce_errstr));
199                 }
200
201                 sec_key_mgmt_free_key(key, &err);
202                 if (err != error_status_ok)
203                 {
204                         dce_error_inq_text(err, dce_errstr, &err2);
205                         DEBUG(0, ("DCE can't free key.\n", dce_errstr));
206                 }
207         }
208
209         if (sec_login_setup_identity((unsigned char *)user,
210                                      sec_login_no_flags,
211                                      &my_dce_sec_context, &err) == 0)
212         {
213                 dce_error_inq_text(err, dce_errstr, &err2);
214                 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
215                           user, dce_errstr));
216                 return (False);
217         }
218
219         sec_login_get_pwent(my_dce_sec_context,
220                             (sec_login_passwd_t *) & pw, &err);
221         if (err != error_status_ok)
222         {
223                 dce_error_inq_text(err, dce_errstr, &err2);
224                 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
225
226                 return (False);
227         }
228
229         sec_login_purge_context(&my_dce_sec_context, &err);
230         if (err != error_status_ok)
231         {
232                 dce_error_inq_text(err, dce_errstr, &err2);
233                 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
234
235                 return (False);
236         }
237
238         /*
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.
245          */
246
247         egid = getegid();
248
249         set_effective_gid(pw->pw_gid);
250         set_effective_uid(pw->pw_uid);
251
252         if (sec_login_setup_identity((unsigned char *)user,
253                                      sec_login_no_flags,
254                                      &my_dce_sec_context, &err) == 0)
255         {
256                 dce_error_inq_text(err, dce_errstr, &err2);
257                 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
258                           user, dce_errstr));
259                 goto err;
260         }
261
262         sec_login_get_pwent(my_dce_sec_context,
263                             (sec_login_passwd_t *) & pw, &err);
264         if (err != error_status_ok)
265         {
266                 dce_error_inq_text(err, dce_errstr, &err2);
267                 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
268                 goto err;
269         }
270
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;
275
276         sec_login_validate_identity(my_dce_sec_context,
277                                     &passwd_rec, &password_reset,
278                                     &auth_src, &err);
279         if (err != error_status_ok)
280         {
281                 dce_error_inq_text(err, dce_errstr, &err2);
282                 DEBUG(0,
283                       ("DCE Identity Validation failed for principal %s: %s\n",
284                        user, dce_errstr));
285                 goto err;
286         }
287
288         sec_login_certify_identity(my_dce_sec_context, &err);
289         if (err != error_status_ok)
290         {
291                 dce_error_inq_text(err, dce_errstr, &err2);
292                 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
293                 goto err;
294         }
295
296         if (auth_src != sec_login_auth_src_network)
297         {
298                 DEBUG(0, ("DCE context has no network credentials.\n"));
299         }
300
301         sec_login_set_context(my_dce_sec_context, &err);
302         if (err != error_status_ok)
303         {
304                 dce_error_inq_text(err, dce_errstr, &err2);
305                 DEBUG(0,
306                       ("DCE login failed for principal %s, cant set context: %s\n",
307                        user, dce_errstr));
308
309                 sec_login_purge_context(&my_dce_sec_context, &err);
310                 goto err;
311         }
312
313         sec_login_get_pwent(my_dce_sec_context,
314                             (sec_login_passwd_t *) & pw, &err);
315         if (err != error_status_ok)
316         {
317                 dce_error_inq_text(err, dce_errstr, &err2);
318                 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
319                 goto err;
320         }
321
322         DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
323                   user, sys_getpid()));
324
325         DEBUG(3, ("DCE principal: %s\n"
326                   "          uid: %d\n"
327                   "          gid: %d\n",
328                   pw->pw_name, pw->pw_uid, pw->pw_gid));
329         DEBUG(3, ("         info: %s\n"
330                   "          dir: %s\n"
331                   "        shell: %s\n",
332                   pw->pw_gecos, pw->pw_dir, pw->pw_shell));
333
334         sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
335         if (err != error_status_ok)
336         {
337                 dce_error_inq_text(err, dce_errstr, &err2);
338                 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
339                 goto err;
340         }
341
342         set_effective_uid(0);
343         set_effective_gid(0);
344
345         t = localtime(&expire_time);
346         if (t) {
347                 const char *asct = asctime(t);
348                 if (asct) {
349                         DEBUG(0,("DCE context expires: %s", asct));
350                 }
351         }
352
353         dcelogin_atmost_once = 1;
354         return (True);
355
356       err:
357
358         /* Go back to root, JRA. */
359         set_effective_uid(0);
360         set_effective_gid(egid);
361         return (False);
362 }
363
364 void dfs_unlogin(void)
365 {
366         error_status_t err;
367         int err2;
368         unsigned char dce_errstr[dce_c_error_string_len];
369
370         sec_login_purge_context(&my_dce_sec_context, &err);
371         if (err != error_status_ok)
372         {
373                 dce_error_inq_text(err, dce_errstr, &err2);
374                 DEBUG(0,
375                       ("DCE purge login context failed for server instance %d: %s\n",
376                        sys_getpid(), dce_errstr));
377         }
378 }
379 #endif
380
381 #ifdef LINUX_BIGCRYPT
382 /****************************************************************************
383 an enhanced crypt for Linux to handle password longer than 8 characters
384 ****************************************************************************/
385 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
386 {
387 #define LINUX_PASSWORD_SEG_CHARS 8
388         char salt[3];
389         int i;
390
391         StrnCpy(salt, salt1, 2);
392         crypted += 2;
393
394         for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
395                 char *p = crypt(password, salt) + 2;
396                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
397                         return (0);
398                 password += LINUX_PASSWORD_SEG_CHARS;
399                 crypted += strlen(p);
400         }
401
402         return (1);
403 }
404 #endif
405
406 #ifdef OSF1_ENH_SEC
407 /****************************************************************************
408 an enhanced crypt for OSF1
409 ****************************************************************************/
410 static char *osf1_bigcrypt(char *password, char *salt1)
411 {
412         static char result[AUTH_MAX_PASSWD_LENGTH] = "";
413         char *p1;
414         char *p2 = password;
415         char salt[3];
416         int i;
417         int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
418         if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
419                 parts++;
420
421         StrnCpy(salt, salt1, 2);
422         StrnCpy(result, salt1, 2);
423         result[2] = '\0';
424
425         for (i = 0; i < parts; i++) {
426                 p1 = crypt(p2, salt);
427                 strncat(result, p1 + 2,
428                         AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
429                 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
430                 p2 += AUTH_CLEARTEXT_SEG_CHARS;
431         }
432
433         return (result);
434 }
435 #endif
436
437
438 /****************************************************************************
439 apply a function to upper/lower case combinations
440 of a string and return true if one of them returns true.
441 try all combinations with N uppercase letters.
442 offset is the first char to try and change (start with 0)
443 it assumes the string starts lowercased
444 ****************************************************************************/
445 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
446                                  int N)
447 {
448         int len = strlen(s);
449         int i;
450         NTSTATUS nt_status;
451
452 #ifdef PASSWORD_LENGTH
453         len = MIN(len, PASSWORD_LENGTH);
454 #endif
455
456         if (N <= 0 || offset >= len)
457                 return (fn(s));
458
459         for (i = offset; i < (len - (N - 1)); i++) {
460                 char c = s[i];
461                 if (!islower_ascii(c))
462                         continue;
463                 s[i] = toupper_ascii(c);
464                 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
465                         return (nt_status);
466                 }
467                 s[i] = c;
468         }
469         return (NT_STATUS_WRONG_PASSWORD);
470 }
471
472 /****************************************************************************
473 apply a function to upper/lower case combinations
474 of a string and return true if one of them returns true.
475 try all combinations with up to N uppercase letters.
476 offset is the first char to try and change (start with 0)
477 it assumes the string starts lowercased
478 ****************************************************************************/
479 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
480 {
481         int n;
482         NTSTATUS nt_status;
483         for (n = 1; n <= N; n++)
484                 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
485                         return nt_status;
486         return NT_STATUS_WRONG_PASSWORD;
487 }
488
489
490 /****************************************************************************
491 core of password checking routine
492 ****************************************************************************/
493 static NTSTATUS password_check(const char *password)
494 {
495 #ifdef WITH_PAM
496         return smb_pam_passcheck(this_user, password);
497 #else
498
499         BOOL ret;
500
501 #ifdef WITH_AFS
502         if (afs_auth(this_user, password))
503                 return NT_STATUS_OK;
504 #endif /* WITH_AFS */
505
506 #ifdef WITH_DFS
507         if (dfs_auth(this_user, password))
508                 return NT_STATUS_OK;
509 #endif /* WITH_DFS */
510
511 #ifdef OSF1_ENH_SEC
512         
513         ret = (strcmp(osf1_bigcrypt(password, this_salt),
514                       this_crypted) == 0);
515         if (!ret) {
516                 DEBUG(2,
517                       ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
518                 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
519         }
520         if (ret) {
521                 return NT_STATUS_OK;
522         } else {
523                 return NT_STATUS_WRONG_PASSWORD;
524         }
525         
526 #endif /* OSF1_ENH_SEC */
527         
528 #ifdef ULTRIX_AUTH
529         ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
530         if (ret) {
531                 return NT_STATUS_OK;
532         } else {
533                 return NT_STATUS_WRONG_PASSWORD;
534         }
535         
536 #endif /* ULTRIX_AUTH */
537         
538 #ifdef LINUX_BIGCRYPT
539         ret = (linux_bigcrypt(password, this_salt, this_crypted));
540         if (ret) {
541                 return NT_STATUS_OK;
542         } else {
543                 return NT_STATUS_WRONG_PASSWORD;
544         }
545 #endif /* LINUX_BIGCRYPT */
546         
547 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
548         
549         /*
550          * Some systems have bigcrypt in the C library but might not
551          * actually use it for the password hashes (HPUX 10.20) is
552          * a noteable example. So we try bigcrypt first, followed
553          * by crypt.
554          */
555
556         if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
557                 return NT_STATUS_OK;
558         else
559                 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
560         if (ret) {
561                 return NT_STATUS_OK;
562         } else {
563                 return NT_STATUS_WRONG_PASSWORD;
564         }
565 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
566         
567 #ifdef HAVE_BIGCRYPT
568         ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
569         if (ret) {
570                 return NT_STATUS_OK;
571         } else {
572                 return NT_STATUS_WRONG_PASSWORD;
573         }
574 #endif /* HAVE_BIGCRYPT */
575         
576 #ifndef HAVE_CRYPT
577         DEBUG(1, ("Warning - no crypt available\n"));
578         return NT_STATUS_LOGON_FAILURE;
579 #else /* HAVE_CRYPT */
580         ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
581         if (ret) {
582                 return NT_STATUS_OK;
583         } else {
584                 return NT_STATUS_WRONG_PASSWORD;
585         }
586 #endif /* HAVE_CRYPT */
587 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
588 #endif /* WITH_PAM */
589 }
590
591
592
593 /****************************************************************************
594 CHECK if a username/password is OK
595 the function pointer fn() points to a function to call when a successful
596 match is found and is used to update the encrypted password file 
597 return NT_STATUS_OK on correct match, appropriate error otherwise
598 ****************************************************************************/
599
600 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password, 
601                     int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
602 {
603         pstring pass2;
604         int level = lp_passwordlevel();
605
606         NTSTATUS nt_status;
607
608 #ifdef DEBUG_PASSWORD
609         DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
610 #endif
611
612         if (!password)
613                 return NT_STATUS_LOGON_FAILURE;
614
615         if (((!*password) || (!pwlen)) && !lp_null_passwords())
616                 return NT_STATUS_LOGON_FAILURE;
617
618 #if defined(WITH_PAM) 
619
620         /*
621          * If we're using PAM we want to short-circuit all the 
622          * checks below and dive straight into the PAM code.
623          */
624
625         fstrcpy(this_user, user);
626
627         DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
628
629 #else /* Not using PAM */
630
631         DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
632
633         if (!pass) {
634                 DEBUG(3, ("Couldn't find user %s\n", user));
635                 return NT_STATUS_NO_SUCH_USER;
636         }
637
638
639         /* Copy into global for the convenience of looping code */
640         /* Also the place to keep the 'password' no matter what
641            crazy struct it started in... */
642         fstrcpy(this_crypted, pass->pw_passwd);
643         fstrcpy(this_salt, pass->pw_passwd);
644
645 #ifdef HAVE_GETSPNAM
646         {
647                 struct spwd *spass;
648
649                 /* many shadow systems require you to be root to get
650                    the password, in most cases this should already be
651                    the case when this function is called, except
652                    perhaps for IPC password changing requests */
653
654                 spass = getspnam(pass->pw_name);
655                 if (spass && spass->sp_pwdp) {
656                         fstrcpy(this_crypted, spass->sp_pwdp);
657                         fstrcpy(this_salt, spass->sp_pwdp);
658                 }
659         }
660 #elif defined(IA_UINFO)
661         {
662                 /* Need to get password with SVR4.2's ia_ functions
663                    instead of get{sp,pw}ent functions. Required by
664                    UnixWare 2.x, tested on version
665                    2.1. (tangent@cyberport.com) */
666                 uinfo_t uinfo;
667                 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
668                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
669         }
670 #endif
671
672 #ifdef HAVE_GETPRPWNAM
673         {
674                 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
675                 if (pr_pw && pr_pw->ufld.fd_encrypt)
676                         fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
677         }
678 #endif
679
680 #ifdef HAVE_GETPWANAM
681         {
682                 struct passwd_adjunct *pwret;
683                 pwret = getpwanam(s);
684                 if (pwret && pwret->pwa_passwd)
685                         fstrcpy(this_crypted, pwret->pwa_passwd);
686         }
687 #endif
688
689 #ifdef OSF1_ENH_SEC
690         {
691                 struct pr_passwd *mypasswd;
692                 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
693                           user));
694                 mypasswd = getprpwnam(user);
695                 if (mypasswd) {
696                         fstrcpy(this_user, mypasswd->ufld.fd_name);
697                         fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
698                 } else {
699                         DEBUG(5,
700                               ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
701                                user));
702                 }
703         }
704 #endif
705
706 #ifdef ULTRIX_AUTH
707         {
708                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
709                 if (ap) {
710                         fstrcpy(this_crypted, ap->a_password);
711                         endauthent();
712                 }
713         }
714 #endif
715
716 #if defined(HAVE_TRUNCATED_SALT)
717         /* crypt on some platforms (HPUX in particular)
718            won't work with more than 2 salt characters. */
719         this_salt[2] = 0;
720 #endif
721
722         if (!*this_crypted) {
723                 if (!lp_null_passwords()) {
724                         DEBUG(2, ("Disallowing %s with null password\n",
725                                   this_user));
726                         return NT_STATUS_LOGON_FAILURE;
727                 }
728                 if (!*password) {
729                         DEBUG(3,
730                               ("Allowing access to %s with null password\n",
731                                this_user));
732                         return NT_STATUS_OK;
733                 }
734         }
735
736 #endif /* defined(WITH_PAM) */
737
738         /* try it as it came to us */
739         nt_status = password_check(password);
740         if NT_STATUS_IS_OK(nt_status) {
741                 if (fn) {
742                         fn(user, password);
743                 }
744                 return (nt_status);
745         } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
746                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
747                 return (nt_status);
748         }
749
750         if (!run_cracker) {
751                 return (nt_status);
752         }
753
754         /* if the password was given to us with mixed case then we don't
755          * need to proceed as we know it hasn't been case modified by the
756          * client */
757         if (strhasupper(password) && strhaslower(password)) {
758                 return nt_status;
759         }
760
761         /* make a copy of it */
762         pstrcpy(pass2, password);
763
764         /* try all lowercase if it's currently all uppercase */
765         if (strhasupper(pass2)) {
766                 strlower_m(pass2);
767                 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
768                         if (fn)
769                                 fn(user, pass2);
770                         return (nt_status);
771                 }
772         }
773
774         /* give up? */
775         if (level < 1) {
776                 return NT_STATUS_WRONG_PASSWORD;
777         }
778
779         /* last chance - all combinations of up to level chars upper! */
780         strlower_m(pass2);
781  
782         if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
783                 if (fn)
784                         fn(user, pass2);
785                 return nt_status;
786         }
787         
788         return NT_STATUS_WRONG_PASSWORD;
789 }