/*
 * Copyright 1993, 1994 by Ulrich Khn. All rights reserved.
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
 * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
 * RISK.
 */

/*
 * File : auth.c
 *        read and eval the epxorts file
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <types.h>
#include <ctype.h>
#include <stat.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "../xfs/nfs.h"
#include "util.h"
#include "exportent.h"
#include "auth.h"

#ifdef SYSLOG
#include <arpa/inet.h>
#include "syslog.h"
#endif


exportlist *exports = NULL;
accesslist *authlist = NULL;


extern int debug;


void
free_grouplist(groups **pgr)
{
	groups *gr;

	gr = *pgr;
	while (gr)
	{
		*pgr = gr->gr_next;
		free(gr->gr_name);
		free(gr);
		gr = *pgr;
	}
}

void
free_clientlist(client_info **pcl)
{
	client_info *cl;

	cl = *pcl;
	while (cl)
	{
		*pcl = cl->next;
		free(cl->name);
		free(cl);
		cl = *pcl;
	}
}

void
free_old_lists()
{
	exportlist *ex;
	accesslist *ac;

	ac = authlist;
	while (ac)
	{
		authlist = ac->next;
		free_clientlist(&ac->access);
		free_clientlist(&ac->rw_access);
		free_clientlist(&ac->root_access);
		/* do *not* free ac->mountpoint, as this points into the export list */
		free(ac);
		ac = authlist;
	}

	ex = exports;
	while (ex)
	{
		exports = ex->ex_next;
		free(ex->ex_filesys);
		free_grouplist(&ex->ex_groups);
		free(ex);
		ex = exports;
	}

	exports = NULL;
	authlist = NULL;
}


/* Parse a list of clients which are given in the xtab file.
 *
 * BUG: no netgroups supported for now.
 */
void
parse_clientlist(char *list, client_info **pcl)
{
	char *s, *p;
	int n;
	client_info *cl;
	struct hostent *hent;

	s = list;
	*pcl = NULL;
	while (*s && (*s != ',') && !iswhite(*s))
	{
		cl = NULL;
		n = 0;
		p = s;
		while (*s && (*s != ',') && (*s != ':') && !iswhite(*s))
		{
			n++;
			s++;
		}
		if (n)
		{
			cl = malloc(sizeof(client_info));
			if (!cl)
				break;

			cl->name = malloc(n+1);
			if (!cl->name)
			{
				free(cl);
				break;
			}
			strncpy(cl->name, p, n);
			cl->name[n] = '\0';

			/* Now convert the name into an inet address */
			hent = gethostbyname(cl->name);
			if (hent)
				cl->addr = *(struct in_addr*)hent->h_addr_list[0];
			else
				bzero(&cl->addr, sizeof(cl->addr));

			cl->next = *pcl;
			*pcl = cl;
		}

		/* skip name separator, but only that */
		if (*s && (*s == ':'))
			s += 1;
	}
}


