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 */
545 /*******************************************************************
546 check on Kerberos authentication
547 ********************************************************************/
548 static BOOL krb4_auth(char *user,char *password)
550 char realm[REALM_SZ];
551 char tkfile[MAXPATHLEN];
553 if (krb_get_lrealm(realm, 1) != KSUCCESS) {
554 (void) safe_strcpy(realm, KRB_REALM, sizeof (realm) - 1);
557 (void) slprintf(tkfile, sizeof(tkfile) - 1, "/tmp/samba_tkt_%d",
560 krb_set_tkt_string(tkfile);
561 if (krb_verify_user(user, "", realm,
563 "rmcd") == KSUCCESS) {
570 #endif /* KRB4_AUTH */
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)
578 #define LINUX_PASSWORD_SEG_CHARS 8
582 StrnCpy(salt,salt1,2);
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)
589 password += LINUX_PASSWORD_SEG_CHARS;
590 crypted += strlen(p);
598 /****************************************************************************
599 an enhanced crypt for OSF1
600 ****************************************************************************/
601 static char *osf1_bigcrypt(char *password,char *salt1)
603 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
608 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
609 if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) {
613 StrnCpy(salt,salt1,2);
614 StrnCpy(result,salt1,2);
616 for (i=0; i<parts;i++) {
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;
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)
640 #ifdef PASSWORD_LENGTH
641 len = MIN(len,PASSWORD_LENGTH);
644 if (N <= 0 || offset >= len) {
648 for (i=offset;i<(len-(N-1));i++) {
650 if (!islower(c)) continue;
652 if (string_combinations2(s,i+1,fn,N-1))
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)
670 if (string_combinations2(s,0,fn,n)) return(True);
675 /****************************************************************************
676 core of password checking routine
677 ****************************************************************************/
678 static BOOL password_check(char *password)
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!!!
691 return (pam_auth(this_user,password));
695 if (afs_auth(this_user,password)) return(True);
699 if (dfs_auth(this_user,password)) return(True);
703 if (krb5_auth(this_user,password)) return(True);
707 if (krb4_auth(this_user,password)) return(True);
712 BOOL ret = (strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
714 DEBUG(2,("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
715 ret = (strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
722 return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
725 #ifdef LINUX_BIGCRYPT
726 return(linux_bigcrypt(password,this_salt,this_crypted));
730 return(strcmp(bigcrypt(password,this_salt),this_crypted) == 0);
734 DEBUG(1,("Warning - no crypt available\n"));
737 return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
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 *))
753 int level = lp_passwordlevel();
756 if (password) password[pwlen] = 0;
759 DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
766 if (((!*password) || (!pwlen)) && !lp_null_passwords()) {
771 pass = (struct passwd *) pwd;
772 user = pass->pw_name;
774 pass = Get_Pwnam(user,True);
778 DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
781 DEBUG(3,("Couldn't find user %s\n",user));
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 */
794 spass = getspnam(pass->pw_name);
795 if (spass && spass->sp_pwdp) {
796 pass->pw_passwd = spass->sp_pwdp;
799 #elif defined(IA_UINFO)
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) */
806 if (ia_openinfo(pass->pw_name, &uinfo) != -1) {
807 ia_get_logpwd(uinfo, &(pass->pw_passwd));
812 #ifdef HAVE_GETPRPWNAM
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;
822 struct pr_passwd *mypasswd;
823 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",
825 mypasswd = getprpwnam (user);
827 fstrcpy(pass->pw_name,mypasswd->ufld.fd_name);
828 fstrcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
830 DEBUG(5,("No entry for user %s in protected database !\n",
839 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
841 fstrcpy(pass->pw_passwd, ap->a_password);
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. */
854 fstrcpy(this_crypted,pass->pw_passwd);
856 if (!*this_crypted) {
857 if (!lp_null_passwords()) {
858 DEBUG(2,("Disallowing %s with null password\n",
863 DEBUG(3,("Allowing access to %s with null password\n",
869 /* try it as it came to us */
870 if (password_check(password)) {
871 if (fn) fn(user,password);
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
878 if (strhasupper(password) && strhaslower(password)) {
882 /* make a copy of it */
883 StrnCpy(pass2,password,sizeof(pstring)-1);
885 /* try all lowercase */
887 if (password_check(password)) {
888 if (fn) fn(user,password);
896 fstrcpy(password,pass2);
901 /* last chance - all combinations of up to level chars upper! */
904 if (string_combinations(password,password_check,level)) {
905 if (fn) fn(user,password);
910 fstrcpy(password,pass2);