pass_check.c could receive encrypted password: printing it out as a %s
[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         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 #ifdef HAVE_GETSPNAM
789         {
790                 struct spwd *spass;
791
792                 /* many shadow systems require you to be root to get
793                    the password, in most cases this should already be
794                    the case when this function is called, except
795                    perhaps for IPC password changing requests */
796
797                 spass = getspnam(pass->pw_name);
798                 if (spass && spass->sp_pwdp) {
799                         pass->pw_passwd = spass->sp_pwdp;
800                 }
801         }
802 #elif defined(IA_UINFO)
803         {
804                 /* Need to get password with SVR4.2's ia_ functions
805                    instead of get{sp,pw}ent functions. Required by
806                    UnixWare 2.x, tested on version
807                    2.1. (tangent@cyberport.com) */
808                 uinfo_t uinfo;
809                 if (ia_openinfo(pass->pw_name, &uinfo) != -1) {
810                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
811                 }
812         }
813 #endif
814
815 #ifdef HAVE_GETPRPWNAM
816         {
817                 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
818                 if (pr_pw && pr_pw->ufld.fd_encrypt)
819                         pass->pw_passwd = pr_pw->ufld.fd_encrypt;
820         }
821 #endif
822
823 #ifdef OSF1_ENH_SEC
824         {
825                 struct pr_passwd *mypasswd;
826                 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",
827                          user));
828                 mypasswd = getprpwnam (user);
829                 if (mypasswd) { 
830                         fstrcpy(pass->pw_name,mypasswd->ufld.fd_name);
831                         fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
832                 } else {
833                         DEBUG(5,("No entry for user %s in protected database !\n",
834                                  user));
835                         return(False);
836                 }
837         }
838 #endif
839
840 #ifdef ULTRIX_AUTH
841         {
842                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
843                 if (ap) {
844                         fstrcpy(pass->pw_passwd, ap->a_password);
845                         endauthent();
846                 }
847         }
848 #endif
849
850         /* extract relevant info */
851         fstrcpy(this_user,pass->pw_name);  
852         fstrcpy(this_salt,pass->pw_passwd);
853         /* crypt on some platforms (HPUX in particular)
854            won't work with more than 2 salt characters. */
855         this_salt[2] = 0;
856         
857         fstrcpy(this_crypted,pass->pw_passwd);
858         
859         if (!*this_crypted) {
860                 if (!lp_null_passwords()) {
861                         DEBUG(2,("Disallowing %s with null password\n",
862                                  this_user));
863                         return(False);
864                 }
865                 if (!*password) {
866                         DEBUG(3,("Allowing access to %s with null password\n",
867                                  this_user));
868                         return(True);
869                 }
870         }
871
872         /* try it as it came to us */
873         if (password_check(password)) {
874                 if (fn) fn(user,password);
875                 return(True);
876         }
877
878         /* if the password was given to us with mixed case then we don't
879            need to proceed as we know it hasn't been case modified by the
880            client */
881         if (strhasupper(password) && strhaslower(password)) {
882                 return(False);
883         }
884
885         /* make a copy of it */
886         StrnCpy(pass2,password,sizeof(pstring)-1);
887   
888         /* try all lowercase */
889         strlower(password);
890         if (password_check(password)) {
891                 if (fn) fn(user,password);
892                 return(True);
893         }
894
895         /* give up? */
896         if (level < 1) {
897
898                 /* restore it */
899                 fstrcpy(password,pass2);
900                 
901                 return(False);
902         }
903
904         /* last chance - all combinations of up to level chars upper! */
905         strlower(password);
906
907         if (string_combinations(password,password_check,level)) {
908                 if (fn) fn(user,password);
909                 return(True);
910         }
911
912         /* restore it */
913         fstrcpy(password,pass2);
914   
915         return(False);
916 }