
/*
    LinuxWare daemon - Netware like server for Linux

    Copyright (C) 1994, 1995  Ales Dryak <e-mail: A.Dryak@sh.cvut.cz>

    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 <stdlib.h> 
#include <errno.h>
#include <unistd.h>
#include <pwd.h>
#include <netinet/in.h>
#include "ipxncp.h"
#include "global.h"
#include "bindery.h"
#include "bindops.h"
#include "util.h"
#include "logging.h"
#include "bindauth.h"

static bind_list this_server={{0x1,NOBODY_GID,BTYPE_SERVER,{"LINUX"},bpass_nologin,BFLAG_STATIC,0,NULL},NULL};

static int cur_uid;
static int cur_gid;

void init_server_object()
{
	this_server.obj.name=server_name;
	LPRINTF(LL_INFO,("server name is %s\n",SERVER_NAME));
	if (ipx_kern_get_internet_addr(&my_addr)<0)
	{
		LPRINTF(LL_FATAL,("get my address: %s\n",ipx_err_string));
		exit(1);
	}
	my_addr.sipx_port=cmdline.ncp_port;
	my_addr.sipx_type=IPX_NCP_PTYPE;
	{
		object_property* my_net_addr=prop_net_address(my_addr.sipx_network,my_addr.sipx_node,my_addr.sipx_port);
		if (my_net_addr==NULL)
		{
			LPRINTF(LL_FATAL,("malloc: %s\n",strerror(errno)));
			exit(1);
		}
		prop_add(&(this_server.obj),my_net_addr);
	}
	LOG_START(LL_INFO)
	log_printf("my address: ");
	ipx_fprint_saddr(log_file,&my_addr);
	log_printf("\n");
	log_printf("server object initialized\n");
	LOG_END
}

void done_server_object()
{
	LPRINTF(LL_INFO,("server object shutdown\n"));
}

static int bpass_user(struct bindery_object* obj,lwpassword* passwd)
{
	FILE* pwd=NULL;
	lwpwent pe;
	int err=BPASS_ERROR;
	int uid;
	int gid;
	
	uid=get_cur_uid();
	gid=get_cur_gid();
	if (!setid(ROOT_UID,ROOT_GID)) goto err_exit;
	pwd=fopen(PASSWD_FILENAME,"rt");
	if (!setid(uid,gid)) goto err_exit;
	
	if (pwd==NULL) {LPRINTF(LL_ERROR,("can't open passwd file %s: %s\n",PASSWD_FILENAME,strerror(errno)));goto err_exit;}
	if (getlwpwnam(pwd,&pe,obj->name.name))
	{
		err=pe.expired?BPASS_NOLOGIN:BPASS_OK;
	}
	else
	{
		err=BPASS_NOLOGIN;
	}
	*passwd=pe.passwd;
	if (fclose(pwd)!=0) {LPRINTF(LL_ERROR,("can't close passwd file %s: %s\n",PASSWD_FILENAME,strerror(errno)));goto err_exit;}
	return err;
err_exit:
	if (pwd!=NULL) fclose(pwd);
	return err;
}

int add_user_passwd(lwpwent* pe)
{
	struct passwd* pent=NULL;
	bind_list* bl=NULL;
	object_property* full_name=NULL;
	struct ncp_object_name sm_name;
	
	LPRINTF(LL_DEBUG,("adding user %s from passwd\n",pe->name.name));
	if (pe->expired)
	{
		LPRINTF(LL_INFO,("user %s has expired account\n",pe->name.name));
	}
	sm_name=pe->name;
	strtolower(sm_name.name);
	pent=getpwnam(sm_name.name);
	if (pent==NULL)
	{
		LPRINTF(LL_ERROR,("add_user_passwd: getpwnam: user=%s error=%s\n",pe->name.name,strerror(errno)));
		goto err_exit;
	}
	bl=bind_alloc();
	if (bl==NULL)
	{
		LPRINTF(LL_ERROR,("add_user_passwd: bind_alloc: out of memory\n"));
		goto err_exit;
	}
	bl->obj.id=pent->pw_uid;
	bl->obj.group_id=pent->pw_gid;
	bl->obj.type=BTYPE_USER;
	bl->obj.name=pe->name; 
	bl->obj.password=bpass_user;
	bl->obj.flag=BFLAG_STATIC;
	full_name=prop_full_name(pent->pw_gecos);
	if (full_name==NULL)
	{
		LPRINTF(LL_ERROR,("add_user_passwd: prop_full_name failed (out of memory?)\n"));
		goto err_exit;
	}
	prop_add(&(bl->obj),full_name);
	bind_add(bl);
	free(pent);
	return 1;
err_exit:	
	if (pent!=NULL) free(pent);
	if (full_name!=NULL) free(full_name);
	if (bl!=NULL) free(bl);
	return 0;
}

int add_users(char* passwdfile)
{
	FILE* pwd;
	lwpwent pe;
	
	pwd=fopen(passwdfile,"rt");
	if (pwd==NULL) {LPRINTF(LL_ERROR,("can't open passwd file %s: %s\n",passwdfile,strerror(errno)));goto err_exit;}

	LPRINTF(LL_INFO,("reading users from %s\n",passwdfile));	
	for(;getlwpwent(pwd,&pe);)
	{
		add_user_passwd(&pe);
	}
	
	if (fclose(pwd)!=0) {LPRINTF(LL_ERROR,("can't close passwd file %s: %s\n",passwdfile,strerror(errno)));goto err_exit;}
	return 1;
err_exit:
	if (pwd!=NULL) fclose(pwd);
	return 0;
}
	
void init_bindery()
{
	time_t t;
	
	add_users(PASSWD_FILENAME);
	bind_add(&this_server);
	time(&t);
	srand(t);
	LPRINTF(LL_INFO,("bindery initialized\n"));
}

void done_bindery()
{
	LPRINTF(LL_INFO,("bindery shutdown\n"));
}

int get_cur_uid()
{
	return cur_uid;
}

int get_cur_gid()
{
	return cur_gid;
}

int setid(int uid,int gid)
{
	if (seteuid(ROOT_UID)!=0)
	{
		LPRINTF(LL_ERROR,("cannot seteuid(%u)\n",ROOT_UID));
		return 0;
	}
	if (setegid(gid)!=0 || getegid()!=gid)
	{
		LPRINTF(LL_ERROR,("cannot setegid(%u)\n",gid));
		return 0;
	}
	if (seteuid(uid)!=0 || geteuid()!=uid)
	{
		LPRINTF(LL_ERROR,("cannot seteuid(%u)\n",uid));
		return 0;
	}
	cur_uid=uid;
	cur_gid=gid;
	return 1;
}
