/*
 * T.C.F.S. Utils 2.0 $Revision: 2.9 $
 *
 * 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_keymaint.c,v $
 *        $State: Exp $
 *
 *     $Revision: 2.9 $
 *       $Author: anidel $
 *         $Date: 1998/12/10 10:31:30 $
 *       $Locker:  $
 */

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

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <mntent.h>
#include <errno.h>
#include <pwd.h>
#include <syslog.h>

#include "tcfslib.h"

#include <linux/kdes.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#if defined (__GLIBC__)
#define _LINUX_SOCKET_H
#endif
#define _I386_BITOPS_H
#include <linux/fs.h>
#include <linux/tcfs_fs.h>

char *tcfs_engines[NUM_CIPHER]=
	{
		"none",
		"DES",
		"IDEA",
		"RC5"
	};

int 
tcfs_decrypt_key (char *u, char *pwd, unsigned char *t, unsigned char *tk, unsigned int flag)
{
  int len, i=0;
  char pass[_PASSWORD_LEN], *cypher;
  char *tcfskey;
  des_key_schedule ks;
  int keysize=(flag==GROUPKEY)? KEYSIZE+1:KEYSIZE;

  if (!tk)
	 return 0;

  strcpy (pass, pwd);

  tcfskey = tcfs_decode ((char *)t, &len);
  if (!tcfskey)
	 return 0;

  while (strlen (pass) < 8)
    {
      char tmp[_PASSWORD_LEN];
      strcpy (tmp, pass);
      strcat (tmp, pass);
      strcat (pass, tmp);
    }

  while ((i*8) < keysize)
    {
      des_set_key ((des_cblock *) pass, ks);

      des_ecb_encrypt ((des_cblock *) (tcfskey+i*8),
		       (des_cblock *) (tcfskey+i*8), ks, DES_DECRYPT);
      i++;
    }
  memset (pass, 0, strlen (pass));

  memcpy (tk, tcfskey, keysize);
  return 1;
}

int 
tcfs_encrypt_key (char *u, char *pw, unsigned char *key, unsigned char *ek, unsigned int flag)
{
  int i = 0;
  char pass[_PASSWORD_LEN];
  char *tcfskey;
  des_key_schedule ks;
  int keysize=(flag==GROUPKEY)?KEYSIZE+1:KEYSIZE;

  if (!ek)
	 return 0;

  strcpy (pass, pw);

  while (strlen(pass)<8)
    {
      char tmp[_PASSWORD_LEN];
      
      strcpy (tmp, pass);
      strcat (tmp, pass);
      strcat (pass, tmp);
    }

  while ((i*8) < keysize)
    {
      des_set_key ((des_cblock *) pass, ks);
      des_ecb_encrypt ((des_cblock *) (key + i * 8), (des_cblock *) (key + i * 8), ks, DES_ENCRYPT);
      i++;
    }

  tcfskey = tcfs_encode ((char*)key, KEYSIZE+8);
  if (!tcfskey)
	 return 0;

  memcpy (ek, tcfskey, UUKEYSIZE);

  return 1;
}



int 
tcfs_callfunction (int function, char *arg, char *filesystem, char *filename)
{
	FILE *mtab;
	int fd, s = 0;
	struct mntent *mp;
	int val;

	if ((mtab = setmntent ("/etc/mtab", "r")) == NULL)
		return 0;

	while ((mp = getmntent (mtab)) != NULL)
	{
		if (strcmp (mp->mnt_type, "tcfs")==0)
		{
			if ((fd = open (mp->mnt_dir, O_RDONLY)) < 0)
			{
				free (mp);
				free (mtab);
				return 0;
			}
			else
			{
				val=ioctl (fd, function, arg);
				if (val>=0)
					s++;
			}
			close (fd);
		}
	}
	endmntent (mtab);

	free (mp);
	if (!s)
		return 0;
	else
		return 1;
}

int 
tcfs_enable (char *u, unsigned char *tcfskey)
{
  struct login user;

  memcpy (user.deskey, tcfskey, KEYSIZE);
  user.uid = (getpwnam (u)->pw_uid);
  return tcfs_callfunction (TCFS_IOC_LOGIN, (char *) &user, NULL, NULL);
}

int
tcfs_group_enable (char *u, tcfs_gid_t gid, int soglia, int parts, unsigned char *tcfskey)
{
  struct tgroup group;

  memcpy (group.deskey, tcfskey, KEYSIZE+1);

#ifdef DEBUG_TCFS
	{
		int i;

		printf ("La chiave letta in group_enable e': ");
		for (i=0;i<=KEYSIZE;i++) {
			printf ("%u:", group.deskey[i]);
		}
		printf ("\n");
	}
#endif
	 
  group.soglia = soglia;
  group.n = parts;
  group.gid = gid;
  group.uid = (getpwnam (u)->pw_uid);

  return tcfs_callfunction (TCFS_IOC_GLOGIN, (char *) &group, NULL, NULL);
 }

