/* Authentication functions - taken from files.c */
#include "global.h"
#include "ctype.h"
#include "commands.h"
#include "netuser.h"
#include "files.h"
#include "md5.h"
#include "mailbox.h"


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: auth.c,v 1.3 1997/08/19 01:19:22 root Exp $";
#endif

extern char Whitespace[];

extern long btol (char *s);
void usercvt (void);
char *getsecurity (char *name, int32 *perms);


#define lookupBUFSIZ	1024


/* Read through FTPUSERS looking for user record
 * Returns line which matches username, or NULLCHAR when no match.
 * Each of the other variables must be copied before freeing the line.
 */
char *
userlookup(username,password,directory,permission,ip_address)
const char *username;
char **password;
char **directory;
long   *permission;
uint32 *ip_address;
{
FILE *fp, *fpsave = NULLFILE;
char *buf;
char *cp = NULLCHAR;
int  wasanony = 0;
char *universal = NULLCHAR;

	if ((fp = fopen (Userfile, READ_TEXT)) == NULLFILE)
		/* Userfile doesn't exist */
		return NULLCHAR;

	buf = mallocw (lookupBUFSIZ);
	for ( ; ; )	{
		if (fgets (buf, lookupBUFSIZ - 1, fp) == NULLCHAR)	{
			if (fpsave)	{
				(void) fclose (fp);
				fp = fpsave;
				fpsave = NULLFILE;
				continue;
			}
			break;
		}
		if (!strnicmp (buf, "#include", 8))	{
			rip (buf);
			cp = skipwhite (&buf[8]);
			fpsave = fp;
			if((fp = fopen (cp, READ_TEXT)) == NULLFILE)	{
				fp = fpsave;
				fpsave = NULLFILE;
			}
			continue;
		}
		if (*buf == '#')
			continue;	/* Comment */

		if ((cp = strpbrk (buf, " \t")) == NULLCHAR)
			/* Bogus entry */
			continue;
		*cp++ = '\0';		/* Now points to password */
		cp = skipwhite (cp);	/* It does now... KFU */

		if (stricmp (username, buf) == 0)
			break;		/* Found user */
		if (stricmp ("univperm", buf) == 0)
			universal = strdup (cp);	/* remember their anon entry */
	}
	if (feof (fp))	{
		if (universal == NULLCHAR)	{
			/* username not found in file, nor was univperm */
			if (fpsave)
				(void) fclose (fpsave);
			(void) fclose (fp);
			free (buf);
			return NULLCHAR;
		}
		/* restore anonymous to the buffer */
		strncpy (cp = buf, universal, lookupBUFSIZ);
		wasanony = 1;
	}
	if (universal != NULLCHAR)
		free (universal);
	(void) fclose (fp);
	if (fpsave)
		(void) fclose (fpsave);

	if (cp == NULLCHAR)
		return cp;

	if (password != NULL)
		*password = cp;

	/* Look for space or tab after password field in file */
	if ((cp = strpbrk (cp, " \t")) == NULLCHAR)	{
		/* Invalid file entry */
		free (buf);
		return NULLCHAR;
	}
	*cp++ = '\0';	/* Now points to directory field */
	cp = skipwhite (cp);

	if (*cp == '#')		{ /* using a security level entry */
		char *cp2, *path;
		int32 perm = 0;
		rip (cp);
		if ((cp2 = strpbrk (++cp, " \t")) != NULLCHAR)
			*cp2++ = 0;
		path = getsecurity (cp--, &perm);
		if (path != NULLCHAR)
			sprintf(cp, "%s %ld", path, perm);
	}
	if (directory != NULL)
		*directory = cp;

	if ((cp = strpbrk (cp, " \t")) == NULLCHAR)	{
		/* Permission field missing */
		free (buf);
		return NULLCHAR;
	}
	*cp++ = '\0';	/* now points to permission field */
	cp = skipwhite (cp);

	if (permission != NULL)	{
		if (!strnicmp (cp, "0x", 2))
			*permission = htol (cp);
		else if (*cp == '%')
			*permission = btol (cp);
		else
			*permission = atol (cp);
		if (wasanony)
			*permission |= WAS_ANONY;

		if (*permission & NO_SENDCMD)
			*permission |= (NO_PBBSMAIL + NO_INETMAIL + NO_AMPRMAIL);

	}

	if ((cp = strpbrk (cp, " \t")) == NULLCHAR)	{
		/* IP address missing */
		if (ip_address != NULL)
			*ip_address = 0L;
	} else {
		*cp++ = '\0';	/* now points at IP address field */
		cp = skipwhite (cp);
		if (ip_address != NULL)
			*ip_address = resolve(cp);
	}
	return buf;
}


/* Subroutine for logging in the user whose name is name and password is pass.
   The buffer path should be long enough to keep a line from the userfile.
   If pwdignore is true, the password check will be overridden.
   The return value is the permissions field or -1 if the login failed.
   Path is set to point at the path field, and pwdignore will be true if no
   particular password was needed for this user.
 */
