Moved code that changes the pw_passwd entry (i.e shadow password and
[ira/wip.git] / source3 / passdb / pass_check.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Password checking
5    Copyright (C) Andrew Tridgell 1992-1998
6    
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.
11    
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.
16    
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.
20 */
21
22 /* this module is for checking a username/password against a system
23    password database. The SMB encrypted password support is elsewhere */
24
25 #include "includes.h"
26
27 extern int DEBUGLEVEL;
28
29 /* these are kept here to keep the string_combinations function simple */
30 static char this_user[100]="";
31 static char this_salt[100]="";
32 static char this_crypted[100]="";
33
34
35 #ifdef HAVE_PAM
36 /*******************************************************************
37 check on PAM authentication
38 ********************************************************************/
39
40 /* We first need some helper functions */
41 #include <security/pam_appl.h>
42 /* Static variables used to communicate between the conversation function
43  * and the server_login function
44  */
45 static char *PAM_username;
46 static char *PAM_password;
47
48 /* PAM conversation function
49  * Here we assume (for now, at least) that echo on means login name, and
50  * echo off means password.
51  */
52 static int PAM_conv (int num_msg,
53                      const struct pam_message **msg,
54                      struct pam_response **resp,
55                      void *appdata_ptr) {
56   int replies = 0;
57   struct pam_response *reply = NULL;
58
59   #define COPY_STRING(s) (s) ? strdup(s) : NULL
60
61   reply = malloc(sizeof(struct pam_response) * num_msg);
62   if (!reply) return PAM_CONV_ERR;
63
64   for (replies = 0; replies < num_msg; replies++) {
65     switch (msg[replies]->msg_style) {
66       case PAM_PROMPT_ECHO_ON:
67         reply[replies].resp_retcode = PAM_SUCCESS;
68         reply[replies].resp = COPY_STRING(PAM_username);
69           /* PAM frees resp */
70         break;
71       case PAM_PROMPT_ECHO_OFF:
72         reply[replies].resp_retcode = PAM_SUCCESS;
73         reply[replies].resp = COPY_STRING(PAM_password);
74           /* PAM frees resp */
75         break;
76       case PAM_TEXT_INFO:
77         /* fall through */
78       case PAM_ERROR_MSG:
79         /* ignore it... */
80         reply[replies].resp_retcode = PAM_SUCCESS;
81         reply[replies].resp = NULL;
82         break;
83       default:
84         /* Must be an error of some sort... */
85         free (reply);
86         return PAM_CONV_ERR;
87     }
88   }
89   if (reply) *resp = reply;
90   return PAM_SUCCESS;
91 }
92 static struct pam_conv PAM_conversation = {
93     &PAM_conv,
94     NULL
95 };
96
97
98 static BOOL pam_auth(char *user,char *password)
99 {
100   pam_handle_t *pamh;
101   int pam_error;
102
103   /* Now use PAM to do authentication.  For now, we won't worry about
104    * session logging, only authentication.  Bail out if there are any
105    * errors.  Since this is a limited protocol, and an even more limited
106    * function within a server speaking this protocol, we can't be as
107    * verbose as would otherwise make sense.
108    * Query: should we be using PAM_SILENT to shut PAM up?
109    */
110   #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
111      pam_end(pamh, 0); return False; \
112    }
113   PAM_password = password;
114   PAM_username = user;
115   pam_error = pam_start("samba", user, &PAM_conversation, &pamh);
116   PAM_BAIL;
117 /* Setting PAM_SILENT stops generation of error messages to syslog
118  * to enable debugging on Red Hat Linux set:
119  * /etc/pam.d/samba:
120  *      auth required /lib/security/pam_pwdb.so nullok shadow audit
121  * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging)
122  */
123   pam_error = pam_authenticate(pamh, PAM_SILENT);
124   PAM_BAIL;
125   /* It is not clear to me that account management is the right thing
126    * to do, but it is not clear that it isn't, either.  This can be
127    * removed if no account management should be done.  Alternately,
128    * put a pam_allow.so entry in /etc/pam.conf for account handling. */
129   pam_error = pam_acct_mgmt(pamh, PAM_SILENT);
130   PAM_BAIL;
131   pam_end(pamh, PAM_SUCCESS);
132   /* If this point is reached, the user has been authenticated. */
133   return(True);
134 }
135 #endif
136
137
138 #ifdef WITH_AFS
139 /*******************************************************************
140 check on AFS authentication
141 ********************************************************************/
142 static BOOL afs_auth(char *user,char *password)
143 {
144         long password_expires = 0;
145         char *reason;
146     
147         /* For versions of AFS prior to 3.3, this routine has few arguments, */
148         /* but since I can't find the old documentation... :-)               */
149         setpag();
150         if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
151                                        user,
152                                        (char *) 0, /* instance */
153                                        (char *) 0, /* cell */
154                                        password,
155                                        0,          /* lifetime, default */
156                                        &password_expires, /*days 'til it expires */
157                                        0,          /* spare 2 */
158                                        &reason) == 0) {
159                 return(True);
160         }
161         return(False);
162 }
163 #endif
164
165
166 #ifdef WITH_DFS
167
168 /*****************************************************************
169  This new version of the DFS_AUTH code was donated by Karsten Muuss
170  <muuss@or.uni-bonn.de>. It fixes the following problems with the
171  old code :
172
173   - Server credentials may expire
174   - Client credential cache files have wrong owner
175   - purge_context() function is called with invalid argument
176
177  This new code was modified to ensure that on exit the uid/gid is
178  still root, and the original directory is restored. JRA.
179 ******************************************************************/
180
181 sec_login_handle_t my_dce_sec_context;
182 int dcelogin_atmost_once = 0;
183
184 /*******************************************************************
185 check on a DCE/DFS authentication
186 ********************************************************************/
187 static BOOL dfs_auth(char *user,char *password)
188 {
189         error_status_t err;
190         int err2;
191         int prterr;
192         signed32 expire_time, current_time;
193         boolean32 password_reset;
194         struct passwd *pw;
195         sec_passwd_rec_t passwd_rec;
196         sec_login_auth_src_t auth_src = sec_login_auth_src_network;
197         unsigned char dce_errstr[dce_c_error_string_len];
198
199         if (dcelogin_atmost_once) return(False);
200
201 #ifdef HAVE_CRYPT
202         /*
203          * We only go for a DCE login context if the given password
204          * matches that stored in the local password file.. 
205          * Assumes local passwd file is kept in sync w/ DCE RGY!
206          */
207
208         if (strcmp((char *)crypt(password,this_salt),this_crypted)) {
209                 return(False);
210         }
211 #endif
212
213         sec_login_get_current_context(&my_dce_sec_context, &err);
214         if (err != error_status_ok ) {  
215                 dce_error_inq_text(err, dce_errstr, &err2);
216                 DEBUG(0,("DCE can't get current context. %s\n", dce_errstr));
217
218                 return(False);
219         }
220
221         sec_login_certify_identity(my_dce_sec_context, &err);
222         if (err != error_status_ok) {  
223                 dce_error_inq_text(err, dce_errstr, &err2);
224                 DEBUG(0,("DCE can't get current context. %s\n", dce_errstr));
225                 
226                 return(False);
227         }
228
229         sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
230         if (err != error_status_ok) {
231                 dce_error_inq_text(err, dce_errstr, &err2);
232                 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
233                 
234                 return(False);
235         }
236   
237         time(&current_time);
238
239         if (expire_time < (current_time + 60)) {
240                 struct passwd    *pw;
241                 sec_passwd_rec_t *key;
242   
243                 sec_login_get_pwent(my_dce_sec_context, 
244                                     (sec_login_passwd_t*)&pw, &err);
245                 if (err != error_status_ok ) {
246                         dce_error_inq_text(err, dce_errstr, &err2);
247                         DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
248                         
249                         return(False);
250                 }
251                 
252                 sec_login_refresh_identity(my_dce_sec_context, &err);
253                 if (err != error_status_ok) { 
254                         dce_error_inq_text(err, dce_errstr, &err2);
255                         DEBUG(0,("DCE can't refresh identity. %s\n", 
256                                  dce_errstr));
257                         
258                         return(False);
259                 }
260   
261                 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
262                                      (unsigned char *)pw->pw_name,
263                                      sec_c_key_version_none,
264                                      (void**)&key, &err);
265                 if (err != error_status_ok) {
266                         dce_error_inq_text(err, dce_errstr, &err2);
267                         DEBUG(0,("DCE can't get key for %s. %s\n", 
268                                  pw->pw_name, dce_errstr));
269
270                         return(False);
271                 }
272   
273                 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
274                                                &password_reset, &auth_src, 
275                                                &err);
276                 if (err != error_status_ok ) {
277                         dce_error_inq_text(err, dce_errstr, &err2);
278                         DEBUG(0,("DCE can't validate and certify identity for %s. %s\n", 
279                                  pw->pw_name, dce_errstr));
280                 }
281   
282                 sec_key_mgmt_free_key(key, &err);
283                 if (err != error_status_ok ) {
284                         dce_error_inq_text(err, dce_errstr, &err2);
285                         DEBUG(0,("DCE can't free key.\n", dce_errstr));
286                 }
287         }
288
289         if (sec_login_setup_identity((unsigned char *)user,
290                                      sec_login_no_flags,
291                                      &my_dce_sec_context,
292                                      &err) == 0) {
293                 dce_error_inq_text(err, dce_errstr, &err2);
294                 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
295                          user,dce_errstr));
296                 return(False);
297         }
298
299         sec_login_get_pwent(my_dce_sec_context, 
300                             (sec_login_passwd_t*)&pw, &err);
301         if (err != error_status_ok) {
302                 dce_error_inq_text(err, dce_errstr, &err2);
303                 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
304                 
305                 return(False);
306         }
307
308         sec_login_purge_context(&my_dce_sec_context, &err);
309         if (err != error_status_ok) {
310                 dce_error_inq_text(err, dce_errstr, &err2);
311                 DEBUG(0,("DCE can't purge context. %s\n", dce_errstr));
312
313                 return(False);
314         }
315
316         /*
317          * NB. I'd like to change these to call something like become_user()
318          * instead but currently we don't have a connection
319          * context to become the correct user. This is already
320          * fairly platform specific code however, so I think
321          * this should be ok. I have added code to go
322          * back to being root on error though. JRA.
323          */
324         
325         if (setregid(-1, pw->pw_gid) != 0) {
326                 DEBUG(0,("Can't set egid to %d (%s)\n", 
327                          pw->pw_gid, strerror(errno)));
328                 return False;
329         }
330
331         if (setreuid(-1, pw->pw_uid) != 0) {
332                 setgid(0);
333                 DEBUG(0,("Can't set euid to %d (%s)\n", 
334                          pw->pw_uid, strerror(errno)));
335                 return False;
336         }
337  
338         if (sec_login_setup_identity((unsigned char *)user,
339                                      sec_login_no_flags,
340                                      &my_dce_sec_context,
341                                      &err) == 0) {
342                 dce_error_inq_text(err, dce_errstr, &err2);
343                 /* Go back to root, JRA. */
344                 setuid(0);
345                 setgid(0);
346                 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
347                          user,dce_errstr));
348                 return(False);
349         }
350
351         sec_login_get_pwent(my_dce_sec_context, 
352                             (sec_login_passwd_t*)&pw, &err);
353         if (err != error_status_ok ) {
354                 dce_error_inq_text(err, dce_errstr, &err2);
355                 /* Go back to root, JRA. */
356                 setuid(0);
357                 setgid(0);
358                 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
359                 
360                 return(False);
361         }
362
363         passwd_rec.version_number = sec_passwd_c_version_none;
364         passwd_rec.pepper = NULL;
365         passwd_rec.key.key_type = sec_passwd_plain;
366         passwd_rec.key.tagged_union.plain  = (idl_char *)password;
367         
368         sec_login_validate_identity(my_dce_sec_context,
369                                     &passwd_rec, &password_reset,
370                                     &auth_src, &err);
371         if (err != error_status_ok ) { 
372                 dce_error_inq_text(err, dce_errstr, &err2);
373                 /* Go back to root, JRA. */
374                 setuid(0);
375                 setgid(0);
376                 DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
377                          user,dce_errstr));
378                 
379                 return(False);
380         }
381
382         sec_login_certify_identity(my_dce_sec_context, &err);
383         if (err != error_status_ok) { 
384                 dce_error_inq_text(err, dce_errstr, &err2);
385                 /* Go back to root, JRA. */
386                 setuid(0);
387                 setgid(0);
388                 DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr));
389                 
390                 return(False);
391         }
392
393         if (auth_src != sec_login_auth_src_network) { 
394                 DEBUG(0,("DCE context has no network credentials.\n"));
395         }
396
397         sec_login_set_context(my_dce_sec_context, &err);
398         if (err != error_status_ok) {  
399                 dce_error_inq_text(err, dce_errstr, &err2);
400                 DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n",
401                          user,dce_errstr));
402                 
403                 sec_login_purge_context(&my_dce_sec_context, &err);
404                 /* Go back to root, JRA. */
405                 setuid(0);
406                 setgid(0);
407                 return(False);
408         }
409         
410         sec_login_get_pwent(my_dce_sec_context, 
411                             (sec_login_passwd_t*)&pw, &err);
412         if (err != error_status_ok) {
413                 dce_error_inq_text(err, dce_errstr, &err2);
414                 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
415
416                 /* Go back to root, JRA. */
417                 setuid(0);
418                 setgid(0);
419                 return(False);
420         }
421         
422         DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
423                  user, getpid()));
424         
425         DEBUG(3,("DCE principal: %s\n"
426                  "          uid: %d\n"
427                  "          gid: %d\n",
428                  pw->pw_name, pw->pw_uid, pw->pw_gid));
429         DEBUG(3,("         info: %s\n"
430                  "          dir: %s\n"
431                  "        shell: %s\n",
432                  pw->pw_gecos, pw->pw_dir, pw->pw_shell));
433         
434         sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
435         if (err != error_status_ok) {
436                 dce_error_inq_text(err, dce_errstr, &err2);
437                 /* Go back to root, JRA. */
438                 setuid(0);
439                 setgid(0);
440                 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
441                 
442                 return(False);
443         }
444         
445         setuid(0);
446         setgid(0);
447         
448         DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time))));
449         
450         dcelogin_atmost_once = 1;
451         return (True);
452 }
453
454 void dfs_unlogin(void)
455 {
456         error_status_t err;
457         int err2;
458         unsigned char dce_errstr[dce_c_error_string_len];
459         
460         sec_login_purge_context(&my_dce_sec_context, &err);
461         if (err != error_status_ok) {  
462                 dce_error_inq_text(err, dce_errstr, &err2);
463                 DEBUG(0,("DCE purge login context failed for server instance %d: %s\n",
464                          getpid(), dce_errstr));
465         }
466 }
467 #endif
468
469 #ifdef KRB5_AUTH
470 /*******************************************************************
471 check on Kerberos authentication
472 ********************************************************************/
473 static BOOL krb5_auth(char *user,char *password)
474 {
475         krb5_data tgtname = {
476                 0,
477                 KRB5_TGS_NAME_SIZE,
478                 KRB5_TGS_NAME
479         };
480         krb5_context kcontext;
481         krb5_principal kprinc;
482         krb5_principal server;
483         krb5_creds kcreds;
484         int options = 0;
485         krb5_address **addrs = (krb5_address **)0;
486         krb5_preauthtype *preauth = NULL;
487         krb5_keytab keytab = NULL;
488         krb5_timestamp now;
489         krb5_ccache ccache = NULL;
490         int retval;
491         char *name;
492
493         if (retval=krb5_init_context(&kcontext)) {
494                 return(False);
495         }
496
497         if (retval = krb5_timeofday(kcontext, &now)) {
498                 return(False);
499         }
500
501         if (retval = krb5_cc_default(kcontext, &ccache)) {
502                 return(False);
503         }
504         
505         if (retval = krb5_parse_name(kcontext, user, &kprinc)) {
506                 return(False);
507         }
508
509         ZERO_STRUCT(kcreds);
510
511         kcreds.client = kprinc;
512         
513         if ((retval = krb5_build_principal_ext(kcontext, &server,
514                 krb5_princ_realm(kcontext, kprinc)->length,
515                 krb5_princ_realm(kcontext, kprinc)->data,
516                 tgtname.length,
517                 tgtname.data,
518                 krb5_princ_realm(kcontext, kprinc)->length,
519                 krb5_princ_realm(kcontext, kprinc)->data,
520                 0))) {
521                 return(False);
522         }
523
524         kcreds.server = server;
525
526         retval = krb5_get_in_tkt_with_password(kcontext,
527                                                options,
528                                                addrs,
529                                                NULL,
530                                                preauth,
531                                                password,
532                                                0,
533                                                &kcreds,
534                                                0);
535
536         if (retval) {
537                 return(False);
538         }
539
540         return(True);
541 }
542 #endif /* KRB5_AUTH */
543
544 #ifdef KRB4_AUTH
545 #include <krb.h>
546
547 /*******************************************************************
548 check on Kerberos authentication
549 ********************************************************************/
550 static BOOL krb4_auth(char *user,char *password)
551 {
552         char realm[REALM_SZ];
553         char tkfile[MAXPATHLEN];
554   
555         if (krb_get_lrealm(realm, 1) != KSUCCESS) {
556                 (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
557         }
558
559         (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", 
560                         (int)getpid());
561   
562         krb_set_tkt_string(tkfile);
563         if (krb_verify_user(user, "", realm,
564                             password, 0,
565                             "rmcd") == KSUCCESS) {
566                 unlink(tkfile);
567                 return 1;
568         }
569         unlink(tkfile);
570         return 0;
571 }
572 #endif /* KRB4_AUTH */
573
574 #ifdef LINUX_BIGCRYPT
575 /****************************************************************************
576 an enhanced crypt for Linux to handle password longer than 8 characters
577 ****************************************************************************/
578 static int linux_bigcrypt(char *password,char *salt1, char *crypted)
579 {
580 #define LINUX_PASSWORD_SEG_CHARS 8
581         char salt[3];
582         int i;
583   
584         StrnCpy(salt,salt1,2);
585         crypted +=2;
586   
587         for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
588                 char * p = crypt(password,salt) + 2;
589                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
590                         return(0);
591                 password += LINUX_PASSWORD_SEG_CHARS;
592                 crypted  += strlen(p);
593         }
594   
595         return(1);
596 }
597 #endif
598
599 #ifdef OSF1_ENH_SEC
600 /****************************************************************************
601 an enhanced crypt for OSF1
602 ****************************************************************************/
603 static char *osf1_bigcrypt(char *password,char *salt1)
604 {
605         static char result[AUTH_MAX_PASSWD_LENGTH] = "";
606         char *p1;
607         char *p2=password;
608         char salt[3];
609         int i;
610         int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
611         if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) {
612                 parts++;
613         }
614         
615         StrnCpy(salt,salt1,2);
616         StrnCpy(result,salt1,2);
617
618         for (i=0; i<parts;i++) {
619                 p1 = crypt(p2,salt);
620                 strncat(result,p1+2,AUTH_MAX_PASSWD_LENGTH-strlen(p1+2)-1);
621                 StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
622                 p2 += AUTH_CLEARTEXT_SEG_CHARS;
623         }
624
625         return(result);
626 }
627 #endif
628
629
630 /****************************************************************************
631 apply a function to upper/lower case combinations
632 of a string and return true if one of them returns true.
633 try all combinations with N uppercase letters.
634 offset is the first char to try and change (start with 0)
635 it assumes the string starts lowercased
636 ****************************************************************************/
637 static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(char *),int N)
638 {
639         int len = strlen(s);
640         int i;
641
642 #ifdef PASSWORD_LENGTH
643         len = MIN(len,PASSWORD_LENGTH);
644 #endif
645
646         if (N <= 0 || offset >= len) {
647                 return(fn(s));
648         }
649
650         for (i=offset;i<(len-(N-1));i++) {      
651                 char c = s[i];
652                 if (!islower(c)) continue;
653                 s[i] = toupper(c);
654                 if (string_combinations2(s,i+1,fn,N-1))
655                         return(True);
656                 s[i] = c;
657         }
658         return(False);
659 }
660
661 /****************************************************************************
662 apply a function to upper/lower case combinations
663 of a string and return true if one of them returns true.
664 try all combinations with up to N uppercase letters.
665 offset is the first char to try and change (start with 0)
666 it assumes the string starts lowercased
667 ****************************************************************************/
668 static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N)
669 {
670         int n;
671         for (n=1;n<=N;n++)
672                 if (string_combinations2(s,0,fn,n)) return(True);
673         return(False);
674 }
675
676
677 /****************************************************************************
678 core of password checking routine
679 ****************************************************************************/
680 static BOOL password_check(char *password)
681 {
682
683 #ifdef HAVE_PAM
684         /* This falls through if the password check fails
685            - if HAVE_CRYPT is not defined this causes an error msg
686            saying Warning - no crypt available
687            - if HAVE_CRYPT is defined this is a potential security hole
688            as it may authenticate via the crypt call when PAM
689            settings say it should fail.
690            if (pam_auth(user,password)) return(True);
691            Hence we make a direct return to avoid a second chance!!!
692         */
693         return (pam_auth(this_user,password));
694 #endif
695         
696 #ifdef WITH_AFS
697         if (afs_auth(this_user,password)) return(True);
698 #endif
699         
700 #ifdef WITH_DFS
701         if (dfs_auth(this_user,password)) return(True);
702 #endif 
703
704 #ifdef KRB5_AUTH
705         if (krb5_auth(this_user,password)) return(True);
706 #endif
707
708 #ifdef KRB4_AUTH
709         if (krb4_auth(this_user,password)) return(True);
710 #endif
711
712 #ifdef OSF1_ENH_SEC
713         {
714           BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
715           if(!ret) {
716                   DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
717                   ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
718           }
719           return ret;
720         }
721 #endif
722
723 #ifdef ULTRIX_AUTH
724         return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
725 #endif
726
727 #ifdef LINUX_BIGCRYPT
728         return(linux_bigcrypt(password,this_salt,this_crypted));
729 #endif
730
731 #ifdef HAVE_BIGCRYPT
732         return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
733 #endif
734
735 #ifndef HAVE_CRYPT
736         DEBUG(1,("Warning - no crypt available\n"));
737         return(False);
738 #else
739         return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
740 #endif
741 }
742
743
744
745 /****************************************************************************
746 check if a username/password is OK
747 the function pointer fn() points to a function to call when a successful
748 match is found and is used to update the encrypted password file 
749 return True on correct match, False otherwise
750 ****************************************************************************/
751 BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd,
752                 BOOL (*fn)(char *, char *))
753 {
754         pstring pass2;
755         int level = lp_passwordlevel();
756         const struct passwd *pass;
757
758         if (password) password[pwlen] = 0;
759
760 #if DEBUG_PASSWORD
761         DEBUG(100,("checking user=[%s] pass=",user));
762         dump_data(100, password, strlen(password));
763 #endif
764
765         if (!password) {
766                 return(False);
767         }
768
769         if (((!*password) || (!pwlen)) && !lp_null_passwords()) {
770                 return(False);
771         }
772
773         if (pwd && !user) {
774                 pass = (struct passwd *) pwd;
775                 user = pass->pw_name;
776         } else {
777                 pass = Get_Pwnam(user,True);
778         }
779
780
781         DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
782
783         if (!pass) {
784                 DEBUG(3,("Couldn't find user %s\n",user));
785                 return(False);
786         }
787
788         /* extract relevant info */
789         fstrcpy(this_user,pass->pw_name);  
790         fstrcpy(this_salt,pass->pw_passwd);
791         /* crypt on some platforms (HPUX in particular)
792            won't work with more than 2 salt characters. */
793         this_salt[2] = 0;
794         
795         fstrcpy(this_crypted,pass->pw_passwd);
796         
797         if (!*this_crypted) {
798                 if (!lp_null_passwords()) {
799                         DEBUG(2,("Disallowing %s with null password\n",
800                                  this_user));
801                         return(False);
802                 }
803                 if (!*password) {
804                         DEBUG(3,("Allowing access to %s with null password\n",
805                                  this_user));
806                         return(True);
807                 }
808         }
809
810         /* try it as it came to us */
811         if (password_check(password)) {
812                 if (fn) fn(user,password);
813                 return(True);
814         }
815
816         /* if the password was given to us with mixed case then we don't
817            need to proceed as we know it hasn't been case modified by the
818            client */
819         if (strhasupper(password) && strhaslower(password)) {
820                 return(False);
821         }
822
823         /* make a copy of it */
824         StrnCpy(pass2,password,sizeof(pstring)-1);
825   
826         /* try all lowercase */
827         strlower(password);
828         if (password_check(password)) {
829                 if (fn) fn(user,password);
830                 return(True);
831         }
832
833         /* give up? */
834         if (level < 1) {
835
836                 /* restore it */
837                 fstrcpy(password,pass2);
838                 
839                 return(False);
840         }
841
842         /* last chance - all combinations of up to level chars upper! */
843         strlower(password);
844
845         if (string_combinations(password,password_check,level)) {
846                 if (fn) fn(user,password);
847                 return(True);
848         }
849
850         /* restore it */
851         fstrcpy(password,pass2);
852   
853         return(False);
854 }