/*
 *	Transparent Cryptographic File System (TCFS) for NetBSD 
 *	Author and mantainer: 	Luigi Catuogno [luicat@tcfs.unisa.it]
 *	
 *	references:		http://tcfs.dia.unisa.it
 *				tcfs-bsd@tcfs.unisa.it
 */

/*
 *	Base utility set v0.1
 *
 *	  $Source: /home/luicat/source/lib/RCS/tcfs_dbmaint.c,v $
 *	   $State: Exp $
 *	$Revision: 1.1 $
 *	  $Author: luicat $
 *	    $Date: 1999/10/28 10:24:48 $
 *
 */

static const char *RCSid="$id: $";

/* RCS_HEADER_ENDS_HERE */



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <gdbm.h>
#include <syslog.h>
#include <sys/types.h>
#include <fcntl.h>
#include <tcfs.h>
#include <tcfslib.h>
#include <tcfspwdb.h>

int 
tcfspwdbr_new (tcfspwdb **new)
{
	*new=(tcfspwdb *)calloc(1,sizeof(tcfspwdb));

	if (!*new)
		return 0;

	return 1;
}

int
tcfsgpwdbr_new (tcfsgpwdb **new)
{
	*new=(tcfsgpwdb *)calloc(1,sizeof(tcfsgpwdb));

	if (!*new)
		return 0;

	return 1;
}

int 
tcfspwdbr_edit (tcfspwdb **tmp, int flags,...)
{
  va_list argv;
  char *d;

  if (!*tmp)
    if (!tcfspwdbr_new (tmp))
      return 0;

  va_start (argv, flags);

  if (flags & F_USR)
    {
      d = va_arg (argv, char *);
      strcpy ((*tmp)->user, d);
    }

  if (flags & F_PWD)
    {
      d = va_arg (argv, char *);
      strcpy ((*tmp)->upw, d);
    }

  va_end (argv);
  return 1;
}

int 
tcfsgpwdbr_edit (tcfsgpwdb **tmp, int flags,...)
{
  va_list argv;
  char *d;

  if (!*tmp)
    if (!tcfsgpwdbr_new (tmp))
      return 0;

  va_start (argv, flags);

  if (flags & F_USR)
    {
      d = va_arg (argv, char *);
      strcpy ((*tmp)->user, d);
    }

  if (flags & F_GKEY)
    {
      d = va_arg (argv, char *);
      strcpy ((*tmp)->gkey, d);
    }

  if (flags & F_GID)
	 {
		gid_t d;
		d = va_arg (argv, gid_t);
		(*tmp)->gid = d;
	}

  if (flags & F_MEMBERS)
	 {
		int d;
		d = va_arg (argv, int);
		(*tmp)->n=d;
	 }

  if (flags & F_THRESHOLD)
	 {
		int d;
		d = va_arg (argv, int);
		(*tmp)->soglia=d;
	 }

  va_end (argv);
  return 1;
}

int 
tcfspwdbr_read (tcfspwdb *t, int flags,...)
{
  va_list argv;
  int r;
  char *d;

  va_start (argv, flags);

  if (flags & F_USR)
    {
      d = va_arg (argv, char *);
      memset (d, 0, UserLen);
      strcpy (d, t->user);
    }

  if (flags & F_PWD)
    {
      d = va_arg (argv, char *);
      memset (d, 0, PassLen);
      strcpy (d, t->upw);
    }

  va_end (argv);
  return 0;
}

int 
tcfsgpwdbr_read (tcfsgpwdb *t, int flags,...)
{
  va_list argv;
  int r;
  char *d;

  va_start (argv, flags);

  if (flags & F_USR)
    {
      d = va_arg (argv, char *);
      strcpy (d, t->user);
    }

  if (flags & F_GKEY)
    {
      d = va_arg (argv, char *);
      strcpy (d, t->gkey);
    }

  if (flags & F_GID)
	 {
		gid_t *d;

		d = va_arg (argv, gid_t *);
		memcpy (d, &t->gid, sizeof (gid_t));
	}
	/* Incomplete... */

  va_end (argv);
  return 0;
}

void 
tcfspwdbr_dispose (tcfspwdb *t)
{
  free ((void *)t);
}

void 
tcfsgpwdbr_dispose (tcfsgpwdb *t)
{
  free ((void *)t);
}