long
userlogin(name,pass,path,len,pwdignore)
const char *name;
char *pass;
char **path;
int len;			/* Length of buffer pointed at by *path */
int *pwdignore;
{
char *buf;
char *password;
char *directory;
long permission;
int anonymous;
char *cp;
char hashpass[16];
MD5_CTX md;

	if ((buf = userlookup (name, &password, &directory,
		&permission, NULL)) == NULLCHAR)
			return -1;

	anonymous = *pwdignore;
	if (strcmp (password, "*") == 0)
		anonymous = TRUE;	/* User ID is password-free */
	if (!anonymous) {
		if (readhex (hashpass, password, sizeof (hashpass)) != (int) sizeof (hashpass)){
			/* Invalid hashed password in file */
			free (buf);
			return -1;
		}
		MD5Init (&md);
		MD5Update (&md, (const unsigned char *) name, strlen (name));
		MD5Update (&md, (unsigned char *) pass, strlen (pass));
		MD5Final (&md);
		if (memcmp (md.digest, hashpass, sizeof (hashpass)) != 0){
			/* Incorrect password given */
			free (buf);
			return -1;
		}
	}
	if ((int)strlen(directory) + 1 > len) {
		/* not enough room for path */
		free (buf);
		return -1;
	}

	strncpy (*path, directory, (unsigned) len);
	/* Convert any backslashes to forward slashes, for backward
	 * compatibility with the old NET
	 */
	while ((cp = strchr (*path, '\\')) != NULLCHAR)
		*cp = '/';

	free (buf);
	*pwdignore = anonymous;
	/* Finally return the permission bits */
	return permission;
}


/* MD5 hash plaintext passwords in a file */
static void
usercvtfile(const char *file, int recursing)
{
FILE *fp,*fptmp;
char *buf, *cp;
char hexbuf[16];
int needsit = 0;
int len,nlen,plen,i;
char *pass;
MD5_CTX md;

	if ((fp = fopen (file, READ_TEXT)) == NULLFILE)
		return;		/* file doesn't exist */

	tprintf ("%sEncoding passwords in %s\n", (recursing) ? "" : "\n", file);
	buf = mallocw (BUFSIZ);
	while (fgets (buf, BUFSIZ, fp) != NULLCHAR){
		rip (buf);
		len = (int) strlen (buf);
		if (len == 0)
			continue;	/* Blank line */

		if (!strnicmp (buf, "#include", 8))	{
			cp = skipwhite (&buf[8]);
			usercvtfile (cp, 1);
			continue;
		}
		if (*buf == '#')
			continue;	/* Comment line */

		if ((nlen = (int) strcspn (buf,Whitespace)) == len)
			continue;	/* No end to the name! */

		/* Skip whitespace between name and pass */
		for (pass = &buf[nlen]; isspace (*pass); pass++)
			;
		if(*pass != '\0' && *pass != '*'
		 && readhex (hexbuf, pass, sizeof (hexbuf)) != 16){
			needsit = 1;
			break;
		}
	}
	if(!needsit){
		/* Everything is in order */
		(void) fclose (fp);
		free (buf);
		return;
	}
	/* At least one entry needs its password hashed */
	rewind (fp);
	fptmp = tmpfile();
	if (fptmp == NULLFILE)	{
		printf ("Can't rewrite %s: can't create a tempfile!\n", file);
		(void) fclose (fp);
		free (buf);
		return;
	}
	while (fgets (buf, BUFSIZ, fp) != NULLCHAR)	{
		rip (buf);
		if ((len = (int) strlen (buf)) == 0 || *buf == '#'
		 || (nlen = (int) strcspn (buf, Whitespace)) == len){
			/* Line is blank, a comment or unparseable;
			 * copy unchanged
			 */
			fputs (buf, fptmp);
			fputc ('\n', fptmp);
			continue;
		}
		/* Skip whitespace between name and pass */
		for (pass = &buf[nlen]; isspace (*pass); pass++)
			;

		if(*pass == '\0' || *pass == '*'
		 || (plen = (int) strcspn (pass, Whitespace)) == (int)strlen (pass)
		 || readhex (hexbuf, pass, sizeof (hexbuf)) == (int)sizeof (hexbuf)){
			/* Other fields are missing, no password is required,
			 * or password is already hashed; copy unchanged
			 */
			fputs (buf, fptmp);
			fputc ('\n', fptmp);
			continue;
		}
		MD5Init (&md);
		MD5Update (&md, (unsigned char *) buf, (unsigned int)nlen);	/* Hash name */
		MD5Update (&md, (unsigned char *) pass, (unsigned int)plen);	/* Hash password */
		MD5Final (&md);
		fwrite (buf, 1, (unsigned int)nlen, fptmp);	/* Write name */
		fputc (' ', fptmp);		/* and space */
		for (i = 0; i < 16; i++)	/* Write hashed password */
			fprintf (fptmp, "%02x", md.digest[i] & 0xff);
		fputs (&pass[plen], fptmp);	/* Write remainder of line */
		fputc ('\n', fptmp);
	}
	/* Now copy the temp file back into the userfile */
	(void) fclose (fp);
	rewind (fptmp);
	if ((fp = fopen (file, WRITE_TEXT)) == NULLFILE)	{
		printf ("Can't rewrite %s\n", file);
		free (buf);
		return;
	}
	while (fgets (buf, BUFSIZ, fptmp) != NULLCHAR)
		fputs (buf, fp);
	(void) fclose (fp);
	(void) fclose (fptmp);
	free (buf);
}


/* MD5 hash plaintext passwords in user file */
void
usercvt()
{
	usercvtfile (Userfile, 0);
}



