2 Unix SMB/Netbios implementation.
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 */
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]="";
36 /*******************************************************************
37 check on PAM authentication
38 ********************************************************************/
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
45 static char *PAM_username;
46 static char *PAM_password;
48 /* PAM conversation function
49 * Here we assume (for now, at least) that echo on means login name, and
50 * echo off means password.
52 static int PAM_conv (int num_msg,
53 const struct pam_message **msg,
54 struct pam_response **resp,
57 struct pam_response *reply = NULL;
59 #define COPY_STRING(s) (s) ? strdup(s) : NULL
61 reply = malloc(sizeof(struct pam_response) * num_msg);
62 if (!reply) return PAM_CONV_ERR;
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);
71 case PAM_PROMPT_ECHO_OFF:
72 reply[replies].resp_retcode = PAM_SUCCESS;
73 reply[replies].resp = COPY_STRING(PAM_password);
80 reply[replies].resp_retcode = PAM_SUCCESS;
81 reply[replies].resp = NULL;
84 /* Must be an error of some sort... */
89 if (reply) *resp = reply;
92 static struct pam_conv PAM_conversation = {
98 static BOOL pam_auth(char *user,char *password)
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?
110 #define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
111 pam_end(pamh, 0); return False; \
113 PAM_password = password;
115 pam_error = pam_start("samba", user, &PAM_conversation, &pamh);
117 /* Setting PAM_SILENT stops generation of error messages to syslog
118 * to enable debugging on Red Hat Linux set:
120 * auth required /lib/security/pam_pwdb.so nullok shadow audit
121 * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging)
123 pam_error = pam_authenticate(pamh, PAM_SILENT);
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);
131 pam_end(pamh, PAM_SUCCESS);
132 /* If this point is reached, the user has been authenticated. */
139 /*******************************************************************
140 check on AFS authentication
141 ********************************************************************/
142 static BOOL afs_auth(char *user,char *password)
144 long password_expires = 0;
147 /* For versions of AFS prior to 3.3, this routine has few arguments, */
148 /* but since I can't find the old documentation... :-) */
150 if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
152 (char *) 0, /* instance */
153 (char *) 0, /* cell */
155 0, /* lifetime, default */
156 &password_expires, /*days 'til it expires */
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
173 - Server credentials may expire
174 - Client credential cache files have wrong owner
175 - purge_context() function is called with invalid argument
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 ******************************************************************/
181 sec_login_handle_t my_dce_sec_context;
182 int dcelogin_atmost_once = 0;
184 /*******************************************************************
185 check on a DCE/DFS authentication
186 ********************************************************************/
187 static BOOL dfs_auth(char *user,char *password)
192 signed32 expire_time, current_time;
193 boolean32 password_reset;
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];
199 if (dcelogin_atmost_once) return(False);
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!
208 if (strcmp((char *)crypt(password,this_salt),this_crypted)) {
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));
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));
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));
239 if (expire_time < (current_time + 60)) {
241 sec_passwd_rec_t *key;
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));
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",
261 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
262 (unsigned char *)pw->pw_name,
263 sec_c_key_version_none,
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));
273 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
274 &password_reset, &auth_src,
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));
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));
289 if (sec_login_setup_identity((unsigned char *)user,
293 dce_error_inq_text(err, dce_errstr, &err2);
294 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
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));
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));
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.
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)));
331 if (setreuid(-1, pw->pw_uid) != 0) {
333 DEBUG(0,("Can't set euid to %d (%s)\n",
334 pw->pw_uid, strerror(errno)));
338 if (sec_login_setup_identity((unsigned char *)user,
342 dce_error_inq_text(err, dce_errstr, &err2);
343 /* Go back to root, JRA. */
346 DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
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. */
358 DEBUG(0,("DCE can't get pwent. %s\n", dce_errstr));
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;
368 sec_login_validate_identity(my_dce_sec_context,
369 &passwd_rec, &password_reset,
371 if (err != error_status_ok ) {
372 dce_error_inq_text(err, dce_errstr, &err2);
373 /* Go back to root, JRA. */
376 DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
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. */
388 DEBUG(0,("DCE certify identity failed: %s\n", dce_errstr));
393 if (auth_src != sec_login_auth_src_network) {
394 DEBUG(0,("DCE context has no network credentials.\n"));
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",
403 sec_login_purge_context(&my_dce_sec_context, &err);
404 /* Go back to root, JRA. */
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));
416 /* Go back to root, JRA. */
422 DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
425 DEBUG(3,("DCE principal: %s\n"
428 pw->pw_name, pw->pw_uid, pw->pw_gid));
429 DEBUG(3,(" info: %s\n"
432 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
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. */
440 DEBUG(0,("DCE can't get expiration. %s\n", dce_errstr));
448 DEBUG(0,("DCE context expires: %s",asctime(localtime(&expire_time))));
450 dcelogin_atmost_once = 1;
454 void dfs_unlogin(void)
458 unsigned char dce_errstr[dce_c_error_string_len];
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));
470 /*******************************************************************
471 check on Kerberos authentication
472 ********************************************************************/
473 static BOOL krb5_auth(char *user,char *password)
475 krb5_data tgtname = {
480 krb5_context kcontext;
481 krb5_principal kprinc;
482 krb5_principal server;
485 krb5_address **addrs = (krb5_address **)0;
486 krb5_preauthtype *preauth = NULL;
487 krb5_keytab keytab = NULL;
489 krb5_ccache ccache = NULL;
493 if (retval=krb5_init_context(&kcontext)) {
497 if (retval = krb5_timeofday(kcontext, &now)) {
501 if (retval = krb5_cc_default(kcontext, &ccache)) {
505 if (retval = krb5_parse_name(kcontext, user, &kprinc)) {
511 kcreds.client = kprinc;
513 if ((retval = krb5_build_principal_ext(kcontext, &server,
514 krb5_princ_realm(kcontext, kprinc)->length,
515 krb5_princ_realm(kcontext, kprinc)->data,
518 krb5_princ_realm(kcontext, kprinc)->length,
519 krb5_princ_realm(kcontext, kprinc)->data,
524 kcreds.server = server;
526 retval = krb5_get_in_tkt_with_password(kcontext,
542 #endif /* KRB5_AUTH */
547 /*******************************************************************
548 check on Kerberos authentication
549 ********************************************************************/
550 static BOOL krb4_auth(char *user,char *password)
552 char realm[REALM_SZ];
553 char tkfile[MAXPATHLEN];
555 if (krb_get_lrealm(realm, 1) != KSUCCESS) {
556 (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
559 (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
562 krb_set_tkt_string(tkfile);
563 if (krb_verify_user(user, "", realm,
565 "rmcd") == KSUCCESS) {
572 #endif /* KRB4_AUTH */
574 #ifdef LINUX_BIGCRYPT
575 /****************************************************************************
576 an enhanced crypt for Linux to handle password longer than 8 characters
577 ****************************************************************************/
578 static int linux_bigcrypt(char *password,char *salt1, char *crypted)
580 #define LINUX_PASSWORD_SEG_CHARS 8
584 StrnCpy(salt,salt1,2);
587 for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
588 char * p = crypt(password,salt) + 2;
589 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
591 password += LINUX_PASSWORD_SEG_CHARS;
592 crypted += strlen(p);
600 /****************************************************************************
601 an enhanced crypt for OSF1
602 ****************************************************************************/
603 static char *osf1_bigcrypt(char *password,char *salt1)
605 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
610 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
611 if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) {
615 StrnCpy(salt,salt1,2);
616 StrnCpy(result,salt1,2);
618 for (i=0; i<parts;i++) {
620 strncat(result,p1+2,AUTH_MAX_PASSWD_LENGTH-strlen(p1+2)-1);
621 StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
622 p2 += AUTH_CLEARTEXT_SEG_CHARS;
630 /****************************************************************************
631 apply a function to upper/lower case combinations
632 of a string and return true if one of them returns true.
633 try all combinations with N uppercase letters.
634 offset is the first char to try and change (start with 0)
635 it assumes the string starts lowercased
636 ****************************************************************************/
637 static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(char *),int N)
642 #ifdef PASSWORD_LENGTH
643 len = MIN(len,PASSWORD_LENGTH);
646 if (N <= 0 || offset >= len) {
650 for (i=offset;i<(len-(N-1));i++) {
652 if (!islower(c)) continue;
654 if (string_combinations2(s,i+1,fn,N-1))
661 /****************************************************************************
662 apply a function to upper/lower case combinations
663 of a string and return true if one of them returns true.
664 try all combinations with up to N uppercase letters.
665 offset is the first char to try and change (start with 0)
666 it assumes the string starts lowercased
667 ****************************************************************************/
668 static BOOL string_combinations(char *s,BOOL (*fn)(char *),int N)
672 if (string_combinations2(s,0,fn,n)) return(True);
677 /****************************************************************************
678 core of password checking routine
679 ****************************************************************************/
680 static BOOL password_check(char *password)
684 /* This falls through if the password check fails
685 - if HAVE_CRYPT is not defined this causes an error msg
686 saying Warning - no crypt available
687 - if HAVE_CRYPT is defined this is a potential security hole
688 as it may authenticate via the crypt call when PAM
689 settings say it should fail.
690 if (pam_auth(user,password)) return(True);
691 Hence we make a direct return to avoid a second chance!!!
693 return (pam_auth(this_user,password));
697 if (afs_auth(this_user,password)) return(True);
701 if (dfs_auth(this_user,password)) return(True);
705 if (krb5_auth(this_user,password)) return(True);
709 if (krb4_auth(this_user,password)) return(True);
714 BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
716 DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
717 ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
724 return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
727 #ifdef LINUX_BIGCRYPT
728 return(linux_bigcrypt(password,this_salt,this_crypted));
732 return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
736 DEBUG(1,("Warning - no crypt available\n"));
739 return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
745 /****************************************************************************
746 check if a username/password is OK
747 the function pointer fn() points to a function to call when a successful
748 match is found and is used to update the encrypted password file
749 return True on correct match, False otherwise
750 ****************************************************************************/
751 BOOL pass_check(char *user,char *password, int pwlen, struct passwd *pwd,
752 BOOL (*fn)(char *, char *))
755 int level = lp_passwordlevel();
756 const struct passwd *pass;
758 if (password) password[pwlen] = 0;
761 DEBUG(100,("checking user=[%s] pass=",user));
762 dump_data(100, password, strlen(password));
769 if (((!*password) || (!pwlen)) && !lp_null_passwords()) {
774 pass = (struct passwd *) pwd;
775 user = pass->pw_name;
777 pass = Get_Pwnam(user,True);
781 DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
784 DEBUG(3,("Couldn't find user %s\n",user));
788 /* extract relevant info */
789 fstrcpy(this_user,pass->pw_name);
790 fstrcpy(this_salt,pass->pw_passwd);
791 /* crypt on some platforms (HPUX in particular)
792 won't work with more than 2 salt characters. */
795 fstrcpy(this_crypted,pass->pw_passwd);
797 if (!*this_crypted) {
798 if (!lp_null_passwords()) {
799 DEBUG(2,("Disallowing %s with null password\n",
804 DEBUG(3,("Allowing access to %s with null password\n",
810 /* try it as it came to us */
811 if (password_check(password)) {
812 if (fn) fn(user,password);
816 /* if the password was given to us with mixed case then we don't
817 need to proceed as we know it hasn't been case modified by the
819 if (strhasupper(password) && strhaslower(password)) {
823 /* make a copy of it */
824 StrnCpy(pass2,password,sizeof(pstring)-1);
826 /* try all lowercase */
828 if (password_check(password)) {
829 if (fn) fn(user,password);
837 fstrcpy(password,pass2);
842 /* last chance - all combinations of up to level chars upper! */
845 if (string_combinations(password,password_check,level)) {
846 if (fn) fn(user,password);
851 fstrcpy(password,pass2);