split the system password checking routines out of smbd/password.c and
[Samba.git] / source3 / passdb / pass_check.c
blobb5aa832f48be7ac966b8d6f645e5c3118926c10c
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 Password checking
5 Copyright (C) Andrew Tridgell 1992-1998
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.
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.
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.
22 /* this module is for checking a username/password against a system
23 password database. The SMB encrypted password support is elsewhere */
25 #include "includes.h"
27 extern int DEBUGLEVEL;
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]="";
35 /****************************************************************************
36 update the enhanced security database. Only relevant for OSF1 at the moment.
37 ****************************************************************************/
38 static void update_protected_database(char *user, BOOL result)
40 #ifdef OSF1_ENH_SEC
41 struct pr_passwd *mypasswd;
42 time_t starttime;
44 mypasswd = getprpwnam (user);
45 starttime = time (NULL);
47 if (result) {
48 mypasswd->ufld.fd_slogin = starttime;
49 mypasswd->ufld.fd_nlogins = 0;
51 putprpwnam(user,mypasswd);
52 } else {
53 mypasswd->ufld.fd_ulogin = starttime;
54 mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1;
55 if (mypasswd->ufld.fd_max_tries != 0 &&
56 mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries) {
57 mypasswd->uflg.fg_lock = 0;
58 DEBUG(3,("Account %s is disabled\n", user));
60 putprpwnam(user ,mypasswd);
62 #endif
66 #ifdef HAVE_PAM
67 /*******************************************************************
68 check on PAM authentication
69 ********************************************************************/
71 /* We first need some helper functions */
72 #include <security/pam_appl.h>
73 /* Static variables used to communicate between the conversation function
74 * and the server_login function
76 static char *PAM_username;
77 static char *PAM_password;
79 /* PAM conversation function
80 * Here we assume (for now, at least) that echo on means login name, and
81 * echo off means password.
83 static int PAM_conv (int num_msg,
84 const struct pam_message **msg,
85 struct pam_response **resp,
86 void *appdata_ptr) {
87 int replies = 0;
88 struct pam_response *reply = NULL;
90 #define COPY_STRING(s) (s) ? strdup(s) : NULL
92 reply = malloc(sizeof(struct pam_response) * num_msg);
93 if (!reply) return PAM_CONV_ERR;
95 for (replies = 0; replies < num_msg; replies++) {
96 switch (msg[replies]->msg_style) {
97 case PAM_PROMPT_ECHO_ON:
98 reply[replies].resp_retcode = PAM_SUCCESS;
99 reply[replies].resp = COPY_STRING(PAM_username);
100 /* PAM frees resp */
101 break;
102 case PAM_PROMPT_ECHO_OFF:
103 reply[replies].resp_retcode = PAM_SUCCESS;
104 reply[replies].resp = COPY_STRING(PAM_password);
105 /* PAM frees resp */
106 break;
107 case PAM_TEXT_INFO:
108 /* fall through */
109 case PAM_ERROR_MSG:
110 /* ignore it... */
111 reply[replies].resp_retcode = PAM_SUCCESS;
112 reply[replies].resp = NULL;
113 break;
114 default:
115 /* Must be an error of some sort... */
116 free (reply);
117 return PAM_CONV_ERR;
120 if (reply) *resp = reply;
121 return PAM_SUCCESS;
123 static struct pam_conv PAM_conversation = {
124 &PAM_conv,
125 NULL
129 static BOOL pam_auth(char *this_user,char *password)
131 pam_handle_t *pamh;
132 int pam_error;
134 /* Now use PAM to do authentication. For now, we won't worry about
135 * session logging, only authentication. Bail out if there are any
136 * errors. Since this is a limited protocol, and an even more limited
137 * function within a server speaking this protocol, we can't be as
138 * verbose as would otherwise make sense.
139 * Query: should we be using PAM_SILENT to shut PAM up?
141 #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
142 pam_end(pamh, 0); return False; \
144 PAM_password = password;
145 PAM_username = this_user;
146 pam_error = pam_start("samba", this_user, &PAM_conversation, &pamh);
147 PAM_BAIL;
148 /* Setting PAM_SILENT stops generation of error messages to syslog
149 * to enable debugging on Red Hat Linux set:
150 * /etc/pam.d/samba:
151 * auth required /lib/security/pam_pwdb.so nullok shadow audit
152 * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging)
154 pam_error = pam_authenticate(pamh, PAM_SILENT);
155 PAM_BAIL;
156 /* It is not clear to me that account management is the right thing
157 * to do, but it is not clear that it isn't, either. This can be
158 * removed if no account management should be done. Alternately,
159 * put a pam_allow.so entry in /etc/pam.conf for account handling. */
160 pam_error = pam_acct_mgmt(pamh, PAM_SILENT);
161 PAM_BAIL;
162 pam_end(pamh, PAM_SUCCESS);
163 /* If this point is reached, the user has been authenticated. */
164 return(True);
166 #endif
169 #ifdef WITH_AFS
170 /*******************************************************************
171 check on AFS authentication
172 ********************************************************************/
173 static BOOL afs_auth(char *this_user,char *password)
175 long password_expires = 0;
176 char *reason;
178 /* For versions of AFS prior to 3.3, this routine has few arguments, */
179 /* but since I can't find the old documentation... :-) */
180 setpag();
181 if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
182 this_user,
183 (char *) 0, /* instance */
184 (char *) 0, /* cell */
185 password,
186 0, /* lifetime, default */
187 &password_expires, /*days 'til it expires */
188 0, /* spare 2 */
189 &reason) == 0) {
190 return(True);
192 return(False);
194 #endif
197 #ifdef WITH_DFS
199 /*****************************************************************
200 This new version of the DFS_AUTH code was donated by Karsten Muuss
201 <muuss@or.uni-bonn.de>. It fixes the following problems with the
202 old code :
204 - Server credentials may expire
205 - Client credential cache files have wrong owner
206 - purge_context() function is called with invalid argument
208 This new code was modified to ensure that on exit the uid/gid is
209 still root, and the original directory is restored. JRA.
210 ******************************************************************/
212 sec_login_handle_t my_dce_sec_context;
213 int dcelogin_atmost_once = 0;
215 /*******************************************************************
216 check on a DCE/DFS authentication
217 ********************************************************************/
218 static BOOL dfs_auth(char *this_user,char *password)
220 error_status_t err;
221 int err2;
222 int prterr;
223 signed32 expire_time, current_time;
224 boolean32 password_reset;
225 struct passwd *pw;
226 sec_passwd_rec_t passwd_rec;
227 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
228 unsigned char dce_errstr[dce_c_error_string_len];
230 if (dcelogin_atmost_once) return(False);
232 #ifdef HAVE_CRYPT
234 * We only go for a DCE login context if the given password
235 * matches that stored in the local password file..
236 * Assumes local passwd file is kept in sync w/ DCE RGY!
239 if (strcmp((char *)crypt(password,this_salt),this_crypted)) {
240 return(False);
242 #endif
244 sec_login_get_current_context(&my_dce_sec_context, &err);
245 if (err != error_status_ok ) {
246 dce_error_inq_text(err, dce_errstr, &err2);
247 DEBUG(0,("DCE can't get current context. %s\n", dce_errstr));
249 return(False);
252 sec_login_certify_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 get current context. %s\n", dce_errstr));
257 return(False);
260 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
261 if (err != error_status_ok) {
262 dce_error_inq_text(err, dce_errstr, &err2);
263 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
265 return(False);
268 time(&current_time);
270 if (expire_time < (current_time + 60)) {
271 struct passwd *pw;
272 sec_passwd_rec_t *key;
274 sec_login_get_pwent(my_dce_sec_context,
275 (sec_login_passwd_t*)&pw, &err);
276 if (err != error_status_ok ) {
277 dce_error_inq_text(err, dce_errstr, &err2);
278 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
280 return(False);
283 sec_login_refresh_identity(my_dce_sec_context, &err);
284 if (err != error_status_ok) {
285 dce_error_inq_text(err, dce_errstr, &err2);
286 DEBUG(0,("DCE can't refresh identity. %s\n",
287 dce_errstr));
289 return(False);
292 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
293 (unsigned char *)pw->pw_name,
294 sec_c_key_version_none,
295 (void**)&key, &err);
296 if (err != error_status_ok) {
297 dce_error_inq_text(err, dce_errstr, &err2);
298 DEBUG(0,("DCE can't get key for %s. %s\n",
299 pw->pw_name, dce_errstr));
301 return(False);
304 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
305 &password_reset, &auth_src,
306 &err);
307 if (err != error_status_ok ) {
308 dce_error_inq_text(err, dce_errstr, &err2);
309 DEBUG(0,("DCE can't validate and certify identity for %s. %s\n",
310 pw->pw_name, dce_errstr));
313 sec_key_mgmt_free_key(key, &err);
314 if (err != error_status_ok ) {
315 dce_error_inq_text(err, dce_errstr, &err2);
316 DEBUG(0,("DCE can't free key.\n", dce_errstr));
320 if (sec_login_setup_identity((unsigned char *)this_user,
321 sec_login_no_flags,
322 &my_dce_sec_context,
323 &err) == 0) {
324 dce_error_inq_text(err, dce_errstr, &err2);
325 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
326 this_user,dce_errstr));
327 return(False);
330 sec_login_get_pwent(my_dce_sec_context,
331 (sec_login_passwd_t*)&pw, &err);
332 if (err != error_status_ok) {
333 dce_error_inq_text(err, dce_errstr, &err2);
334 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
336 return(False);
339 sec_login_purge_context(&my_dce_sec_context, &err);
340 if (err != error_status_ok) {
341 dce_error_inq_text(err, dce_errstr, &err2);
342 DEBUG(0,("DCE can't purge context. %s\n", dce_errstr));
344 return(False);
348 * NB. I'd like to change these to call something like become_user()
349 * instead but currently we don't have a connection
350 * context to become the correct user. This is already
351 * fairly platform specific code however, so I think
352 * this should be ok. I have added code to go
353 * back to being root on error though. JRA.
356 if (setregid(-1, pw->pw_gid) != 0) {
357 DEBUG(0,("Can't set egid to %d (%s)\n",
358 pw->pw_gid, strerror(errno)));
359 return False;
362 if (setreuid(-1, pw->pw_uid) != 0) {
363 setgid(0);
364 DEBUG(0,("Can't set euid to %d (%s)\n",
365 pw->pw_uid, strerror(errno)));
366 return False;
369 if (sec_login_setup_identity((unsigned char *)this_user,
370 sec_login_no_flags,
371 &my_dce_sec_context,
372 &err) == 0) {
373 dce_error_inq_text(err, dce_errstr, &err2);
374 /* Go back to root, JRA. */
375 setuid(0);
376 setgid(0);
377 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
378 this_user,dce_errstr));
379 return(False);
382 sec_login_get_pwent(my_dce_sec_context,
383 (sec_login_passwd_t*)&pw, &err);
384 if (err != error_status_ok ) {
385 dce_error_inq_text(err, dce_errstr, &err2);
386 /* Go back to root, JRA. */
387 setuid(0);
388 setgid(0);
389 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
391 return(False);
394 passwd_rec.version_number = sec_passwd_c_version_none;
395 passwd_rec.pepper = NULL;
396 passwd_rec.key.key_type = sec_passwd_plain;
397 passwd_rec.key.tagged_union.plain = (idl_char *)password;
399 sec_login_validate_identity(my_dce_sec_context,
400 &passwd_rec, &password_reset,
401 &auth_src, &err);
402 if (err != error_status_ok ) {
403 dce_error_inq_text(err, dce_errstr, &err2);
404 /* Go back to root, JRA. */
405 setuid(0);
406 setgid(0);
407 DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
408 this_user,dce_errstr));
410 return(False);
413 sec_login_certify_identity(my_dce_sec_context, &err);
414 if (err != error_status_ok) {
415 dce_error_inq_text(err, dce_errstr, &err2);
416 /* Go back to root, JRA. */
417 setuid(0);
418 setgid(0);
419 DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr));
421 return(False);
424 if (auth_src != sec_login_auth_src_network) {
425 DEBUG(0,("DCE context has no network credentials.\n"));
428 sec_login_set_context(my_dce_sec_context, &err);
429 if (err != error_status_ok) {
430 dce_error_inq_text(err, dce_errstr, &err2);
431 DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n",
432 this_user,dce_errstr));
434 sec_login_purge_context(&my_dce_sec_context, &err);
435 /* Go back to root, JRA. */
436 setuid(0);
437 setgid(0);
438 return(False);
441 sec_login_get_pwent(my_dce_sec_context,
442 (sec_login_passwd_t*)&pw, &err);
443 if (err != error_status_ok) {
444 dce_error_inq_text(err, dce_errstr, &err2);
445 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
447 /* Go back to root, JRA. */
448 setuid(0);
449 setgid(0);
450 return(False);
453 DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
454 this_user, getpid()));
456 DEBUG(3,("DCE principal: %s\n"
457 " uid: %d\n"
458 " gid: %d\n",
459 pw->pw_name, pw->pw_uid, pw->pw_gid));
460 DEBUG(3,(" info: %s\n"
461 " dir: %s\n"
462 " shell: %s\n",
463 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
465 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
466 if (err != error_status_ok) {
467 dce_error_inq_text(err, dce_errstr, &err2);
468 /* Go back to root, JRA. */
469 setuid(0);
470 setgid(0);
471 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
473 return(False);
476 setuid(0);
477 setgid(0);
479 DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time))));
481 dcelogin_atmost_once = 1;
482 return (True);
485 void dfs_unlogin(void)
487 error_status_t err;
488 int err2;
489 unsigned char dce_errstr[dce_c_error_string_len];
491 sec_login_purge_context(&my_dce_sec_context, &err);
492 if (err != error_status_ok) {
493 dce_error_inq_text(err, dce_errstr, &err2);
494 DEBUG(0,("DCE purge login context failed for server instance %d: %s\n",
495 getpid(), dce_errstr));
498 #endif
500 #ifdef KRB5_AUTH
501 /*******************************************************************
502 check on Kerberos authentication
503 ********************************************************************/
504 static BOOL krb5_auth(char *this_user,char *password)
506 krb5_data tgtname = {
508 KRB5_TGS_NAME_SIZE,
509 KRB5_TGS_NAME
511 krb5_context kcontext;
512 krb5_principal kprinc;
513 krb5_principal server;
514 krb5_creds kcreds;
515 int options = 0;
516 krb5_address **addrs = (krb5_address **)0;
517 krb5_preauthtype *preauth = NULL;
518 krb5_keytab keytab = NULL;
519 krb5_timestamp now;
520 krb5_ccache ccache = NULL;
521 int retval;
522 char *name;
524 if (retval=krb5_init_context(&kcontext)) {
525 return(False);
528 if (retval = krb5_timeofday(kcontext, &now)) {
529 return(False);
532 if (retval = krb5_cc_default(kcontext, &ccache)) {
533 return(False);
536 if (retval = krb5_parse_name(kcontext, this_user, &kprinc)) {
537 return(False);
540 memset((char *)&kcreds, 0, sizeof(kcreds));
542 kcreds.client = kprinc;
544 if ((retval = krb5_build_principal_ext(kcontext, &server,
545 krb5_princ_realm(kcontext, kprinc)->length,
546 krb5_princ_realm(kcontext, kprinc)->data,
547 tgtname.length,
548 tgtname.data,
549 krb5_princ_realm(kcontext, kprinc)->length,
550 krb5_princ_realm(kcontext, kprinc)->data,
551 0))) {
552 return(False);
555 kcreds.server = server;
557 retval = krb5_get_in_tkt_with_password(kcontext,
558 options,
559 addrs,
560 NULL,
561 preauth,
562 password,
564 &kcreds,
567 if (retval) {
568 return(False);
571 return(True);
573 #endif /* KRB5_AUTH */
575 #ifdef KRB4_AUTH
576 /*******************************************************************
577 check on Kerberos authentication
578 ********************************************************************/
579 static BOOL krb4_auth(char *this_user,char *password)
581 char realm[REALM_SZ];
582 char tkfile[MAXPATHLEN];
584 if (krb_get_lrealm(realm, 1) != KSUCCESS) {
585 (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
588 (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
589 getpid());
591 krb_set_tkt_string(tkfile);
592 if (krb_verify_user(this_user, "", realm,
593 password, 0,
594 "rmcd") == KSUCCESS) {
595 unlink(tkfile);
596 return 1;
598 unlink(tkfile);
599 return 0;
601 #endif /* KRB4_AUTH */
603 #ifdef LINUX_BIGCRYPT
604 /****************************************************************************
605 an enhanced crypt for Linux to handle password longer than 8 characters
606 ****************************************************************************/
607 static int linux_bigcrypt(char *password,char *salt1, char *crypted)
609 #define LINUX_PASSWORD_SEG_CHARS 8
610 char salt[3];
611 int i;
613 StrnCpy(salt,salt1,2);
614 crypted +=2;
616 for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
617 char * p = crypt(password,salt) + 2;
618 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
619 return(0);
620 password += LINUX_PASSWORD_SEG_CHARS;
621 crypted += strlen(p);
624 return(1);
626 #endif
628 #ifdef OSF1_ENH_SEC
629 /****************************************************************************
630 an enhanced crypt for OSF1
631 ****************************************************************************/
632 static char *osf1_bigcrypt(char *password,char *salt1)
634 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
635 char *p1;
636 char *p2=password;
637 char salt[3];
638 int i;
639 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
640 if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) {
641 parts++;
644 StrnCpy(salt,salt1,2);
645 StrnCpy(result,salt1,2);
647 for (i=0; i<parts;i++) {
648 p1 = crypt(p2,salt);
649 strncat(result,p1+2,AUTH_MAX_PASSWD_LENGTH-strlen(p1+2)-1);
650 StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
651 p2 += AUTH_CLEARTEXT_SEG_CHARS;
654 return(result);
656 #endif
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 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_combinations2(char *s,int offset,BOOL (*fn)(char *),int N)
668 int len = strlen(s);
669 int i;
671 #ifdef PASSWORD_LENGTH
672 len = MIN(len,PASSWORD_LENGTH);
673 #endif
675 if (N <= 0 || offset >= len) {
676 return(fn(s));
679 for (i=offset;i<(len-(N-1));i++) {
680 char c = s[i];
681 if (!islower(c)) continue;
682 s[i] = toupper(c);
683 if (string_combinations2(s,i+1,fn,N-1))
684 return(True);
685 s[i] = c;
687 return(False);
690 /****************************************************************************
691 apply a function to upper/lower case combinations
692 of a string and return true if one of them returns true.
693 try all combinations with up to N uppercase letters.
694 offset is the first char to try and change (start with 0)
695 it assumes the string starts lowercased
696 ****************************************************************************/
697 static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N)
699 int n;
700 for (n=1;n<=N;n++)
701 if (string_combinations2(s,0,fn,n)) return(True);
702 return(False);
706 /****************************************************************************
707 core of password checking routine
708 ****************************************************************************/
709 static BOOL password_check(char *password)
712 #ifdef HAVE_PAM
713 /* This falls through if the password check fails
714 - if HAVE_CRYPT is not defined this causes an error msg
715 saying Warning - no crypt available
716 - if HAVE_CRYPT is defined this is a potential security hole
717 as it may authenticate via the crypt call when PAM
718 settings say it should fail.
719 if (pam_auth(this_user,password)) return(True);
720 Hence we make a direct return to avoid a second chance!!!
722 return (pam_auth(this_user,password));
723 #endif
725 #ifdef WITH_AFS
726 if (afs_auth(this_user,password)) return(True);
727 #endif
729 #ifdef WITH_DFS
730 if (dfs_auth(this_user,password)) return(True);
731 #endif
733 #ifdef KRB5_AUTH
734 if (krb5_auth(this_user,password)) return(True);
735 #endif
737 #ifdef KRB4_AUTH
738 if (krb4_auth(this_user,password)) return(True);
739 #endif
741 #ifdef OSF1_ENH_SEC
743 BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
744 if(!ret) {
745 DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
746 ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
748 return ret;
750 #endif
752 #ifdef ULTRIX_AUTH
753 return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
754 #endif
756 #ifdef LINUX_BIGCRYPT
757 return(linux_bigcrypt(password,this_salt,this_crypted));
758 #endif
760 #ifdef HAVE_BIGCRYPT
761 return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
762 #endif
764 #ifndef HAVE_CRYPT
765 DEBUG(1,("Warning - no crypt available\n"));
766 return(False);
767 #else
768 return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
769 #endif
774 /****************************************************************************
775 check if a username/password is OK
776 the function pointer fn() points to a function to call when a successful
777 match is found and is used to update the encrypted password file
778 return True on correct match, False otherwise
779 ****************************************************************************/
780 BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd,
781 BOOL (*fn)(char *, char *))
783 pstring pass2;
784 int level = lp_passwordlevel();
785 struct passwd *pass;
787 if (password) password[pwlen] = 0;
789 #if DEBUG_PASSWORD
790 DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
791 #endif
793 if (!password) {
794 return(False);
797 if (((!*password) || (!pwlen)) && !lp_null_passwords()) {
798 return(False);
801 if (pwd && !user) {
802 pass = (struct passwd *) pwd;
803 user = pass->pw_name;
804 } else {
805 pass = Get_Pwnam(user,True);
809 DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
811 if (!pass) {
812 DEBUG(3,("Couldn't find user %s\n",user));
813 return(False);
816 #ifdef HAVE_GETSPNAM
818 struct spwd *spass;
820 /* many shadow systems require you to be root to get
821 the password, in most cases this should already be
822 the case when this function is called, except
823 perhaps for IPC password changing requests */
825 spass = getspnam(pass->pw_name);
826 if (spass && spass->sp_pwdp) {
827 pass->pw_passwd = spass->sp_pwdp;
830 #elif defined(IA_UINFO)
832 /* Need to get password with SVR4.2's ia_ functions
833 instead of get{sp,pw}ent functions. Required by
834 UnixWare 2.x, tested on version
835 2.1. (tangent@cyberport.com) */
836 uinfo_t uinfo;
837 if (ia_openinfo(pass->pw_name, &uinfo) != -1) {
838 ia_get_logpwd(uinfo, &(pass->pw_passwd));
841 #endif
843 #ifdef HAVE_GETPRPWNAM
845 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
846 if (pr_pw && pr_pw->ufld.fd_encrypt)
847 pass->pw_passwd = pr_pw->ufld.fd_encrypt;
849 #endif
851 #ifdef OSF1_ENH_SEC
853 struct pr_passwd *mypasswd;
854 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",
855 user));
856 mypasswd = getprpwnam (user);
857 if (mypasswd) {
858 fstrcpy(pass->pw_name,mypasswd->ufld.fd_name);
859 fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
860 } else {
861 DEBUG(5,("No entry for user %s in protected database !\n",
862 user));
863 return(False);
866 #endif
868 #ifdef ULTRIX_AUTH
870 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
871 if (ap) {
872 fstrcpy(pass->pw_passwd, ap->a_password);
873 endauthent();
876 #endif
878 /* extract relevant info */
879 fstrcpy(this_user,pass->pw_name);
880 fstrcpy(this_salt,pass->pw_passwd);
881 /* crypt on some platforms (HPUX in particular)
882 won't work with more than 2 salt characters. */
883 this_salt[2] = 0;
885 fstrcpy(this_crypted,pass->pw_passwd);
887 if (!*this_crypted) {
888 if (!lp_null_passwords()) {
889 DEBUG(2,("Disallowing %s with null password\n",
890 this_user));
891 return(False);
893 if (!*password) {
894 DEBUG(3,("Allowing access to %s with null password\n",
895 this_user));
896 return(True);
900 /* try it as it came to us */
901 if (password_check(password)) {
902 update_protected_database(user,True);
903 if (fn) fn(user,password);
904 return(True);
907 /* if the password was given to us with mixed case then we don't
908 need to proceed as we know it hasn't been case modified by the
909 client */
910 if (strhasupper(password) && strhaslower(password)) {
911 return(False);
914 /* make a copy of it */
915 StrnCpy(pass2,password,sizeof(pstring)-1);
917 /* try all lowercase */
918 strlower(password);
919 if (password_check(password)) {
920 update_protected_database(user,True);
921 if (fn) fn(user,password);
922 return(True);
925 /* give up? */
926 if (level < 1) {
927 update_protected_database(user,False);
929 /* restore it */
930 fstrcpy(password,pass2);
932 return(False);
935 /* last chance - all combinations of up to level chars upper! */
936 strlower(password);
938 if (string_combinations(password,password_check,level)) {
939 update_protected_database(user,True);
940 if (fn) fn(user,password);
941 return(True);
944 update_protected_database(user,False);
946 /* restore it */
947 fstrcpy(password,pass2);
949 return(False);