/*
 *	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/bin/RCS/tcfsaddgroup.c,v $
 *	   $State: Exp $
 *	$Revision: 1.1 $
 *	  $Author: luicat $
 *	    $Date: 1999/10/28 10:13:00 $
 *
 */

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

/* RCS_HEADER_ENDS_HERE */



#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <tcfslib.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <tcfserrors.h>

char *usage="Usage: %s [OPTION]
Add a TCFS group to the TCFS group database.

  -g <group>          Group id [or name] of the TCFS group
  -m <members>        Number of members of the group
  -t <threshold>      Threshold of the group
  -v                  Makes the output a little more verbose
  -h                  Shows this help\n";

int threshold;
unsigned char coeff[KEYSIZE][256];
unsigned char *S=NULL;          /* Pointer to a 64-bit TCFS group key */

union bobbit
{
	unsigned char byte;
	struct
	{ 
		unsigned char b1:1;
		unsigned char b2:1;
		unsigned char b3:1;
		unsigned char b4:1;
		unsigned char b5:1;
		unsigned char b6:1;
		unsigned char b7:1;
		unsigned char b8:1;
	} bf;
};

int tcfsgetuid (char *login) 
{    
	struct passwd *entry;

	setpwent();

	while ((entry=getpwent())!=NULL)
	{
		if (strcmp (login, entry->pw_name)==0)
			return entry->pw_uid;
	}

	endpwent();
	free(entry);

	return -1;
}    

void gencoeff (void)
{    
	int i, j;

	for (i=0;i<KEYSIZE;i++){
		for (j=1;j<threshold;j++){
			coeff[j][i]=rand()%257;
		}
	}
}

unsigned char *gengrpkey (char *login)
{
	int l, x1, i, j, k=0;
	unsigned int x;

	unsigned char *res=NULL;
	unsigned int tmp;
	union bobbit obits;

	res=(unsigned char*)calloc(KEYSIZE+KEYSIZE/8, sizeof(char));
	if (!res)
		tcfs_error (ER_MEM, NULL);

	x1 = tcfsgetuid(login);
	x = (x1 %257);

#ifdef DEBUG_TCFS
	printf ("La chiave utente di %u e':\n", x);
#endif

	for (i=0;i<KEYSIZE;i++)
	{
		tmp=0;
		for (j=1;j<threshold;j++) {
			tmp+=(eleva(x1,j,257)*coeff[j][i]) % 257;
#ifdef DEBUG_TCFS
			printf ("x1= %u\tj=%d\tcoeff[%d][%d]=%u\ttmp=%u\tchiave: ", x1, j, j, i, coeff[j][i], tmp);
#endif
			}
		tmp+=(unsigned int)S[i];
		tmp%=257;

		memcpy (res+k++, &tmp, 1);
#ifdef DEBUG_TCFS
		printf ("%u\n", *(res+k-1));
#endif
		switch (i%8){
			case 0:
				obits.bf.b1=tmp>>8;
				break;
			case 1:
				obits.bf.b2=tmp>>8;
				break;
			case 2:
				obits.bf.b3=tmp>>8;
				break;
			case 3:
				obits.bf.b4=tmp>>8;
				break;
			case 4:
				obits.bf.b5=tmp>>8;
				break;
			case 5:
				obits.bf.b6=tmp>>8;
				break;
			case 6:
				obits.bf.b7=tmp>>8;
				break;
			case 7:
				obits.bf.b8=tmp>>8;
				break;
		}

		if ((i%8)==7) {
			res[k]=obits.byte;
			k++;

#ifdef DEBUG_TCFS
			printf ("%u\n", res[k-1]);
#endif

			obits.byte=0;
		}
	}

	/*
	res[KEYSIZE]=obits.byte;
	*/
	return res;
}

