/*
 * T.C.F.S. Utils 2.0 $Revision: 2.13 $
 *
 * Authors:	Giuseppe Cattaneo, <cattaneo@udsab.dia.unisa.it>
 *		Giuseppe Persiano, <giuper@udsab.dia.unisa.it>
 *		Luigi Catuogno, <luicat@mikonos.dia.unisa.it>
 *		Angelo Celentano, <angcel@mikonos.dia.unisa.it>
 *		Andrea Cozzolino, <andcoz@mikonos.dia.unisa.it>
 *		Aniello Del Sorbo, <anidel@mikonos.dia.unisa.it>
 *		Ermelindo Mauriello, <ermmau@mikonos.dia.unisa.it>
 *		Raffaele Pisapia, <rafpis@mikonos.dia.unisa.it>
 *
 * Permission  to use,  copy,  and modify this  software  without fee
 * is hereby granted, provided that this entire notice is included in
 * all  copies  of  any  software  which  is  or includes  a copy  or
 * modification of this  software and in all copies of the supporting
 * documentation for such software.
 *
 * This  software maybe  be used for  any purpose provided  the above 
 * copyright  notice  is  retained.  It is  supplied as  is, with  no 
 * warranty expressed or implied.
 */

/*
 *       $Source: /home/anidel/Wip/TCFS-2.2.0a/tcfslib-0.2/src/RCS/tcfs_dbmaint.c,v $
 *        $State: Exp $
 *
 *     $Revision: 2.13 $
 *       $Author: anidel $
 *         $Date: 1998/12/10 10:31:30 $
 *       $Locker:  $
 */

static const char *RCSid="$Id: tcfs_dbmaint.c,v 2.13 1998/12/10 10:31:30 anidel Exp $";
/* -+-_== */ 

#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 "tcfslib.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)
	 {
		tcfs_gid_t d;
		d = va_arg (argv, tcfs_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)
	 {
		tcfs_gid_t *d;

		d = va_arg (argv, tcfs_gid_t *);
		memcpy (d, &t->gid, sizeof (tcfs_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_gdbm_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_gdbm_ggetpwnam (char *user, tcfs_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_gdbm_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_gdbm_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 (tcfs_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;
}

/* These will never be implemented */
void
tcfs_text_strgetgpwnam (void)
{
}

void
tcfs_text_strputgpwnam (void)
{
}
/* */

tcfspwdb *
tcfs_text_strgetpwnam (char *str, tcfspwdb *dest)
{
  static char *v[NumOfField], **a, *i;
  tcfspwdb *r;

  r = NULL;
  if (!str)
    return NULL;

  i = str;
  if (!dest)
    {
      r = (tcfspwdb *) malloc (sizeof (struct tcfspwdb_r));
      if (!r)
	{
	  syslog (LOG_ERR, "malloc failed");
	  exit (1);
	}
    }
  else
    r = dest;

  memset (r->user, 0, MaxUserLen);
  memset (r->upw, 0,  UUKEYSIZE);


  for (a = v; (*a = strsep (&i, ":")) != NULL;)
    if (**a != '\0')
      ++a;
    else
      (*(*a - 1) = '\0'), ++a;

  memcpy (r->user, v[0], strlen (v[0]));
  memcpy (r->upw, v[1], strlen (v[1]));

  return r;
}

void 
tcfs_text_strputpwnam (tcfspwdb *f, char *line)
{
  sprintf (line, "%s:%s", f->user, f->upw);
  return;
}

tcfspwdb *
tcfs_text_getpwnam (char *u, tcfspwdb *dest)
{
  tcfspwdb *r;
  static char *var[2], **ap, *is;
  static char line[MaxLineLen];
  int done = 0, lockf;

  FILE *pwdf;

  r = NULL;

  if ((lockf = open (TCFSPWDLOCK, O_RDONLY)) != -1)
    {
      syslog (LOG_ERR, "tcfspasswd locked");
      goto fine;
    }

  pwdf = fopen (TCFSPWDFILE, "r");
  if (!pwdf)
    {
      syslog (LOG_ERR, "cannot open tcfspasswd file");
      goto fine;
    }

  while (!feof (pwdf))
    {
      fgets (line, MaxLineLen, pwdf);
      if (feof (pwdf))
	break;

      is = line;
      for (ap = var; (*ap = strsep (&is, ":")) != NULL;)
	if (**ap != '\0')
	  ++ap;
	else
	  (*(*ap - 1) = '\0'), ++ap;

      if (!strncmp (u, var[0], strlen (u)))
	{
	  done = 1;
	  break;
	}
    }

  fclose (pwdf);
  if (!done)
    goto fine;

  if (!dest)
    {
      r = (tcfspwdb *) malloc (sizeof (struct tcfspwdb_r));
      if (!r)
	{
	  syslog (LOG_ERR, "malloc failed");
	  close (lockf);
	  unlink (TCFSPWDLOCK);
	  exit (1);
	}
    }
  else
    {
      r = dest;
    }

  memset (r->user, 0, MaxUserLen);
  memset (r->upw, 0, UUKEYSIZE);
  memcpy (r->user, var[0], strlen (var[0]));
  memcpy (r->upw, var[1], strlen (var[1]));
fine:
  close (lockf);
  unlink (TCFSPWDLOCK);
  return r;
}


int 
tcfs_textdb_putpwnam (tcfspwdb *f, int flags)
{
  char *user;
  FILE *tcfstmp, *tcfspwd;
  static struct tcfspwdb_r e;
  char entry[MaxLineLen], line[MaxLineLen];
  int done = 0, found = 0;

  user = f->user;

  if (!(tcfstmp = fopen (TCFSPWDTMP, "w")))
    {
      syslog (LOG_ERR, "Can't open /etc/tcfstmp, can't update key file");
      return 0;
    }

  tcfspwd = fopen (TCFSPWDFILE, "r");

  while (!feof (tcfspwd))
    {

      fgets (entry, MaxLineLen, tcfspwd);
      if (feof (tcfspwd))
	break;


      if (strncmp (entry, user, strlen (user)) != 0)
	{
	  fwrite (entry, 1, strlen (entry), tcfstmp);
	}
      else
	{
	  found = 1;

	  if (!tcfs_text_strgetpwnam (entry, &e))
	    return 0;

	  if (flags & U_DEL)
	    {
	      done = 1;
	      continue;
	    }

	  if (flags & U_CHG)
	    {
	      tcfs_text_strputpwnam (f, line);
	      fprintf (tcfstmp, "%s\n", line);
	      done = 1;
	      continue;
	    }
	}
    }
  fclose (tcfspwd);

  if ((!done) && (!found))
    {
      fclose (tcfstmp);
      unlink (TCFSPWDTMP);
      return 0;
    }

  if ((!done) && (found))
    {
      tcfs_text_strputpwnam (f, line);
      fprintf (tcfstmp, "%s\n", line);
      fclose (tcfstmp);
    }

  unlink (TCFSPWDOLD);
  link (TCFSPWDFILE, TCFSPWDOLD);
  unlink (TCFSPWDFILE);
  link (TCFSPWDTMP, TCFSPWDFILE);
  unlink (TCFSPWDTMP);
  chmod (TCFSPWDFILE, 0644);
  syslog (LOG_INFO, "tcfspasswd updated");
  return 1;
}

int
tcfs_group_chgpwd (char *user, tcfs_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;
}
