r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[abartlet/samba.git/.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, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* this module is for checking a username/password against a system
21    password database. The SMB encrypted password support is elsewhere */
22
23 #include "includes.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_AUTH
27
28 /* these are kept here to keep the string_combinations function simple */
29 static fstring this_user;
30 #if !defined(WITH_PAM) 
31 static fstring this_salt;
32 static fstring this_crypted;
33 #endif
34
35 #ifdef WITH_AFS
36
37 #include <afs/stds.h>
38 #include <afs/kautils.h>
39
40 /*******************************************************************
41 check on AFS authentication
42 ********************************************************************/
43 static BOOL afs_auth(char *user, char *password)
44 {
45         long password_expires = 0;
46         char *reason;
47
48         /* For versions of AFS prior to 3.3, this routine has few arguments, */
49         /* but since I can't find the old documentation... :-)               */
50         setpag();
51         if (ka_UserAuthenticateGeneral
52             (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0,       /* instance */
53              (char *)0,         /* cell */
54              password, 0,       /* lifetime, default */
55              &password_expires, /*days 'til it expires */
56              0,                 /* spare 2 */
57              &reason) == 0)
58         {
59                 return (True);
60         }
61         DEBUG(1,
62               ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
63         return (False);
64 }
65 #endif
66
67
68 #ifdef WITH_DFS
69
70 #include <dce/dce_error.h>
71 #include <dce/sec_login.h>
72
73 /*****************************************************************
74  This new version of the DFS_AUTH code was donated by Karsten Muuss
75  <muuss@or.uni-bonn.de>. It fixes the following problems with the
76  old code :
77
78   - Server credentials may expire
79   - Client credential cache files have wrong owner
80   - purge_context() function is called with invalid argument
81
82  This new code was modified to ensure that on exit the uid/gid is
83  still root, and the original directory is restored. JRA.
84 ******************************************************************/
85
86 sec_login_handle_t my_dce_sec_context;
87 int dcelogin_atmost_once = 0;
88
89 /*******************************************************************
90 check on a DCE/DFS authentication
91 ********************************************************************/
92 static BOOL dfs_auth(char *user, char *password)
93 {
94         struct tm *t;
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         t = localtime(&expire_time);
345         if (t) {
346                 const char *asct = asctime(t);
347                 if (asct) {
348                         DEBUG(0,("DCE context expires: %s", asct));
349                 }
350         }
351
352         dcelogin_atmost_once = 1;
353         return (True);
354
355       err:
356
357         /* Go back to root, JRA. */
358         set_effective_uid(0);
359         set_effective_gid(egid);
360         return (False);
361 }
362
363 void dfs_unlogin(void)
364 {
365         error_status_t err;
366         int err2;
367         unsigned char dce_errstr[dce_c_error_string_len];
368
369         sec_login_purge_context(&my_dce_sec_context, &err);
370         if (err != error_status_ok)
371         {
372                 dce_error_inq_text(err, dce_errstr, &err2);
373                 DEBUG(0,
374                       ("DCE purge login context failed for server instance %d: %s\n",
375                        sys_getpid(), dce_errstr));
376         }
377 }
378 #endif
379
380 #ifdef LINUX_BIGCRYPT
381 /****************************************************************************
382 an enhanced crypt for Linux to handle password longer than 8 characters
383 ****************************************************************************/
384 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
385 {
386 #define LINUX_PASSWORD_SEG_CHARS 8
387         char salt[3];
388         int i;
389
390         StrnCpy(salt, salt1, 2);
391         crypted += 2;
392
393         for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
394                 char *p = crypt(password, salt) + 2;
395                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
396                         return (0);
397                 password += LINUX_PASSWORD_SEG_CHARS;
398                 crypted += strlen(p);
399         }
400
401         return (1);
402 }
403 #endif
404
405 #ifdef OSF1_ENH_SEC
406 /****************************************************************************
407 an enhanced crypt for OSF1
408 ****************************************************************************/
409 static char *osf1_bigcrypt(char *password, char *salt1)
410 {
411         static char result[AUTH_MAX_PASSWD_LENGTH] = "";
412         char *p1;
413         char *p2 = password;
414         char salt[3];
415         int i;
416         int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
417         if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
418                 parts++;
419
420         StrnCpy(salt, salt1, 2);
421         StrnCpy(result, salt1, 2);
422         result[2] = '\0';
423
424         for (i = 0; i < parts; i++) {
425                 p1 = crypt(p2, salt);
426                 strncat(result, p1 + 2,
427                         AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
428                 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
429                 p2 += AUTH_CLEARTEXT_SEG_CHARS;
430         }
431
432         return (result);
433 }
434 #endif
435
436
437 /****************************************************************************
438 apply a function to upper/lower case combinations
439 of a string and return true if one of them returns true.
440 try all combinations with N uppercase letters.
441 offset is the first char to try and change (start with 0)
442 it assumes the string starts lowercased
443 ****************************************************************************/
444 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
445                                  int N)
446 {
447         int len = strlen(s);
448         int i;
449         NTSTATUS nt_status;
450
451 #ifdef PASSWORD_LENGTH
452         len = MIN(len, PASSWORD_LENGTH);
453 #endif
454
455         if (N <= 0 || offset >= len)
456                 return (fn(s));
457
458         for (i = offset; i < (len - (N - 1)); i++) {
459                 char c = s[i];
460                 if (!islower_ascii(c))
461                         continue;
462                 s[i] = toupper_ascii(c);
463                 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
464                         return (nt_status);
465                 }
466                 s[i] = c;
467         }
468         return (NT_STATUS_WRONG_PASSWORD);
469 }
470
471 /****************************************************************************
472 apply a function to upper/lower case combinations
473 of a string and return true if one of them returns true.
474 try all combinations with up to N uppercase letters.
475 offset is the first char to try and change (start with 0)
476 it assumes the string starts lowercased
477 ****************************************************************************/
478 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
479 {
480         int n;
481         NTSTATUS nt_status;
482         for (n = 1; n <= N; n++)
483                 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
484                         return nt_status;
485         return NT_STATUS_WRONG_PASSWORD;
486 }
487
488
489 /****************************************************************************
490 core of password checking routine
491 ****************************************************************************/
492 static NTSTATUS password_check(const char *password)
493 {
494 #ifdef WITH_PAM
495         return smb_pam_passcheck(this_user, password);
496 #else
497
498         BOOL ret;
499
500 #ifdef WITH_AFS
501         if (afs_auth(this_user, password))
502                 return NT_STATUS_OK;
503 #endif /* WITH_AFS */
504
505 #ifdef WITH_DFS
506         if (dfs_auth(this_user, password))
507                 return NT_STATUS_OK;
508 #endif /* WITH_DFS */
509
510 #ifdef OSF1_ENH_SEC
511         
512         ret = (strcmp(osf1_bigcrypt(password, this_salt),
513                       this_crypted) == 0);
514         if (!ret) {
515                 DEBUG(2,
516                       ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
517                 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
518         }
519         if (ret) {
520                 return NT_STATUS_OK;
521         } else {
522                 return NT_STATUS_WRONG_PASSWORD;
523         }
524         
525 #endif /* OSF1_ENH_SEC */
526         
527 #ifdef ULTRIX_AUTH
528         ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
529         if (ret) {
530                 return NT_STATUS_OK;
531         } else {
532                 return NT_STATUS_WRONG_PASSWORD;
533         }
534         
535 #endif /* ULTRIX_AUTH */
536         
537 #ifdef LINUX_BIGCRYPT
538         ret = (linux_bigcrypt(password, this_salt, this_crypted));
539         if (ret) {
540                 return NT_STATUS_OK;
541         } else {
542                 return NT_STATUS_WRONG_PASSWORD;
543         }
544 #endif /* LINUX_BIGCRYPT */
545         
546 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
547         
548         /*
549          * Some systems have bigcrypt in the C library but might not
550          * actually use it for the password hashes (HPUX 10.20) is
551          * a noteable example. So we try bigcrypt first, followed
552          * by crypt.
553          */
554
555         if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
556                 return NT_STATUS_OK;
557         else
558                 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
559         if (ret) {
560                 return NT_STATUS_OK;
561         } else {
562                 return NT_STATUS_WRONG_PASSWORD;
563         }
564 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
565         
566 #ifdef HAVE_BIGCRYPT
567         ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
568         if (ret) {
569                 return NT_STATUS_OK;
570         } else {
571                 return NT_STATUS_WRONG_PASSWORD;
572         }
573 #endif /* HAVE_BIGCRYPT */
574         
575 #ifndef HAVE_CRYPT
576         DEBUG(1, ("Warning - no crypt available\n"));
577         return NT_STATUS_LOGON_FAILURE;
578 #else /* HAVE_CRYPT */
579         ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
580         if (ret) {
581                 return NT_STATUS_OK;
582         } else {
583                 return NT_STATUS_WRONG_PASSWORD;
584         }
585 #endif /* HAVE_CRYPT */
586 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
587 #endif /* WITH_PAM */
588 }
589
590
591
592 /****************************************************************************
593 CHECK if a username/password is OK
594 the function pointer fn() points to a function to call when a successful
595 match is found and is used to update the encrypted password file 
596 return NT_STATUS_OK on correct match, appropriate error otherwise
597 ****************************************************************************/
598
599 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password, 
600                     int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
601 {
602         pstring pass2;
603         int level = lp_passwordlevel();
604
605         NTSTATUS nt_status;
606
607 #ifdef DEBUG_PASSWORD
608         DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
609 #endif
610
611         if (!password)
612                 return NT_STATUS_LOGON_FAILURE;
613
614         if (((!*password) || (!pwlen)) && !lp_null_passwords())
615                 return NT_STATUS_LOGON_FAILURE;
616
617 #if defined(WITH_PAM) 
618
619         /*
620          * If we're using PAM we want to short-circuit all the 
621          * checks below and dive straight into the PAM code.
622          */
623
624         fstrcpy(this_user, user);
625
626         DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
627
628 #else /* Not using PAM */
629
630         DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
631
632         if (!pass) {
633                 DEBUG(3, ("Couldn't find user %s\n", user));
634                 return NT_STATUS_NO_SUCH_USER;
635         }
636
637
638         /* Copy into global for the convenience of looping code */
639         /* Also the place to keep the 'password' no matter what
640            crazy struct it started in... */
641         fstrcpy(this_crypted, pass->pw_passwd);
642         fstrcpy(this_salt, pass->pw_passwd);
643
644 #ifdef HAVE_GETSPNAM
645         {
646                 struct spwd *spass;
647
648                 /* many shadow systems require you to be root to get
649                    the password, in most cases this should already be
650                    the case when this function is called, except
651                    perhaps for IPC password changing requests */
652
653                 spass = getspnam(pass->pw_name);
654                 if (spass && spass->sp_pwdp) {
655                         fstrcpy(this_crypted, spass->sp_pwdp);
656                         fstrcpy(this_salt, spass->sp_pwdp);
657                 }
658         }
659 #elif defined(IA_UINFO)
660         {
661                 /* Need to get password with SVR4.2's ia_ functions
662                    instead of get{sp,pw}ent functions. Required by
663                    UnixWare 2.x, tested on version
664                    2.1. (tangent@cyberport.com) */
665                 uinfo_t uinfo;
666                 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
667                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
668         }
669 #endif
670
671 #ifdef HAVE_GETPRPWNAM
672         {
673                 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
674                 if (pr_pw && pr_pw->ufld.fd_encrypt)
675                         fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
676         }
677 #endif
678
679 #ifdef HAVE_GETPWANAM
680         {
681                 struct passwd_adjunct *pwret;
682                 pwret = getpwanam(s);
683                 if (pwret && pwret->pwa_passwd)
684                         fstrcpy(this_crypted, pwret->pwa_passwd);
685         }
686 #endif
687
688 #ifdef OSF1_ENH_SEC
689         {
690                 struct pr_passwd *mypasswd;
691                 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
692                           user));
693                 mypasswd = getprpwnam(user);
694                 if (mypasswd) {
695                         fstrcpy(this_user, mypasswd->ufld.fd_name);
696                         fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
697                 } else {
698                         DEBUG(5,
699                               ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
700                                user));
701                 }
702         }
703 #endif
704
705 #ifdef ULTRIX_AUTH
706         {
707                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
708                 if (ap) {
709                         fstrcpy(this_crypted, ap->a_password);
710                         endauthent();
711                 }
712         }
713 #endif
714
715 #if defined(HAVE_TRUNCATED_SALT)
716         /* crypt on some platforms (HPUX in particular)
717            won't work with more than 2 salt characters. */
718         this_salt[2] = 0;
719 #endif
720
721         if (!*this_crypted) {
722                 if (!lp_null_passwords()) {
723                         DEBUG(2, ("Disallowing %s with null password\n",
724                                   this_user));
725                         return NT_STATUS_LOGON_FAILURE;
726                 }
727                 if (!*password) {
728                         DEBUG(3,
729                               ("Allowing access to %s with null password\n",
730                                this_user));
731                         return NT_STATUS_OK;
732                 }
733         }
734
735 #endif /* defined(WITH_PAM) */
736
737         /* try it as it came to us */
738         nt_status = password_check(password);
739         if NT_STATUS_IS_OK(nt_status) {
740                 if (fn) {
741                         fn(user, password);
742                 }
743                 return (nt_status);
744         } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
745                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
746                 return (nt_status);
747         }
748
749         if (!run_cracker) {
750                 return (nt_status);
751         }
752
753         /* if the password was given to us with mixed case then we don't
754          * need to proceed as we know it hasn't been case modified by the
755          * client */
756         if (strhasupper(password) && strhaslower(password)) {
757                 return nt_status;
758         }
759
760         /* make a copy of it */
761         pstrcpy(pass2, password);
762
763         /* try all lowercase if it's currently all uppercase */
764         if (strhasupper(pass2)) {
765                 strlower_m(pass2);
766                 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
767                         if (fn)
768                                 fn(user, pass2);
769                         return (nt_status);
770                 }
771         }
772
773         /* give up? */
774         if (level < 1) {
775                 return NT_STATUS_WRONG_PASSWORD;
776         }
777
778         /* last chance - all combinations of up to level chars upper! */
779         strlower_m(pass2);
780  
781         if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
782                 if (fn)
783                         fn(user, pass2);
784                 return nt_status;
785         }
786         
787         return NT_STATUS_WRONG_PASSWORD;
788 }