/* 
 *  Unix SMB/Netbios implementation.
 *  Version 1.9.
 *  RPC Pipe client / server routines
 *  Copyright (C) Andrew Tridgell              1992-1999,
 *  Copyright (C) Luke Kenneth Casson Leighton 1996-1999,
 *  Copyright (C) Paul Ashton                  1997-1999.
 *  
 *  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, Cambgrpsge, MA 02139, USA.
 */


#include "includes.h"

extern int DEBUGLEVEL;


/*******************************************************************
makes a CREDS_UNIX structure.
********************************************************************/
BOOL make_creds_unix(CREDS_UNIX *r_u, const char* user_name,
				const char* requested_name,
				const char* real_name,
				BOOL guest)
{
	if (r_u == NULL) return False;

	DEBUG(5,("make_creds_unix\n"));

	fstrcpy(r_u->user_name     , user_name);
	fstrcpy(r_u->requested_name, requested_name);
	fstrcpy(r_u->real_name     , real_name);
	r_u->guest = guest;

	return True;
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_unix(char *desc, CREDS_UNIX *r_u, prs_struct *ps, int depth)
{
	if (r_u == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_unix");
	depth++;

	prs_align(ps);
	prs_string("user_name", ps, depth,   r_u->user_name, strlen(r_u->user_name), sizeof(r_u->user_name));
	prs_align(ps);
	prs_string("requested_name", ps, depth,   r_u->requested_name, strlen(r_u->requested_name), sizeof(r_u->requested_name));
	prs_align(ps);
	prs_string("real_name", ps, depth,   r_u->real_name, strlen(r_u->real_name), sizeof(r_u->real_name));
	prs_align(ps);
	prs_uint32("guest", ps, depth, (uint32 *)&(r_u->guest));
	return True;
}


/*******************************************************************
frees a structure.
********************************************************************/
void creds_free_unix(CREDS_UNIX *r_u)
{
}

/*******************************************************************
makes a CREDS_UNIX_SEC structure.
********************************************************************/
BOOL make_creds_unix_sec(CREDS_UNIX_SEC *r_u,
		uint32 uid, uint32 gid, uint32 num_grps, gid_t *grps)
{
	int i;
	if (r_u == NULL) return False;

	DEBUG(5,("make_creds_unix_sec\n"));

	r_u->uid      = uid;
	r_u->gid      = gid;
	r_u->num_grps = num_grps;
	r_u->grps = (uint32*)Realloc(NULL, sizeof(r_u->grps[0]) *
				       r_u->num_grps);
	if (r_u->grps == NULL && num_grps != 0)
	{
		return False;
	}
	for (i = 0; i < num_grps; i++)
	{
		r_u->grps[i] = (gid_t)grps[i];
	}

	return True;
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_unix_sec(char *desc, CREDS_UNIX_SEC *r_u, prs_struct *ps, int depth)
{
	uint32 i;

	if (r_u == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_unix_sec");
	depth++;

	prs_align(ps);

	prs_uint32("uid", ps, depth, &(r_u->uid));
	prs_uint32("gid", ps, depth, &(r_u->gid));
	prs_uint32("num_grps", ps, depth, (uint32 *)&(r_u->num_grps));
	if (r_u->num_grps != 0)
	{
		r_u->grps = (uint32*)Realloc(r_u->grps,
				       sizeof(r_u->grps[0]) *
				       r_u->num_grps);
		if (r_u->grps == NULL)
		{
			creds_free_unix_sec(r_u);
			return False;
		}
	}
	for (i = 0; i < r_u->num_grps; i++)
	{
		prs_uint32("", ps, depth, &(r_u->grps[i]));
	}
	return True;
}


/*******************************************************************
frees a structure.
********************************************************************/
void creds_free_unix_sec(CREDS_UNIX_SEC *r_u)
{
	if (r_u->grps != NULL)
	{
		free(r_u->grps);
		r_u->grps = NULL;
	}
}

/*******************************************************************
makes a CREDS_NT_SEC structure.
********************************************************************/
BOOL make_creds_nt_sec(CREDS_NT_SEC *r_u,
		DOM_SID *sid, uint32 num_grps, uint32 *grps)
{
	int i;
	if (r_u == NULL) return False;

	DEBUG(5,("make_creds_unix_sec\n"));

	sid_copy(&r_u->sid, sid);
	r_u->num_grps = num_grps;
	r_u->grp_rids = (uint32*)Realloc(NULL, sizeof(r_u->grp_rids[0]) *
				       r_u->num_grps);

	if (r_u->grp_rids == NULL && num_grps != 0)
	{
		return False;
	}
	for (i = 0; i < num_grps; i++)
	{
		r_u->grp_rids[i] = grps[i];
	}

	return True;
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_nt_sec(char *desc, CREDS_NT_SEC *r_u, prs_struct *ps, int depth)
{
	int i;
	if (r_u == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_nt");
	depth++;

	prs_align(ps);

	smb_io_dom_sid ("sid", &r_u->sid, ps, depth);
	prs_align(ps);

	prs_uint32("num_grps", ps, depth, &(r_u->num_grps));
	if (r_u->num_grps != 0)
	{
		r_u->grp_rids = (uint32*)Realloc(r_u->grp_rids,
				       sizeof(r_u->grp_rids[0]) *
				       r_u->num_grps);
		if (r_u->grp_rids == NULL)
		{
			creds_free_nt_sec(r_u);
			return False;
		}
	}
	for (i = 0; i < r_u->num_grps; i++)
	{
		prs_uint32("", ps, depth, &(r_u->grp_rids[i]));
	}

	return True;
}

/*******************************************************************
frees a structure.
********************************************************************/
void creds_free_nt_sec(CREDS_NT_SEC *r_u)
{
	if (r_u->grp_rids != NULL)
	{
		free(r_u->grp_rids);
		r_u->grp_rids = NULL;
	}
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_pwd_info(char *desc, struct pwd_info *pwd, prs_struct *ps, int depth)
{
	if (pwd == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_pwd_info");
	depth++;

	prs_align(ps);

	prs_uint32("nullpwd", ps, depth, (uint32 *)&(pwd->null_pwd));
	if (pwd->null_pwd)
	{
		return True;
	}
	
	prs_uint32("cleartext", ps, depth, (uint32 *)&(pwd->cleartext));
	if (pwd->cleartext)
	{
		prs_string("password", ps, depth,   pwd->password, strlen(pwd->password), sizeof(pwd->password));
		prs_align(ps);
		return True;
	}
	prs_uint32("crypted", ps, depth, (uint32 *)&(pwd->crypted));
		
	prs_uint8s(False, "smb_lm_pwd", ps, depth, (unsigned char*)&pwd->smb_lm_pwd, sizeof(pwd->smb_lm_pwd));
	prs_align(ps);
	prs_uint8s(False, "smb_nt_pwd", ps, depth, (unsigned char*)&pwd->smb_nt_pwd, sizeof(pwd->smb_nt_pwd));
	prs_align(ps);

	prs_uint8s(False, "smb_lm_owf", ps, depth, (unsigned char*)&pwd->smb_lm_owf, sizeof(pwd->smb_lm_owf));
	prs_align(ps);
	prs_uint32("nt_owf_len", ps, depth, &(pwd->nt_owf_len));
	if (pwd->nt_owf_len > sizeof(pwd->smb_nt_owf))
	{
		return False;
	}
	prs_uint8s(False, "smb_nt_owf", ps, depth, (unsigned char*)&pwd->smb_nt_owf, pwd->nt_owf_len);
	prs_align(ps);

	prs_uint8s(False, "lm_cli_chal", ps, depth, (unsigned char*)&pwd->lm_cli_chal, sizeof(pwd->lm_cli_chal));
	prs_align(ps);
	prs_uint32("nt_cli_chal_len", ps, depth, &(pwd->nt_cli_chal_len));

	if (pwd->nt_cli_chal_len > sizeof(pwd->nt_cli_chal))
	{
		return False;
	}
	prs_uint8s(False, "nt_cli_chal", ps, depth, (unsigned char*)&pwd->nt_cli_chal, pwd->nt_cli_chal_len);
	prs_align(ps);

	return True;
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_nt(char *desc, CREDS_NT *r_u, prs_struct *ps, int depth)
{
	if (r_u == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_nt");
	depth++;

	prs_align(ps);

	/* lkclXXXX CHEAT!!!!!!!! */
	prs_string("user_name", ps, depth,   r_u->user_name, strlen(r_u->user_name), sizeof(r_u->user_name));
	prs_align(ps);
	prs_string("domain", ps, depth,   r_u->domain, strlen(r_u->domain), sizeof(r_u->domain));
	prs_align(ps);

	creds_io_pwd_info("pwd", &r_u->pwd, ps, depth);
	prs_align(ps);

	prs_uint32("ntlmssp", ps, depth, &(r_u->ntlmssp_flags));

	return True;
}

/*******************************************************************
frees a structure.
********************************************************************/
void creds_free_nt(CREDS_NT *r_u)
{
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_hybrid(char *desc, CREDS_HYBRID *r_u, prs_struct *ps, int depth)
{
	if (r_u == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_hybrid");
	depth++;

	prs_align(ps);

	prs_uint32("reuse", ps, depth, (uint32 *)&(r_u->reuse));

	prs_uint32("ptr_ntc", ps, depth, &(r_u->ptr_ntc));
	prs_uint32("ptr_uxc", ps, depth, &(r_u->ptr_uxc));
	prs_uint32("ptr_nts", ps, depth, &(r_u->ptr_nts));
	prs_uint32("ptr_uxs", ps, depth, &(r_u->ptr_uxs));
	prs_uint32("ptr_ssk", ps, depth, &(r_u->ptr_ssk));
	if (r_u->ptr_ntc != 0)
	{
		if (!creds_io_nt  ("ntc", &r_u->ntc, ps, depth)) return False;
	}
	if (r_u->ptr_uxc != 0)
	{
		if (!creds_io_unix("uxc", &r_u->uxc, ps, depth)) return False;
	}
	if (r_u->ptr_nts != 0)
	{
		if (!creds_io_nt_sec  ("nts", &r_u->nts, ps, depth)) return False;
	}
	if (r_u->ptr_uxs != 0)
	{
		if (!creds_io_unix_sec("uxs", &r_u->uxs, ps, depth)) return False;
	}
	if (r_u->ptr_ssk != 0)
	{
		prs_uint8s(False, "usr_sess_key", ps, depth, (unsigned char*)&r_u->usr_sess_key, sizeof(r_u->usr_sess_key));
	}
	else
	{
		memset(r_u->usr_sess_key, 0, sizeof(r_u->usr_sess_key));
	}
	return True;
}

void copy_unix_creds(CREDS_UNIX *to, const CREDS_UNIX *from)
{
	if (from == NULL)
	{
		to->user_name[0] = 0;
		return;
	}
	fstrcpy(to->user_name, from->user_name);
}

void copy_nt_sec_creds(CREDS_NT_SEC *to, const CREDS_NT_SEC *from)
{
	if (from == NULL)
	{
		ZERO_STRUCTP(to);
		return;
	}
	sid_copy(&to->sid, &from->sid);
	to->num_grps = 0;
	to->grp_rids = NULL;

	if (from->num_grps != 0)
	{
		size_t size = from->num_grps * sizeof(from->grp_rids[0]);
		to->grp_rids = (uint32*)malloc(size);
		if (to->grp_rids == NULL)
		{
			return;
		}
		to->num_grps = from->num_grps;
		memcpy(to->grp_rids, from->grp_rids, size);
	}
}

void copy_unix_sec_creds(CREDS_UNIX_SEC *to, const CREDS_UNIX_SEC *from)
{
	if (from == NULL)
	{
		to->uid = -1;
		to->gid = -1;
		to->num_grps = 0;
		to->grps = NULL;
		return;
	}
	to->uid = from->uid;
	to->gid = from->gid;
	to->num_grps = 0;
	to->grps = NULL;

	if (from->num_grps != 0)
	{
		size_t size = from->num_grps * sizeof(from->grps[0]);
		to->grps = (uint32*)malloc(size);
		if (to->grps == NULL)
		{
			return;
		}
		to->num_grps = from->num_grps;
		memcpy(to->grps, from->grps, size);
	}
}

void create_ntc_from_cli_state (CREDS_NT *to, const struct cli_state *cli_from)
{
	/* 
	 * NULL credentials -- 
	 * if this gets executed, it is a programming error.
         * fall through to copy_nt_creds() 
         */
        if (cli_from == NULL)
        {
		copy_nt_creds (to, NULL);
                return;
        }

        safe_strcpy(to->domain   , cli_from->domain   , sizeof(cli_from->domain   )-1);
        safe_strcpy(to->user_name, cli_from->user_name, sizeof(cli_from->user_name)-1);
        memcpy(&to->pwd, &cli_from->pwd, sizeof(cli_from->pwd));
        to->ntlmssp_flags = cli_from->ntlmssp_flags;
        DEBUG(10,("create_ntc_fromcli_state: user %s domain %s flgs: %x\n",
               to->user_name, to->domain,
               to->ntlmssp_flags));

}


void copy_nt_creds(struct ntuser_creds *to,
				const struct ntuser_creds *from)
{
	if (from == NULL)
	{
		DEBUG(10,("copy_nt_creds: null creds\n"));
		to->domain[0] = 0;
		to->user_name[0] = 0;
		pwd_set_nullpwd(&to->pwd);
		to->ntlmssp_flags = 0;

		return;
	}
	safe_strcpy(to->domain   , from->domain   , sizeof(from->domain   )-1);
	safe_strcpy(to->user_name, from->user_name, sizeof(from->user_name)-1);
	memcpy(&to->pwd, &from->pwd, sizeof(from->pwd));
	to->ntlmssp_flags = from->ntlmssp_flags;
	DEBUG(10,("copy_nt_creds: user %s domain %s flgs: %x\n",
	       to->user_name, to->domain, 
	       to->ntlmssp_flags));
}

void copy_user_creds(struct user_creds *to,
				const struct user_creds *from)
{
	ZERO_STRUCTP(to);
	if (from == NULL)
	{
		to->ptr_ntc = 0;
		to->ptr_uxc = 0;
		to->ptr_nts = 0;
		to->ptr_uxs = 0;
		to->ptr_ssk = 0;
		copy_nt_creds(&to->ntc, NULL);
		copy_unix_creds(&to->uxc, NULL);
		copy_nt_sec_creds(&to->nts, NULL);
		copy_unix_sec_creds(&to->uxs, NULL);
		to->reuse = False;
		return;
	}

	to->reuse = from->reuse;

	to->ptr_nts = from->ptr_nts;
	to->ptr_uxs = from->ptr_uxs;
	to->ptr_ntc = from->ptr_ntc;
	to->ptr_uxc = from->ptr_uxc;
	to->ptr_ssk = from->ptr_ssk;

	if (to->ptr_ntc != 0)
	{
		copy_nt_creds(&to->ntc, &from->ntc);
	}
	if (to->ptr_uxc != 0)
	{
		copy_unix_creds(&to->uxc, &from->uxc);
	}
	if (to->ptr_nts != 0)
	{
		copy_nt_sec_creds(&to->nts, &from->nts);
	}
	if (to->ptr_uxs != 0)
	{
		copy_unix_sec_creds(&to->uxs, &from->uxs);
	}
	if (to->ptr_ssk != 0)
	{
		memcpy(to->usr_sess_key, from->usr_sess_key,
		        sizeof(to->usr_sess_key));
	}
}

void free_user_creds(struct user_creds *creds)
{
	creds_free_unix(&creds->uxc);
	creds_free_nt  (&creds->ntc);
	creds_free_unix_sec(&creds->uxs);
	creds_free_nt_sec  (&creds->nts);
}

/*******************************************************************
reads or writes a structure.
********************************************************************/
BOOL creds_io_cmd(char *desc, CREDS_CMD *r_u, prs_struct *ps, int depth)
{
	if (r_u == NULL) return False;

	prs_debug(ps, depth, desc, "creds_io_cmd");
	depth++;

	prs_align(ps);

	prs_uint16("version", ps, depth, &(r_u->version));
	prs_uint16("command", ps, depth, &(r_u->command));
	prs_uint32("pid    ", ps, depth, &(r_u->pid    ));

	prs_string("name   ", ps, depth,   r_u->name, strlen(r_u->name), sizeof(r_u->name));
	prs_align(ps);
	
	prs_uint32("ptr_creds", ps, depth, &(r_u->ptr_creds));
	if (r_u->ptr_creds != 0)
	{
		if (!creds_io_hybrid("creds", r_u->cred, ps, depth))
		{
			return False;
		}
	}


	return True;
}


BOOL create_ntuser_creds( prs_struct *ps,
				const char* name, 
				uint16 version, uint16 command,
				uint32 pid,
				const struct ntuser_creds *ntu,
				BOOL reuse)
{
	CREDS_CMD cmd;
	struct user_creds usr;

	ZERO_STRUCT(cmd);
	ZERO_STRUCT(usr);

	DEBUG(10,("create_user_creds: %s %d %d\n",
		name, version, command));

	usr.reuse = reuse;

	fstrcpy(cmd.name, name);
	cmd.version = version;
	cmd.command = command;
	cmd.pid   = pid  ;
	cmd.ptr_creds = ntu != NULL ? 1 : 0;
	cmd.cred = &usr;

	if (ntu != NULL)
	{
		copy_nt_creds(&usr.ntc, ntu);
		usr.ptr_ntc = 1;
	}
	else
	{
		usr.ptr_ntc = 0;
	}
		
	prs_init(ps, 1024, NULL, MARSHALL);

	ps->data_offset = 4;
	return creds_io_cmd("creds", &cmd, ps, 0);
}

BOOL create_user_creds( prs_struct *ps,
				const char* name, 
				uint16 version, uint16 command,
				uint32 pid,
				struct user_creds *usr)
{
	CREDS_CMD cmd;

	ZERO_STRUCT(cmd);

	DEBUG(10,("create_user_creds: %s %d %d\n",
		name, version, command));

	fstrcpy(cmd.name, name);
	cmd.version = version;
	cmd.command = command;
	cmd.pid     = pid  ;
	cmd.ptr_creds = usr != NULL ? 1 : 0;
	cmd.cred = usr;

	prs_init(ps, 1024, NULL, MARSHALL);

	ps->data_offset = 4;
	return creds_io_cmd("creds", &cmd, ps, 0);
}