int main (int argn, char *argv[])
{
	int index, val;
	gid_t gid;
	int have_gid=FALSE, have_members=FALSE, have_threshold=FALSE,
			be_verbose=FALSE;
	int temp_members, members;
	tcfsgpwdb **group_info;

	/*
	 * Going to check the arguments
	 */
	while ((val=getopt (argn, argv, "vg:m:t:h"))!=EOF)
		switch (val)
		{
			case 'm':
				members=atoi(optarg);
				have_members=TRUE;
				break;
			case 'g':
				gid=(gid_t)atoi(optarg);
				if (!gid && optarg[0]!='0') /* group name given */
				{
					struct group *group_id;

					group_id=getgrnam(optarg);
					if (!group_id)
						tcfs_error (ER_CUSTOM, "Nonexistent group.");

					gid=group_id->gr_gid;
				}

				have_gid=TRUE;
				break;
			case 't':
				threshold=atoi(optarg);
				have_threshold=TRUE;
				break;
			case 'h':
				show_usage (usage, argv[0]);
				exit (OK);
			case 'v':
				be_verbose=TRUE;
				break;
			default:
				fprintf (stderr, "Try %s --help for more information.\n", argv[0]);
				exit (ER_UNKOPT);
		}

	if (argn-optind)
		tcfs_error (ER_UNKOPT, NULL);

	if (!have_gid)
	{
		char *buff=NULL;

		buff=(char*)calloc(2048, sizeof(char));
		if (!buff)
			tcfs_error (ER_MEM, NULL);

		printf ("Group id [or name] of the TCFS group to add to the database: ");
		fgets (buff,2048,stdin);
		buff[strlen(buff)-1]='\0';
		gid=atoi(buff);

		if (!gid && buff[0]!='0') /* group name given */
		{
			struct group *group_id;

			group_id=getgrnam(buff);
			if (!group_id)
				tcfs_error (ER_CUSTOM, "Nonexistent group.");

			gid=group_id->gr_gid;
		}

		if (gid<=0)
			tcfs_error (ER_CUSTOM, "A positive ID please!");

		free (buff);
	}

	if (!have_members)
	{
		char *buff=NULL;

		buff=(char*)calloc(2048, sizeof(char));
		if (!buff)
			tcfs_error (ER_MEM, NULL);

		printf ("Number of members for the TCFS group ID #%d: ", gid);
		fgets (buff,2048,stdin);
		buff[strlen(buff)-1]='\0';
		members=atoi(buff);

		free(buff);
	}

	if (!have_threshold)
	{
		char *buff=NULL;

		buff=(char*)calloc(2048, sizeof(char));
		if (!buff)
			tcfs_error (ER_MEM, NULL);

		printf ("Threshold for the TCFS group ID #%d: ", gid);
		fgets (buff,2048,stdin);
		buff[strlen(buff)-1]='\0';
		threshold=atoi(buff);

		free (buff);
	}

	if (members<2)
		tcfs_error (ER_CUSTOM, "At least two members!");

	if (threshold>members || threshold<=0)
		tcfs_error (ER_CUSTOM, "The threshold must be no greater than the number of members and greater than zero!");

	printf ("Please, press 10 random keys:\n");

	S=gentcfskey();
#ifdef DEBUG_TCFS
	{
		int i;

		printf ("La chiave segreta e':\n");

		for (i=0;i<KEYSIZE;i++)
			printf ("%u:", S[i]);

		printf ("\n");
	}
#endif

	gencoeff();

	temp_members=members;

	group_info=(tcfsgpwdb **)calloc(members, sizeof(tcfsgpwdb *));

	/*
	 * Creating user entry
	 */
	while (members)
	{
		char *user=NULL, *passwd=NULL;
		unsigned char *newkey=NULL, *cryptedkey=NULL;
		tcfsgpwdb *tmp=NULL;
		int tmpmemb=temp_members, cont=0;

		group_info[members-1]=(tcfsgpwdb *)calloc(1, sizeof(tcfsgpwdb));

		group_info[members-1]->gid=gid;
		group_info[members-1]->n=members;
		group_info[members-1]->soglia=threshold;

		if (!unix_auth (&user, &passwd, FALSE))
		{
			fprintf (stderr, "Invalid password or the user does not exist.\n");
			continue;
		}

		if (tcfs_ggetpwnam (user, gid, &tmp))
			tcfs_error(ER_CUSTOM, "Group already exists.");

		while (tmpmemb>members)
		{
			if (!strcmp (user, group_info[tmpmemb-1]->user))
			{
				fprintf (stderr, "User already present into the group.\n");
				cont=1;
				break;
			}

			tmpmemb--;
		}

		if (cont)
			continue;

		strcpy (group_info[members-1]->user, user);

		newkey=(unsigned char*)calloc(KEYSIZE*2, sizeof (char));
		cryptedkey=(unsigned char*)calloc(UUKEYSIZE, sizeof(char));

		/*
		 * I could apply De Morgan...but I hate his rule!
		 * Btw this is more readable.
		 */
		if (!cryptedkey || !newkey)
			tcfs_error (ER_MEM, NULL);

		memcpy (newkey, gengrpkey (user), KEYSIZE+KEYSIZE/8);
		*(newkey+KEYSIZE+2)='\0';
#ifdef DEBUG_TCFS
		{
			int i;

			printf ("%s newkey: ", user);
			for (i=0;i<=KEYSIZE;i++)
				printf ("%u:", newkey[i]);
			printf ("\n");
		}
#endif

		/*
		 * Encrypt the just generated key with the user password
	 	 */
		if (!tcfs_encrypt_key (user, passwd, newkey, cryptedkey, GROUPKEY))
			tcfs_error (ER_MEM, NULL);

#ifdef DEBUG_TCFS
		{
			unsigned char *key;
			int i;

			key=(unsigned char *)calloc(UUKEYSIZE, sizeof(char));
			if (!tcfs_decrypt_key (user, passwd, cryptedkey, key, GROUPKEY))
				exit (0);

			printf ("%s key: ", user);
			for (i=0;i<=KEYSIZE;i++)
				printf ("%u:", key[i]);
			printf ("\n");

			free (key);
		}
#endif

		free (newkey);

		strcpy (group_info[members-1]->gkey, cryptedkey);
		free (cryptedkey);

		members--;
	}

	members=temp_members;

	while (members)
	{
		if (be_verbose)
			printf ("Creating a new entry for user %s in the TCFS database...\n", group_info[members-1]->user);

		if (!tcfs_gputpwnam (group_info[members-1]->user, group_info[members-1], U_NEW))
		{
			/* TODO: Remove the group entries saved before */
			tcfs_error (ER_CUSTOM, "Error: cannot add a user to the group.");
		}

		if (be_verbose)
			printf ("TCFS group entry for user %s created.\n", group_info[members-1]->user);

		members--;
	}

	tcfs_error (ER_CUSTOM, "\nAll group keys generated.");

	return 0;
}


int eleva (int x, int y, int z)
{
	int mask=0x80000000;
	int res=1,i;

	for (i=0;i<32;i++)
	{
		res=(res*res)%z;
		if (y & mask)
			res=(x*res)%z;
		mask=mask>>1;
	}

	return res;
}
