/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   change a password in a local smbpasswd file
   Copyright (C) Andrew Tridgell 1998
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"


/*************************************************************
add a new user to the local smbpasswd file
*************************************************************/

static BOOL add_new_user(char *user_name, uid_t uid, int local_flags,
			 uchar *new_p16, uchar *new_nt_p16)
{
	struct smb_passwd new_smb_pwent;

	/* Create a new smb passwd entry and set it to the given password. */
	new_smb_pwent.smb_userid = uid;
	new_smb_pwent.smb_name = user_name; 
	new_smb_pwent.smb_passwd = NULL;
	new_smb_pwent.smb_nt_passwd = NULL;
	new_smb_pwent.pass_last_set_time = time(NULL);
	new_smb_pwent.acct_ctrl = ((local_flags & LOCAL_TRUST_ACCOUNT) ? ACB_WSTRUST : ACB_NORMAL);
	
	if(local_flags & LOCAL_DISABLE_USER) {
		new_smb_pwent.acct_ctrl |= ACB_DISABLED;
	}
	
	if (local_flags & LOCAL_SET_NO_PASSWORD) {
		new_smb_pwent.acct_ctrl |= ACB_PWNOTREQ;
	} else {
		new_smb_pwent.smb_passwd = new_p16;
		new_smb_pwent.smb_nt_passwd = new_nt_p16;
	}

	
	return add_smbpwd_entry(&new_smb_pwent);
}


/*************************************************************
change a password entry in the local smbpasswd file
*************************************************************/

BOOL local_password_change(char *user_name, int local_flags,
			   char *new_passwd, 
			   char *err_str, size_t err_str_len,
			   char *msg_str, size_t msg_str_len)
{
	struct passwd  *pwd = NULL;
	void *vp;
	struct smb_passwd *smb_pwent;
	uchar           new_p16[16];
	uchar           new_nt_p16[16];

	*err_str = '\0';
	*msg_str = '\0';

	if (local_flags & LOCAL_ADD_USER) {
	
		/*
		 * Check for a local account - if we're adding only.
		 */
	
		if(!(pwd = sys_getpwnam(user_name))) {
			slprintf(err_str, err_str_len - 1, "User %s does not \
exist in system password file (usually /etc/passwd). Cannot add \
account without a valid local system user.\n", user_name);
			return False;
		}
	}

	/* Calculate the MD4 hash (NT compatible) of the new password. */
	nt_lm_owf_gen(new_passwd, new_nt_p16, new_p16);

	/*
	 * Open the smbpaswd file.
	 */
	vp = startsmbpwent(True);
	if (!vp && errno == ENOENT) {
		FILE *fp;
		slprintf(msg_str,msg_str_len-1,
			"smbpasswd file did not exist - attempting to create it.\n");
		fp = sys_fopen(lp_smb_passwd_file(), "w");
		if (fp) {
			fprintf(fp, "# Samba SMB password file\n");
			fclose(fp);
			vp = startsmbpwent(True);
		}
	}

	if (!vp) {
		slprintf(err_str, err_str_len-1, "Cannot open file %s. Error was %s\n",
			lp_smb_passwd_file(), strerror(errno) );
		return False;
	}
  
	/* Get the smb passwd entry for this user */
	smb_pwent = getsmbpwnam(user_name);
	if (smb_pwent == NULL) {
		if(!(local_flags & LOCAL_ADD_USER)) {
			slprintf(err_str, err_str_len-1,
				"Failed to find entry for user %s.\n", user_name);
			endsmbpwent(vp);
			return False;
		}

		if (add_new_user(user_name, pwd->pw_uid, local_flags, new_p16, new_nt_p16)) {
			slprintf(msg_str, msg_str_len-1, "Added user %s.\n", user_name);
			endsmbpwent(vp);
			return True;
		} else {
			slprintf(err_str, err_str_len-1, "Failed to add entry for user %s.\n", user_name);
			endsmbpwent(vp);
			return False;
		}
	} else {
		/* the entry already existed */
		local_flags &= ~LOCAL_ADD_USER;
	}

	/*
	 * We are root - just write the new password
	 * and the valid last change time.
	 */

	if(local_flags & LOCAL_DISABLE_USER) {
		smb_pwent->acct_ctrl |= ACB_DISABLED;
	} else if (local_flags & LOCAL_ENABLE_USER) {
		if(smb_pwent->smb_passwd == NULL) {
			smb_pwent->smb_passwd = new_p16;
			smb_pwent->smb_nt_passwd = new_nt_p16;
		}
		smb_pwent->acct_ctrl &= ~ACB_DISABLED;
	} else if (local_flags & LOCAL_SET_NO_PASSWORD) {
		smb_pwent->acct_ctrl |= ACB_PWNOTREQ;
		/* This is needed to preserve ACB_PWNOTREQ in mod_smbfilepwd_entry */
		smb_pwent->smb_passwd = NULL;
		smb_pwent->smb_nt_passwd = NULL;
	} else {
		/*
		 * If we're dealing with setting a completely empty user account
		 * ie. One with a password of 'XXXX', but not set disabled (like
		 * an account created from scratch) then if the old password was
		 * 'XX's then getsmbpwent will have set the ACB_DISABLED flag.
		 * We remove that as we're giving this user their first password
		 * and the decision hasn't really been made to disable them (ie.
		 * don't create them disabled). JRA.
		 */
		if((smb_pwent->smb_passwd == NULL) && (smb_pwent->acct_ctrl & ACB_DISABLED))
			smb_pwent->acct_ctrl &= ~ACB_DISABLED;
		smb_pwent->acct_ctrl &= ~ACB_PWNOTREQ;
		smb_pwent->smb_passwd = new_p16;
		smb_pwent->smb_nt_passwd = new_nt_p16;
	}
	
	if(local_flags & LOCAL_DELETE_USER) {
		if (del_smbpwd_entry(user_name)==False) {
			slprintf(err_str,err_str_len-1, "Failed to delete entry for user %s.\n", user_name);
			endsmbpwent(vp);
			return False;
		}
		slprintf(msg_str, msg_str_len-1, "Deleted user %s.\n", user_name);
	} else {
		if(mod_smbpwd_entry(smb_pwent,True) == False) {
			slprintf(err_str, err_str_len-1, "Failed to modify entry for user %s.\n", user_name);
			endsmbpwent(vp);
			return False;
		}
		if(local_flags & LOCAL_DISABLE_USER)
			slprintf(msg_str, msg_str_len-1, "Disabled user %s.\n", user_name);
		else if (local_flags & LOCAL_ENABLE_USER)
			slprintf(msg_str, msg_str_len-1, "Enabled user %s.\n", user_name);
		else if (local_flags & LOCAL_SET_NO_PASSWORD)
			slprintf(msg_str, msg_str_len-1, "User %s password set to none.\n", user_name);
	}

	endsmbpwent(vp);

	return True;
}