tcfspwdb *
tcfs_getpwnam (char *user, tcfspwdb **dest)
{
  GDBM_FILE pdb;
  datum srchkey, r;

  if (!*dest)
    if (!tcfspwdbr_new (dest))
      return NULL;

  pdb = gdbm_open (TCFSPWDB, TCFSPWDBSIZ, GDBM_READER, 0644, NULL);
  if (!pdb)
    return NULL;

  srchkey.dptr = user;
  srchkey.dsize = (int)strlen (user);

  r = gdbm_fetch (pdb, srchkey);
  if (!r.dptr)
    {
      gdbm_close (pdb);
      return 0;
    }

  memcpy (*dest, r.dptr, sizeof (tcfspwdb));

  gdbm_close (pdb);
  free (r.dptr);

  return (tcfspwdb *)*dest;
}

tcfsgpwdb *
tcfs_ggetpwnam (char *user, gid_t gid, tcfsgpwdb **dest)
{
  GDBM_FILE pdb;
  datum srchkey, r;
  char *key, *buf;

  if (!*dest)
    if (!tcfsgpwdbr_new (dest))
      return NULL;

  pdb=gdbm_open (TCFSGPWDB, TCFSPWDBSIZ, GDBM_READER, 0644, NULL);
  if (!pdb)
    return NULL;

  key=(char*)calloc(strlen(user)+4/*gid lenght*/+1/*null*/,sizeof(char));
  if (!key)
	return NULL;

  sprintf (key, "%s\33%d\0", user, (int)gid);
  srchkey.dptr=key;
  srchkey.dsize=(int)strlen (key);

  r=gdbm_fetch (pdb, srchkey);

  if (!(r.dptr))
    {
      gdbm_close (pdb);
      return NULL;
    }

  memcpy (*dest, r.dptr, sizeof (tcfsgpwdb));

  gdbm_close (pdb);
  free (key);
  free (r.dptr);

  return *dest;
}

int 
tcfs_putpwnam (char *user, tcfspwdb *src, int flags)
{
	GDBM_FILE pdb;
	static datum srchkey, d;
	int open_flag=0, owf=0;

	if (access (TCFSPWDB, F_OK)<0)
		open_flag=GDBM_WRCREAT;
	else
		open_flag=GDBM_WRITER;

	pdb = gdbm_open (TCFSPWDB, TCFSPWDBSIZ, open_flag, 0644, NULL);
	if (!pdb)
		return 0;

	srchkey.dptr=user;
	srchkey.dsize=(int)strlen (user);

	if (flags != U_DEL)
	{
		d.dptr=(char *)src;
		d.dsize=(int)sizeof(tcfspwdb);

		switch (flags)
		{
			case U_NEW:
				owf = GDBM_INSERT;
				break;
			case U_CHG:
				owf = GDBM_REPLACE;
				break;
		}

		if (gdbm_store (pdb, srchkey, d, owf))
		{
			gdbm_close (pdb);
			return 0;
		}
	}
	else if (gdbm_delete (pdb, srchkey))
	{
		gdbm_close (pdb);
		return 0;
	}

	gdbm_reorganize (pdb);
	gdbm_close (pdb);
	return 1;
}

int 
tcfs_gputpwnam (char *user, tcfsgpwdb *src, int flags)
{
  GDBM_FILE pdb;
  static datum srchkey, d;
  int open_flag=0, owf=0;
  char *key, *buf;
  char *tmp;

  if (access (TCFSGPWDB, F_OK) < 0)
    open_flag=GDBM_WRCREAT;
  else
    open_flag=GDBM_WRITER;

  pdb = gdbm_open (TCFSGPWDB, TCFSPWDBSIZ, open_flag, 0644, 0);
  if (!pdb)
    return 0;

  key = (char *) calloc (strlen(src->user) + 4 + 1, sizeof(char));
  sprintf (key, "%s\33%d\0", src->user, src->gid);

  srchkey.dptr=key;
  srchkey.dsize=strlen (key);

  if (flags!=U_DEL)
    {
      d.dptr=(char *)src;
      d.dsize=sizeof(tcfsgpwdb);

      switch (flags)
	   {
	     case U_NEW:
	       owf = GDBM_INSERT;
	       break;
	     case U_CHG:
	       owf = GDBM_REPLACE;
	       break;
	   }

      if (gdbm_store (pdb, srchkey, d, owf))
	   {
	     gdbm_close (pdb);
	     return 0;
	   }
    }
  else if (gdbm_delete (pdb, srchkey))
    {
      gdbm_close (pdb);
      return 0;
    }

  gdbm_reorganize (pdb);
  gdbm_close (pdb);
  return 1;
}

