11ce0d754e8ab06b806281fe89d3e5e186134344
[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 WITH_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 #include <afs/stds.h>
141 #include <afs/kautils.h>
142
143 /*******************************************************************
144 check on AFS authentication
145 ********************************************************************/
146 static BOOL afs_auth(char *user,char *password)
147 {
148         long password_expires = 0;
149         char *reason;
150     
151         /* For versions of AFS prior to 3.3, this routine has few arguments, */
152         /* but since I can't find the old documentation... :-)               */
153         setpag();
154         if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
155                                        user,
156                                        (char *) 0, /* instance */
157                                        (char *) 0, /* cell */
158                                        password,
159                                        0,          /* lifetime, default */
160                                        &password_expires, /*days 'til it expires */
161                                        0,          /* spare 2 */
162                                        &reason) == 0) {
163                 return(True);
164         }
165         DEBUG(1,("AFS authentication for \"%s\" failed (%s)\n", user, reason));
166         return(False);
167 }
168 #endif
169
170
171 #ifdef WITH_DFS
172
173 #include <dce/dce_error.h>
174 #include <dce/sec_login.h>
175
176 /*****************************************************************
177  This new version of the DFS_AUTH code was donated by Karsten Muuss
178  <muuss@or.uni-bonn.de>. It fixes the following problems with the
179  old code :
180
181   - Server credentials may expire
182   - Client credential cache files have wrong owner
183   - purge_context() function is called with invalid argument
184
185  This new code was modified to ensure that on exit the uid/gid is
186  still root, and the original directory is restored. JRA.
187 ******************************************************************/
188
189 sec_login_handle_t my_dce_sec_context;
190 int dcelogin_atmost_once = 0;
191
192 /*******************************************************************
193 check on a DCE/DFS authentication
194 ********************************************************************/
195 static BOOL dfs_auth(char *user,char *password)
196 {
197         error_status_t err;
198         int err2;
199         int prterr;
200         signed32 expire_time, current_time;
201         boolean32 password_reset;
202         struct passwd *pw;
203         sec_passwd_rec_t passwd_rec;
204         sec_login_auth_src_t auth_src = sec_login_auth_src_network;
205         unsigned char dce_errstr[dce_c_error_string_len];
206         gid_t egid;
207
208         if (dcelogin_atmost_once) return(False);
209
210 #ifdef HAVE_CRYPT
211         /*
212          * We only go for a DCE login context if the given password
213          * matches that stored in the local password file.. 
214          * Assumes local passwd file is kept in sync w/ DCE RGY!
215          */
216
217         if (strcmp((char *)crypt(password,this_salt),this_crypted)) {
218                 return(False);
219         }
220 #endif
221
222         sec_login_get_current_context(&my_dce_sec_context, &err);
223         if (err != error_status_ok ) {  
224                 dce_error_inq_text(err, dce_errstr, &err2);
225                 DEBUG(0,("DCE can't get current context. %s\n", dce_errstr));
226
227                 return(False);
228         }
229
230         sec_login_certify_identity(my_dce_sec_context, &err);
231         if (err != error_status_ok) {  
232                 dce_error_inq_text(err, dce_errstr, &err2);
233                 DEBUG(0,("DCE can't get current context. %s\n", dce_errstr));
234                 
235                 return(False);
236         }
237
238         sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
239         if (err != error_status_ok) {
240                 dce_error_inq_text(err, dce_errstr, &err2);
241                 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
242                 
243                 return(False);
244         }
245   
246         time(&current_time);
247
248         if (expire_time < (current_time + 60)) {
249                 struct passwd    *pw;
250                 sec_passwd_rec_t *key;
251   
252                 sec_login_get_pwent(my_dce_sec_context, 
253                                     (sec_login_passwd_t*)&pw, &err);
254                 if (err != error_status_ok ) {
255                         dce_error_inq_text(err, dce_errstr, &err2);
256                         DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
257                         
258                         return(False);
259                 }
260                 
261                 sec_login_refresh_identity(my_dce_sec_context, &err);
262                 if (err != error_status_ok) { 
263                         dce_error_inq_text(err, dce_errstr, &err2);
264                         DEBUG(0,("DCE can't refresh identity. %s\n", 
265                                  dce_errstr));
266                         
267                         return(False);
268                 }
269   
270                 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
271                                      (unsigned char *)pw->pw_name,
272                                      sec_c_key_version_none,
273                                      (void**)&key, &err);
274                 if (err != error_status_ok) {
275                         dce_error_inq_text(err, dce_errstr, &err2);
276                         DEBUG(0,("DCE can't get key for %s. %s\n", 
277                                  pw->pw_name, dce_errstr));
278
279                         return(False);
280                 }
281   
282                 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
283                                                &password_reset, &auth_src, 
284                                                &err);
285                 if (err != error_status_ok ) {
286                         dce_error_inq_text(err, dce_errstr, &err2);
287                         DEBUG(0,("DCE can't validate and certify identity for %s. %s\n", 
288                                  pw->pw_name, dce_errstr));
289                 }
290   
291                 sec_key_mgmt_free_key(key, &err);
292                 if (err != error_status_ok ) {
293                         dce_error_inq_text(err, dce_errstr, &err2);
294                         DEBUG(0,("DCE can't free key.\n", dce_errstr));
295                 }
296         }
297
298         if (sec_login_setup_identity((unsigned char *)user,
299                                      sec_login_no_flags,
300                                      &my_dce_sec_context,
301                                      &err) == 0) {
302                 dce_error_inq_text(err, dce_errstr, &err2);
303                 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
304                          user,dce_errstr));
305                 return(False);
306         }
307
308         sec_login_get_pwent(my_dce_sec_context, 
309                             (sec_login_passwd_t*)&pw, &err);
310         if (err != error_status_ok) {
311                 dce_error_inq_text(err, dce_errstr, &err2);
312                 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
313                 
314                 return(False);
315         }
316
317         sec_login_purge_context(&my_dce_sec_context, &err);
318         if (err != error_status_ok) {
319                 dce_error_inq_text(err, dce_errstr, &err2);
320                 DEBUG(0,("DCE can't purge context. %s\n", dce_errstr));
321
322                 return(False);
323         }
324
325         /*
326          * NB. I'd like to change these to call something like become_user()
327          * instead but currently we don't have a connection
328          * context to become the correct user. This is already
329          * fairly platform specific code however, so I think
330          * this should be ok. I have added code to go
331          * back to being root on error though. JRA.
332          */
333         
334         egid = getegid();
335
336         if (set_effective_gid(pw->pw_gid) != 0) {
337                 DEBUG(0,("Can't set egid to %d (%s)\n", 
338                          pw->pw_gid, strerror(errno)));
339                 return False;
340         }
341
342         if (set_effective_uid(pw->pw_uid) != 0) {
343                 set_effective_gid(egid);
344                 DEBUG(0,("Can't set euid to %d (%s)\n", 
345                          pw->pw_uid, strerror(errno)));
346                 return False;
347         }
348  
349         if (sec_login_setup_identity((unsigned char *)user,
350                                      sec_login_no_flags,
351                                      &my_dce_sec_context,
352                                      &err) == 0) {
353                 dce_error_inq_text(err, dce_errstr, &err2);
354                 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
355                          user,dce_errstr));
356                 goto err;
357         }
358
359         sec_login_get_pwent(my_dce_sec_context, 
360                             (sec_login_passwd_t*)&pw, &err);
361         if (err != error_status_ok ) {
362                 dce_error_inq_text(err, dce_errstr, &err2);
363                 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
364                 goto err;
365         }
366
367         passwd_rec.version_number = sec_passwd_c_version_none;
368         passwd_rec.pepper = NULL;
369         passwd_rec.key.key_type = sec_passwd_plain;
370         passwd_rec.key.tagged_union.plain  = (idl_char *)password;
371         
372         sec_login_validate_identity(my_dce_sec_context,
373                                     &passwd_rec, &password_reset,
374                                     &auth_src, &err);
375         if (err != error_status_ok ) { 
376                 dce_error_inq_text(err, dce_errstr, &err2);
377                 DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
378                          user,dce_errstr));
379                 goto err;
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                 DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr));
386                 goto err;
387         }
388
389         if (auth_src != sec_login_auth_src_network) { 
390                 DEBUG(0,("DCE context has no network credentials.\n"));
391         }
392
393         sec_login_set_context(my_dce_sec_context, &err);
394         if (err != error_status_ok) {  
395                 dce_error_inq_text(err, dce_errstr, &err2);
396                 DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n",
397                          user,dce_errstr));
398                 
399                 sec_login_purge_context(&my_dce_sec_context, &err);
400                 goto err;
401         }
402         
403         sec_login_get_pwent(my_dce_sec_context, 
404                             (sec_login_passwd_t*)&pw, &err);
405         if (err != error_status_ok) {
406                 dce_error_inq_text(err, dce_errstr, &err2);
407                 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
408                 goto err;
409         }
410         
411         DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
412                  user, getpid()));
413         
414         DEBUG(3,("DCE principal: %s\n"
415                  "          uid: %d\n"
416                  "          gid: %d\n",
417                  pw->pw_name, pw->pw_uid, pw->pw_gid));
418         DEBUG(3,("         info: %s\n"
419                  "          dir: %s\n"
420                  "        shell: %s\n",
421                  pw->pw_gecos, pw->pw_dir, pw->pw_shell));
422         
423         sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
424         if (err != error_status_ok) {
425                 dce_error_inq_text(err, dce_errstr, &err2);
426                 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
427                 goto err;
428         }
429         
430         set_effective_uid(0);
431         set_effective_gid(0);
432         
433         DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time))));
434         
435         dcelogin_atmost_once = 1;
436         return (True);
437
438 err:
439
440         /* Go back to root, JRA. */
441         set_effective_uid(0);
442         set_effective_gid(egid);
443         return(False);
444 }
445
446 void dfs_unlogin(void)
447 {
448         error_status_t err;
449         int err2;
450         unsigned char dce_errstr[dce_c_error_string_len];
451         
452         sec_login_purge_context(&my_dce_sec_context, &err);
453         if (err != error_status_ok) {  
454                 dce_error_inq_text(err, dce_errstr, &err2);
455                 DEBUG(0,("DCE purge login context failed for server instance %d: %s\n",
456                          getpid(), dce_errstr));
457         }
458 }
459 #endif
460
461 #ifdef KRB5_AUTH
462
463 #include <krb5.h>
464
465 /*******************************************************************
466 check on Kerberos authentication
467 ********************************************************************/
468 static BOOL krb5_auth(char *user,char *password)
469 {
470         krb5_data tgtname = {
471                 0,
472                 KRB5_TGS_NAME_SIZE,
473                 KRB5_TGS_NAME
474         };
475         krb5_context kcontext;
476         krb5_principal kprinc;
477         krb5_principal server;
478         krb5_creds kcreds;
479         int options = 0;
480         krb5_address **addrs = (krb5_address **)0;
481         krb5_preauthtype *preauth = NULL;
482         krb5_keytab keytab = NULL;
483         krb5_timestamp now;
484         krb5_ccache ccache = NULL;
485         int retval;
486         char *name;
487
488         if (retval=krb5_init_context(&kcontext)) {
489                 return(False);
490         }
491
492         if (retval = krb5_timeofday(kcontext, &now)) {
493                 return(False);
494         }
495
496         if (retval = krb5_cc_default(kcontext, &ccache)) {
497                 return(False);
498         }
499         
500         if (retval = krb5_parse_name(kcontext, user, &kprinc)) {
501                 return(False);
502         }
503
504         ZERO_STRUCT(kcreds);
505
506         kcreds.client = kprinc;
507         
508         if ((retval = krb5_build_principal_ext(kcontext, &server,
509                 krb5_princ_realm(kcontext, kprinc)->length,
510                 krb5_princ_realm(kcontext, kprinc)->data,
511                 tgtname.length,
512                 tgtname.data,
513                 krb5_princ_realm(kcontext, kprinc)->length,
514                 krb5_princ_realm(kcontext, kprinc)->data,
515                 0))) {
516                 return(False);
517         }
518
519         kcreds.server = server;
520
521         retval = krb5_get_in_tkt_with_password(kcontext,
522                                                options,
523                                                addrs,
524                                                NULL,
525                                                preauth,
526                                                password,
527                                                0,
528                                                &kcreds,
529                                                0);
530
531         if (retval) {
532                 return(False);
533         }
534
535         return(True);
536 }
537 #endif /* KRB5_AUTH */
538
539 #ifdef KRB4_AUTH
540 #include <krb.h>
541
542 /*******************************************************************
543 check on Kerberos authentication
544 ********************************************************************/
545 static BOOL krb4_auth(char *user,char *password)
546 {
547         char realm[REALM_SZ];
548         char tkfile[MAXPATHLEN];
549   
550         if (krb_get_lrealm(realm, 1) != KSUCCESS) {
551                 (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
552         }
553
554         (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d", 
555                         (int)getpid());
556   
557         krb_set_tkt_string(tkfile);
558         if (krb_verify_user(user, "", realm,
559                             password, 0,
560                             "rmcd") == KSUCCESS) {
561                 unlink(tkfile);
562                 return 1;
563         }
564         unlink(tkfile);
565         return 0;
566 }
567 #endif /* KRB4_AUTH */
568
569 #ifdef LINUX_BIGCRYPT
570 /****************************************************************************
571 an enhanced crypt for Linux to handle password longer than 8 characters
572 ****************************************************************************/
573 static int linux_bigcrypt(char *password,char *salt1, char *crypted)
574 {
575 #define LINUX_PASSWORD_SEG_CHARS 8
576         char salt[3];
577         int i;
578   
579         StrnCpy(salt,salt1,2);
580         crypted +=2;
581   
582         for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
583                 char * p = crypt(password,salt) + 2;
584                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
585                         return(0);
586                 password += LINUX_PASSWORD_SEG_CHARS;
587                 crypted  += strlen(p);
588         }
589   
590         return(1);
591 }
592 #endif
593
594 #ifdef OSF1_ENH_SEC
595 /****************************************************************************
596 an enhanced crypt for OSF1
597 ****************************************************************************/
598 static char *osf1_bigcrypt(char *password,char *salt1)
599 {
600         static char result[AUTH_MAX_PASSWD_LENGTH] = "";
601         char *p1;
602         char *p2=password;
603         char salt[3];
604         int i;
605         int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
606         if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) {
607                 parts++;
608         }
609         
610         StrnCpy(salt,salt1,2);
611         StrnCpy(result,salt1,2);
612         result[2]='\0';
613
614         for (i=0; i<parts;i++) {
615                 p1 = crypt(p2,salt);
616                 strncat(result,p1+2,AUTH_MAX_PASSWD_LENGTH-strlen(p1+2)-1);
617                 StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
618                 p2 += AUTH_CLEARTEXT_SEG_CHARS;
619         }
620
621         return(result);
622 }
623 #endif
624
625
626 /****************************************************************************
627 apply a function to upper/lower case combinations
628 of a string and return true if one of them returns true.
629 try all combinations with N uppercase letters.
630 offset is the first char to try and change (start with 0)
631 it assumes the string starts lowercased
632 ****************************************************************************/
633 static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(char *),int N)
634 {
635         int len = strlen(s);
636         int i;
637
638 #ifdef PASSWORD_LENGTH
639         len = MIN(len,PASSWORD_LENGTH);
640 #endif
641
642         if (N <= 0 || offset >= len) {
643                 return(fn(s));
644         }
645
646         for (i=offset;i<(len-(N-1));i++) {      
647                 char c = s[i];
648                 if (!islower(c)) continue;
649                 s[i] = toupper(c);
650                 if (string_combinations2(s,i+1,fn,N-1))
651                         return(True);
652                 s[i] = c;
653         }
654         return(False);
655 }
656
657 /****************************************************************************
658 apply a function to upper/lower case combinations
659 of a string and return true if one of them returns true.
660 try all combinations with up to N uppercase letters.
661 offset is the first char to try and change (start with 0)
662 it assumes the string starts lowercased
663 ****************************************************************************/
664 static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N)
665 {
666         int n;
667         for (n=1;n<=N;n++)
668                 if (string_combinations2(s,0,fn,n)) return(True);
669         return(False);
670 }
671
672
673 /****************************************************************************
674 core of password checking routine
675 ****************************************************************************/
676 static BOOL password_check(char *password)
677 {
678
679 #ifdef WITH_PAM
680         /* This falls through if the password check fails
681            - if HAVE_CRYPT is not defined this causes an error msg
682            saying Warning - no crypt available
683            - if HAVE_CRYPT is defined this is a potential security hole
684            as it may authenticate via the crypt call when PAM
685            settings say it should fail.
686            if (pam_auth(user,password)) return(True);
687            Hence we make a direct return to avoid a second chance!!!
688         */
689         return (pam_auth(this_user,password));
690 #endif /* WITH_PAM */
691         
692 #ifdef WITH_AFS
693         if (afs_auth(this_user,password)) return(True);
694 #endif /* WITH_AFS */
695         
696 #ifdef WITH_DFS
697         if (dfs_auth(this_user,password)) return(True);
698 #endif /* WITH_DFS */
699
700 #ifdef KRB5_AUTH
701         if (krb5_auth(this_user,password)) return(True);
702 #endif /* KRB5_AUTH */
703
704 #ifdef KRB4_AUTH
705         if (krb4_auth(this_user,password)) return(True);
706 #endif /* KRB4_AUTH */
707
708 #ifdef OSF1_ENH_SEC
709         {
710           BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
711           if(!ret) {
712                   DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
713                   ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
714           }
715           return ret;
716         }
717 #endif /* OSF1_ENH_SEC */
718
719 #ifdef ULTRIX_AUTH
720         return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
721 #endif /* ULTRIX_AUTH */
722
723 #ifdef LINUX_BIGCRYPT
724         return(linux_bigcrypt(password,this_salt,this_crypted));
725 #endif /* LINUX_BIGCRYPT */
726
727 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
728
729         /*
730          * Some systems have bigcrypt in the C library but might not
731          * actually use it for the password hashes (HPUX 10.20) is
732          * a noteable example. So we try bigcrypt first, followed
733          * by crypt.
734          */
735
736         if(strcmp(bigcrypt(password,this_salt),this_crypted) == 0)
737                 return True;
738         else 
739                 return (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
740 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
741
742 #ifdef HAVE_BIGCRYPT
743         return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
744 #endif /* HAVE_BIGCRYPT */
745
746 #ifndef HAVE_CRYPT
747         DEBUG(1,("Warning - no crypt available\n"));
748         return(False);
749 #else /* HAVE_CRYPT */
750         return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
751 #endif /* HAVE_CRYPT */
752 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
753 }
754
755
756
757 /****************************************************************************
758 check if a username/password is OK
759 the function pointer fn() points to a function to call when a successful
760 match is found and is used to update the encrypted password file 
761 return True on correct match, False otherwise
762 ****************************************************************************/
763 BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd,
764                 BOOL (*fn)(char *, char *))
765 {
766         pstring pass2;
767         int level = lp_passwordlevel();
768         struct passwd *pass;
769
770         if (password) password[pwlen] = 0;
771
772 #if DEBUG_PASSWORD
773         DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
774 #endif
775
776         if (!password) {
777                 return(False);
778         }
779
780         if (((!*password) || (!pwlen)) && !lp_null_passwords()) {
781                 return(False);
782         }
783
784         if (pwd && !user) {
785                 pass = (struct passwd *) pwd;
786                 user = pass->pw_name;
787         } else {
788                 pass = Get_Pwnam(user,True);
789         }
790
791
792         DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
793
794         if (!pass) {
795                 DEBUG(3,("Couldn't find user %s\n",user));
796                 return(False);
797         }
798
799 #ifdef HAVE_GETSPNAM
800         {
801                 struct spwd *spass;
802
803                 /* many shadow systems require you to be root to get
804                    the password, in most cases this should already be
805                    the case when this function is called, except
806                    perhaps for IPC password changing requests */
807
808                 spass = getspnam(pass->pw_name);
809                 if (spass && spass->sp_pwdp) {
810                         pstrcpy(pass->pw_passwd,spass->sp_pwdp);
811                 }
812         }
813 #elif defined(IA_UINFO)
814         {
815                 /* Need to get password with SVR4.2's ia_ functions
816                    instead of get{sp,pw}ent functions. Required by
817                    UnixWare 2.x, tested on version
818                    2.1. (tangent@cyberport.com) */
819                 uinfo_t uinfo;
820                 if (ia_openinfo(pass->pw_name, &uinfo) != -1) {
821                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
822                 }
823         }
824 #endif
825
826 #ifdef HAVE_GETPRPWNAM
827         {
828                 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
829                 if (pr_pw && pr_pw->ufld.fd_encrypt)
830                         pstrcpy(pass->pw_passwd,pr_pw->ufld.fd_encrypt);
831         }
832 #endif
833
834 #ifdef OSF1_ENH_SEC
835         {
836                 struct pr_passwd *mypasswd;
837                 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",
838                          user));
839                 mypasswd = getprpwnam (user);
840                 if (mypasswd) { 
841                         fstrcpy(pass->pw_name,mypasswd->ufld.fd_name);
842                         fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
843                 } else {
844                         DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
845                                  user));
846                 }
847         }
848 #endif
849
850 #ifdef ULTRIX_AUTH
851         {
852                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
853                 if (ap) {
854                         fstrcpy(pass->pw_passwd, ap->a_password);
855                         endauthent();
856                 }
857         }
858 #endif
859
860         /* extract relevant info */
861         fstrcpy(this_user,pass->pw_name);  
862         fstrcpy(this_salt,pass->pw_passwd);
863
864 #if defined(HAVE_TRUNCATED_SALT)
865         /* crypt on some platforms (HPUX in particular)
866            won't work with more than 2 salt characters. */
867         this_salt[2] = 0;
868 #endif
869         
870         fstrcpy(this_crypted,pass->pw_passwd);
871         
872         if (!*this_crypted) {
873                 if (!lp_null_passwords()) {
874                         DEBUG(2,("Disallowing %s with null password\n",
875                                  this_user));
876                         return(False);
877                 }
878                 if (!*password) {
879                         DEBUG(3,("Allowing access to %s with null password\n",
880                                  this_user));
881                         return(True);
882                 }
883         }
884
885         /* try it as it came to us */
886         if (password_check(password)) {
887                 if (fn) fn(user,password);
888                 return(True);
889         }
890
891         /* if the password was given to us with mixed case then we don't
892            need to proceed as we know it hasn't been case modified by the
893            client */
894         if (strhasupper(password) && strhaslower(password)) {
895                 return(False);
896         }
897
898         /* make a copy of it */
899         StrnCpy(pass2,password,sizeof(pstring)-1);
900   
901         /* try all lowercase */
902         strlower(password);
903         if (password_check(password)) {
904                 if (fn) fn(user,password);
905                 return(True);
906         }
907
908         /* give up? */
909         if (level < 1) {
910
911                 /* restore it */
912                 fstrcpy(password,pass2);
913                 
914                 return(False);
915         }
916
917         /* last chance - all combinations of up to level chars upper! */
918         strlower(password);
919
920         if (string_combinations(password,password_check,level)) {
921                 if (fn) fn(user,password);
922                 return(True);
923         }
924
925         /* restore it */
926         fstrcpy(password,pass2);
927   
928         return(False);
929 }