int
auth_init()
{
	struct exportent *xent;
	FILE *fp;
	long r;
	struct stat stats;
	char buf[MAXPATHLEN+1];
	char *mountpoint, *p;
	exportlist *exprt;
	accesslist *ac;

	if (exports || authlist)
		free_old_lists();

	fp = setexportent();
	if (!fp)
	{
		return -1;
	}

	while ((xent = getexportent(fp)) != NULL)
	{
		/* convert the path into an internal native format */
		if (path2abs(xent->xent_dirname, buf) != 0)
			break;          /* some serious error occured */

		/* first check if the entry represents a valid thing
		 * in the file system; follow symbolic links.
		 */
		r = stat(buf, &stats);
		if (r != 0)
		{
			/* Invalid entry in the export file found, ignore it */
			continue;
		}
	
		/* prepare the mount entry */
		mountpoint = malloc(strlen(buf)+1);
		if (!mountpoint)
			break;
		strcpy(mountpoint, buf);

		exprt = malloc(sizeof(exportlist));
		if (!exprt)
		{
			free(mountpoint);
			break;
		}
		exprt->ex_groups = NULL;
		exprt->ex_filesys = mountpoint;
		exprt->ex_next = exports;
		exports = exprt;

		ac = malloc(sizeof(accesslist));
		if (!ac)
			break;

		ac->next = NULL;
		ac->mountpoint = mountpoint;
		ac->ex = exprt;
		ac->dev = 0;
		ac->inode = 0;
		ac->access = NULL;
		ac->rw_access = NULL;
		ac->root_access = NULL;
		ac->read_only = 0;
		ac->anon_uid = NOBODY_UID;
		ac->anon_gid = NOBODY_GID;

		/* Now set up the lists of hosts this file is exported to. For the
		 * internal access checking list, discriminate between hosts that
		 * are granted mount access, root access and read-write access.
		 */
		if ((p = getexportopt(xent, ACCESS_OPT)) != NULL)
		{
			parse_clientlist(p, &ac->access);
		}
		if ((p = getexportopt(xent, RW_OPT)) != NULL)
		{
			parse_clientlist(p, &ac->rw_access);
			ac->read_only = 1;
		}
		if ((p = getexportopt(xent, ROOT_OPT)) != NULL)
		{
			parse_clientlist(p, &ac->root_access);
		}
		if (getexportopt(xent, RO_OPT) != NULL)
		{
			ac->read_only = 1;
		}
		if ((p = getexportopt(xent, ANON_OPT)) != NULL)
		{
			ac->anon_uid = (uid_t)atol(p);
		}

		ac->next = authlist;
		authlist = ac;
	}

	endexportent(fp);

#if 0
	if (debug)
	{
		for (ac = authlist;  ac;  ac = ac->next)
		{
			client_info *cl;
			syslog(LOG_INFO,"export list for `%s':", ac->mountpoint);
			syslog(LOG_INFO, "  access:");
			for (cl = ac->access;  cl;  cl = cl->next)
				syslog(LOG_INFO, "    %s(%s)", cl->name, inet_ntoa(cl->addr));
			syslog(LOG_INFO, "  rw access:");
			for (cl = ac->rw_access;  cl;  cl = cl->next)
				syslog(LOG_INFO, "    %s(%s), ", cl->name, inet_ntoa(cl->addr));
			syslog(LOG_INFO, "  root access:");
			for (cl = ac->root_access;  cl;  cl = cl->next)
				syslog(LOG_INFO, "    %s(%s), ", cl->name, inet_ntoa(cl->addr));
		}
	}
#endif

	if (exports && authlist)
	{
		/* create a list of groups which are allowed to mount stuff */
		accesslist *ac;

		for (ac = authlist;  ac;  ac = ac->next)
		{
			client_info *cl;

			for (cl = ac->access;  cl;  cl = cl->next)
			{
				groups *gr = malloc(sizeof(groups));
				if (!gr)
					break;
				gr->gr_name = malloc(strlen(cl->name)+1);
				if (!gr->gr_name)
				{
					free(gr);
					break;
				}
				strcpy(gr->gr_name, cl->name);
				gr->gr_next = ac->ex->ex_groups;
				ac->ex->ex_groups = gr;
			}
			for (cl = ac->rw_access;  cl;  cl = cl->next)
			{
				groups *gr = malloc(sizeof(groups));
				if (!gr)
					break;
				gr->gr_name = malloc(strlen(cl->name)+1);
				if (!gr->gr_name)
				{
					free(gr);
					break;
				}
				strcpy(gr->gr_name, cl->name);
				gr->gr_next = ac->ex->ex_groups;
				ac->ex->ex_groups = gr;
			}
			for (cl = ac->root_access;  cl;  cl = cl->next)
			{
				groups *gr = malloc(sizeof(groups));
				if (!gr)
					break;
				gr->gr_name = malloc(strlen(cl->name)+1);
				if (!gr->gr_name)
				{
					free(gr);
					break;
				}
				strcpy(gr->gr_name, cl->name);
				gr->gr_next = ac->ex->ex_groups;
				ac->ex->ex_groups = gr;
			}
			
		}
		return 0;
	}
	else
		return -1;
}