int 
tcfs_disable (char *u, int flags)
{
  int user;

  user = (getpwnam (u)->pw_uid);
  return tcfs_callfunction ((flags==ONE)?TCFS_IOC_LOGOUT:TCFS_IOC_FULLOUT, (char *) &user, NULL, NULL);
}

int
tcfs_group_disable (char *u, tcfs_gid_t gid, int flags)
{
	struct tgroup group;
	static struct tcfsgpwdb_r *t=NULL;

	if(!tcfs_ggetpwnam(u, gid, &t))
		 return 0;

	group.gid = t->gid;
	group.uid = getpwnam(u)->pw_uid;
	group.n = t->n;
	group.soglia = t->soglia;

	if (flags == ONE)
	{
		return tcfs_callfunction (TCFS_IOC_GLOGOUT, (char*)&group, NULL, NULL);
	}
	else
		return tcfs_callfunction (TCFS_IOC_GFULLOUT, (char*)&gid, NULL, NULL);
}

int
tcfs_permanent (char *u, int flags)
{
	int user;

	user=(getpwnam(u)->pw_uid);
	
	return tcfs_callfunction ((flags==SET)?TCFS_IOC_SETFIX:TCFS_IOC_DELFIX, (char *)&user, NULL, NULL);
}

int
tcfs_group_permanent (char *u, tcfs_gid_t gid, int flags)
{
	struct tgroup group;

	group.gid = gid;
	group.uid = (getpwnam (u)->pw_uid);

	return tcfs_callfunction ((flags==SET)?TCFS_IOC_GSETFIX:TCFS_IOC_GDELFIX, (char*)&group, NULL, NULL);
}

int
tcfs_getcount (char *u)
{
	int count=0;

	if(!tcfs_callfunction (TCFS_IOC_GETCOUNT, (char *)&count, NULL, NULL))
		return -1;
	else 
		return count;
}

int
tcfs_group_getcount (char *u, tcfs_gid_t gid)
{
	unsigned int count=(unsigned int) gid;

	if (!tcfs_callfunction (TCFS_IOC_GGETCOUNT, (char *)&count, NULL, NULL))
		return -1;
	else
		return (int)count;
}


int
tcfs_getpermanent (char *u)
{
	/* TCFS_IOC_GETFIX vuole un unsigned char altrimenti ci crea casini  
		tutto dipende da ioctl.c dove qualcuno ha usato un 
		unsigned char per he->permanent che e' il campo dove viene 
		conservato il flag di permanente 				*/

	unsigned char flag;

	if(!tcfs_callfunction (TCFS_IOC_GETFIX, (char *)&flag, NULL, NULL))
		return -1;
	else 
		return (int) flag;
	
}

int
tcfs_group_getpermanent (char *u, tcfs_gid_t gid)
{
	unsigned int flag=(unsigned int) gid;

	if (!tcfs_callfunction (TCFS_IOC_GGETFIX, (char *)&flag, NULL, NULL))
		return -1;
	else
		return (int) flag;
}

int
tcfs_enable_default(char *u, char *p)
{
	unsigned char *tkey;
	int i;
	static struct tcfspwdb_r *t=NULL;

	if(!tcfs_getpwnam(u,&t))
		 return 0;

	tkey=(unsigned char*)malloc(KEYSIZE);

	if (!tkey)
		 return 0;

	tcfs_decrypt_key(u,p,(unsigned char*)t->upw, tkey, USERKEY);
	 
	i=tcfs_enable(u,tkey);
	if (!i)
		{
		 free(tkey);
		 return 0;
		}

	free(tkey);
	return i;
}

int
tcfs_group_enable_default (char *u, tcfs_gid_t gid, char *p)
{
	unsigned char *tkey;
	int i;
	static struct tcfsgpwdb_r *t=NULL;

	if(!tcfs_ggetpwnam(u, gid, &t))
		 return 0;

	tkey=(unsigned char*)malloc(KEYSIZE+1);

	if (!tkey)
		 return 0;

	tcfs_decrypt_key(u,p,(unsigned char*)t->gkey,tkey, GROUPKEY);
#ifdef DEBUG_TCFS
	printf ("Utente: %s\tpassword: %s\n", u, p);
	printf ("La chiave letta e': ");
	for (i=0;i<=KEYSIZE;i++) {
		printf ("%u:", tkey[i]);
	}
	printf ("\n");
#endif
	 
	i=tcfs_group_enable(u, t->gid, t->soglia, t->n, tkey);
	if (!i)
		{
		 free(tkey);
		 return 0;
		}

	free(tkey);
	return i;
}

int tcfs_getversion(char *msg)
{   
	union
	{ 
		long r; 
		char s[sizeof(long)];
	} x;    

	tcfs_callfunction(TCFS_IOC_GETVERSION,(char*)&x,NULL,NULL);

	if(msg!=NULL)
	{
		sprintf(msg, "TCFS v%d.%d %s",x.s[0],x.s[1], (x.s[3]<NUM_CIPHER ? tcfs_engines[x.s[3]] : "unknown cipher"));
	}
											
	return x.r;
}
