some cleanups to use ZERO_STRUCT() and friends
[nivanova/samba-autobuild/.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 /*******************************************************************
546 check on Kerberos authentication
547 ********************************************************************/
548 static BOOL krb4_auth(char *user,char *password)
549 {
550         char realm[REALM_SZ];
551         char tkfile[MAXPATHLEN];
552   
553         if (krb_get_lrealm(realm, 1) != KSUCCESS) {
554                 (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
555         }
556
557         (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", 
558                         getpid());
559   
560         krb_set_tkt_string(tkfile);
561         if (krb_verify_user(user, "", realm,
562                             password, 0,
563                             "rmcd") == KSUCCESS) {
564                 unlink(tkfile);
565                 return 1;
566         }
567         unlink(tkfile);
568         return 0;
569 }
570 #endif /* KRB4_AUTH */
571
572 #ifdef LINUX_BIGCRYPT
573 /****************************************************************************
574 an enhanced crypt for Linux to handle password longer than 8 characters
575 ****************************************************************************/
576 static int linux_bigcrypt(char *password,char *salt1, char *crypted)
577 {
578 #define LINUX_PASSWORD_SEG_CHARS 8
579         char salt[3];
580         int i;
581   
582         StrnCpy(salt,salt1,2);
583         crypted +=2;
584   
585         for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
586                 char * p = crypt(password,salt) + 2;
587                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
588                         return(0);
589                 password += LINUX_PASSWORD_SEG_CHARS;
590                 crypted  += strlen(p);
591         }
592   
593         return(1);
594 }
595 #endif
596
597 #ifdef OSF1_ENH_SEC
598 /****************************************************************************
599 an enhanced crypt for OSF1
600 ****************************************************************************/
601 static char *osf1_bigcrypt(char *password,char *salt1)
602 {
603         static char result[AUTH_MAX_PASSWD_LENGTH] = "";
604         char *p1;
605         char *p2=password;
606         char salt[3];
607         int i;
608         int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
609         if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) {
610                 parts++;
611         }
612         
613         StrnCpy(salt,salt1,2);
614         StrnCpy(result,salt1,2);
615
616         for (i=0; i<parts;i++) {
617                 p1 = crypt(p2,salt);
618                 strncat(result,p1+2,AUTH_MAX_PASSWD_LENGTH-strlen(p1+2)-1);
619                 StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
620                 p2 += AUTH_CLEARTEXT_SEG_CHARS;
621         }
622
623         return(result);
624 }
625 #endif
626
627
628 /****************************************************************************
629 apply a function to upper/lower case combinations
630 of a string and return true if one of them returns true.
631 try all combinations with N uppercase letters.
632 offset is the first char to try and change (start with 0)
633 it assumes the string starts lowercased
634 ****************************************************************************/
635 static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(char *),int N)
636 {
637         int len = strlen(s);
638         int i;
639
640 #ifdef PASSWORD_LENGTH
641         len = MIN(len,PASSWORD_LENGTH);
642 #endif
643
644         if (N <= 0 || offset >= len) {
645                 return(fn(s));
646         }
647
648         for (i=offset;i<(len-(N-1));i++) {      
649                 char c = s[i];
650                 if (!islower(c)) continue;
651                 s[i] = toupper(c);
652                 if (string_combinations2(s,i+1,fn,N-1))
653                         return(True);
654                 s[i] = c;
655         }
656         return(False);
657 }
658
659 /****************************************************************************
660 apply a function to upper/lower case combinations
661 of a string and return true if one of them returns true.
662 try all combinations with up to N uppercase letters.
663 offset is the first char to try and change (start with 0)
664 it assumes the string starts lowercased
665 ****************************************************************************/
666 static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N)
667 {
668         int n;
669         for (n=1;n<=N;n++)
670                 if (string_combinations2(s,0,fn,n)) return(True);
671         return(False);
672 }
673
674
675 /****************************************************************************
676 core of password checking routine
677 ****************************************************************************/
678 static BOOL password_check(char *password)
679 {
680
681 #ifdef HAVE_PAM
682         /* This falls through if the password check fails
683            - if HAVE_CRYPT is not defined this causes an error msg
684            saying Warning - no crypt available
685            - if HAVE_CRYPT is defined this is a potential security hole
686            as it may authenticate via the crypt call when PAM
687            settings say it should fail.
688            if (pam_auth(user,password)) return(True);
689            Hence we make a direct return to avoid a second chance!!!
690         */
691         return (pam_auth(this_user,password));
692 #endif
693         
694 #ifdef WITH_AFS
695         if (afs_auth(this_user,password)) return(True);
696 #endif
697         
698 #ifdef WITH_DFS
699         if (dfs_auth(this_user,password)) return(True);
700 #endif 
701
702 #ifdef KRB5_AUTH
703         if (krb5_auth(this_user,password)) return(True);
704 #endif
705
706 #ifdef KRB4_AUTH
707         if (krb4_auth(this_user,password)) return(True);
708 #endif
709
710 #ifdef OSF1_ENH_SEC
711         {
712           BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
713           if(!ret) {
714                   DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
715                   ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
716           }
717           return ret;
718         }
719 #endif
720
721 #ifdef ULTRIX_AUTH
722         return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
723 #endif
724
725 #ifdef LINUX_BIGCRYPT
726         return(linux_bigcrypt(password,this_salt,this_crypted));
727 #endif
728
729 #ifdef HAVE_BIGCRYPT
730         return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
731 #endif
732
733 #ifndef HAVE_CRYPT
734         DEBUG(1,("Warning - no crypt available\n"));
735         return(False);
736 #else
737         return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
738 #endif
739 }
740
741
742
743 /****************************************************************************
744 check if a username/password is OK
745 the function pointer fn() points to a function to call when a successful
746 match is found and is used to update the encrypted password file 
747 return True on correct match, False otherwise
748 ****************************************************************************/
749 BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd,
750                 BOOL (*fn)(char *, char *))
751 {
752         pstring pass2;
753         int level = lp_passwordlevel();
754         struct passwd *pass;
755
756         if (password) password[pwlen] = 0;
757
758 #if DEBUG_PASSWORD
759         DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
760 #endif
761
762         if (!password) {
763                 return(False);
764         }
765
766         if (((!*password) || (!pwlen)) && !lp_null_passwords()) {
767                 return(False);
768         }
769
770         if (pwd && !user) {
771                 pass = (struct passwd *) pwd;
772                 user = pass->pw_name;
773         } else {
774                 pass = Get_Pwnam(user,True);
775         }
776
777
778         DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
779
780         if (!pass) {
781                 DEBUG(3,("Couldn't find user %s\n",user));
782                 return(False);
783         }
784
785 #ifdef HAVE_GETSPNAM
786         {
787                 struct spwd *spass;
788
789                 /* many shadow systems require you to be root to get
790                    the password, in most cases this should already be
791                    the case when this function is called, except
792                    perhaps for IPC password changing requests */
793
794                 spass = getspnam(pass->pw_name);
795                 if (spass && spass->sp_pwdp) {
796                         pass->pw_passwd = spass->sp_pwdp;
797                 }
798         }
799 #elif defined(IA_UINFO)
800         {
801                 /* Need to get password with SVR4.2's ia_ functions
802                    instead of get{sp,pw}ent functions. Required by
803                    UnixWare 2.x, tested on version
804                    2.1. (tangent@cyberport.com) */
805                 uinfo_t uinfo;
806                 if (ia_openinfo(pass->pw_name, &uinfo) != -1) {
807                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
808                 }
809         }
810 #endif
811
812 #ifdef HAVE_GETPRPWNAM
813         {
814                 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
815                 if (pr_pw && pr_pw->ufld.fd_encrypt)
816                         pass->pw_passwd = pr_pw->ufld.fd_encrypt;
817         }
818 #endif
819
820 #ifdef OSF1_ENH_SEC
821         {
822                 struct pr_passwd *mypasswd;
823                 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",
824                          user));
825                 mypasswd = getprpwnam (user);
826                 if (mypasswd) { 
827                         fstrcpy(pass->pw_name,mypasswd->ufld.fd_name);
828                         fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
829                 } else {
830                         DEBUG(5,("No entry for user %s in protected database !\n",
831                                  user));
832                         return(False);
833                 }
834         }
835 #endif
836
837 #ifdef ULTRIX_AUTH
838         {
839                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
840                 if (ap) {
841                         fstrcpy(pass->pw_passwd, ap->a_password);
842                         endauthent();
843                 }
844         }
845 #endif
846
847         /* extract relevant info */
848         fstrcpy(this_user,pass->pw_name);  
849         fstrcpy(this_salt,pass->pw_passwd);
850         /* crypt on some platforms (HPUX in particular)
851            won't work with more than 2 salt characters. */
852         this_salt[2] = 0;
853         
854         fstrcpy(this_crypted,pass->pw_passwd);
855         
856         if (!*this_crypted) {
857                 if (!lp_null_passwords()) {
858                         DEBUG(2,("Disallowing %s with null password\n",
859                                  this_user));
860                         return(False);
861                 }
862                 if (!*password) {
863                         DEBUG(3,("Allowing access to %s with null password\n",
864                                  this_user));
865                         return(True);
866                 }
867         }
868
869         /* try it as it came to us */
870         if (password_check(password)) {
871                 if (fn) fn(user,password);
872                 return(True);
873         }
874
875         /* if the password was given to us with mixed case then we don't
876            need to proceed as we know it hasn't been case modified by the
877            client */
878         if (strhasupper(password) && strhaslower(password)) {
879                 return(False);
880         }
881
882         /* make a copy of it */
883         StrnCpy(pass2,password,sizeof(pstring)-1);
884   
885         /* try all lowercase */
886         strlower(password);
887         if (password_check(password)) {
888                 if (fn) fn(user,password);
889                 return(True);
890         }
891
892         /* give up? */
893         if (level < 1) {
894
895                 /* restore it */
896                 fstrcpy(password,pass2);
897                 
898                 return(False);
899         }
900
901         /* last chance - all combinations of up to level chars upper! */
902         strlower(password);
903
904         if (string_combinations(password,password_check,level)) {
905                 if (fn) fn(user,password);
906                 return(True);
907         }
908
909         /* restore it */
910         fstrcpy(password,pass2);
911   
912         return(False);
913 }