int
tcfs_rmgroup (gid_t gid)
{
	GDBM_FILE groupdb;
	datum dbkey, nextdbkey;

	groupdb=gdbm_open (TCFSGPWDB, TCFSPWDBSIZ, GDBM_WRITER, 0644, 0);
	if (!groupdb)
		return 0;

	dbkey=gdbm_firstkey (groupdb);

	while (dbkey.dptr)
	{
		char *tmp;

		tmp=(char*)calloc(1024, sizeof(char));

		nextdbkey=gdbm_nextkey (groupdb, dbkey);

		sprintf(tmp, "\33%d\0", gid);
		if (strstr (dbkey.dptr, tmp))
		{
			if (gdbm_delete (groupdb, dbkey))
			{
				gdbm_close (groupdb);

				free (dbkey.dptr);
				free (tmp);

				return 0;
			}
		}

		free (dbkey.dptr);
		free (tmp);

		dbkey=nextdbkey;
	}

	gdbm_reorganize (groupdb);
	return 1;
}


int
tcfs_group_chgpwd (char *user, gid_t gid, char *old, char *new)
{
	tcfsgpwdb *group_info;
	unsigned char *key;

	key=(unsigned char *)calloc(UUKEYSIZE, sizeof (char));
	if (!key)
		return 0;

	if (!tcfs_decrypt_key (user, old, (unsigned char*)group_info->gkey, key, GROUPKEY))
		return 0;

	if (!tcfs_encrypt_key (user, new, key, (unsigned char *)group_info->gkey, GROUPKEY))
		return 0;

	if (!tcfs_gputpwnam (user, group_info, U_CHG))
		return 0;

	free (group_info);
	free (key);

	return 1;
}

int 
tcfs_chgpwd (char *user, char *old, char *new)
{
	tcfspwdb *user_info=NULL;
	unsigned char *key;

	key=(unsigned char*)calloc(UUKEYSIZE, sizeof(char));

	if (!tcfs_getpwnam (user, &user_info))
		return 0;

	if (!tcfs_decrypt_key (user, old, (unsigned char *)user_info->upw, key, USERKEY))
		return 0;

	if (!tcfs_encrypt_key (user, new, key, (unsigned char *)user_info->upw, USERKEY))
		return 0;

	if (!tcfs_putpwnam (user, user_info, U_CHG))
		return 0;

	free (user_info);
	free (key);

	return 1;
}

int
tcfs_chgpassword (char *user, char *old, char *new)
{
	int error1=0, error2=0;
	GDBM_FILE gpdb;
	datum found, key, nextkey;
	unsigned char *ckey;

	ckey=(unsigned char*)calloc(UUKEYSIZE, sizeof(char));
	if (!ckey)
		return 0;

	gpdb=gdbm_open (TCFSGPWDB, TCFSPWDBSIZ, GDBM_WRITER, 0644, NULL);
	if (!gpdb)
		return 0;

	error1=tcfs_chgpwd (user, old, new);
	if (!error1)
		return 0;

	/* Reencrypt group shares */
	key=gdbm_firstkey (gpdb);
	while (key.dptr)
	{
		nextkey=gdbm_nextkey (gpdb, key);
		if (strncmp (user, key.dptr, strlen(user)))
		{
			free (key.dptr);
			key=nextkey;
			continue;
		}

		found=gdbm_fetch (gpdb, key);

		if (!tcfs_decrypt_key (user, old, (unsigned char *)((tcfsgpwdb *)found.dptr)->gkey, ckey, USERKEY))
			return 0;

		if (!tcfs_encrypt_key (user, new, ckey, (unsigned char *)((tcfsgpwdb *)found.dptr)->gkey, USERKEY))
			return 0;

		if (gdbm_store (gpdb, key, found, GDBM_REPLACE))
		{
			free (ckey);
			free (found.dptr);
			free (key.dptr);

			gdbm_close (gpdb);
			return 0;
		}

		free (ckey);
		free (found.dptr);
		free (key.dptr);

		key=nextkey;
	}

	return 1;
}
