
/* MODULE							HTPasswd.c
**		PASSWORD FILE ROUTINES
**
** AUTHORS:
**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
**	MD	Mark Donszelmann    duns@vxdeop.cern.ch
**
** HISTORY:
**	 7 Nov 93 	MD 	free for crypt taken out (static data returned) 
**
**
** BUGS:
**
**
*/

#include <HTUtils.h>

#include <HTAAUtil.h>	/* Common parts of AA	*/
#include <HTAAFile.h>	/* File routines	*/
#include <HTAAServ.h>	/* Server routines	*/
#include <HTPasswd.h>	/* Implemented here	*/

#include <LYLeaks.h>

extern char *crypt();


PRIVATE char salt_chars [65] =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";


/* PRIVATE						next_rec()
**		GO TO THE BEGINNING OF THE NEXT RECORD
**		Otherwise like HTAAFile_nextRec() but
**		does not handle continuation lines
**		(because password file has none).
** ON ENTRY:
**	fp	is the password file from which records are read from.
**
** ON EXIT:
**	returns	nothing. File read pointer is located at the beginning
**		of the next record.
*/
PRIVATE void next_rec ARGS1(FILE *, fp)
{
    int ch = getc(fp);

    while (ch != EOF  &&  ch != CR  &&  ch != LF)
	ch = getc(fp);		/* Skip until end-of-line */

    while (ch != EOF &&
	   (ch == CR  ||  ch == LF))	/*Skip carriage returns and linefeeds*/
	ch = getc(fp);

    if (ch != EOF)
	ungetc(ch, fp);
}


/* PUBLIC						HTAA_encryptPasswd()
**		ENCRYPT PASSWORD TO THE FORM THAT IT IS SAVED
**		IN THE PASSWORD FILE.
** ON ENTRY:
**	password	is a string of arbitrary lenght.
**
** ON EXIT:
**	returns		password in one-way encrypted form.
**
** NOTE:
**	Uses currently the C library function crypt(), which
**	only accepts at most 8 characters long strings and produces
**	always 13 characters long strings. This function is
**	called repeatedly so that longer strings can be encrypted.
**	This is of course not as safe as encrypting the entire
**	string at once, but then again, we are not that paranoid
**	about the security inside the machine.
**
*/
PUBLIC char *HTAA_encryptPasswd ARGS1(CONST char *, password)
{
    char salt[3];
    char chunk[9];
    char *result;
    char *tmp;
    CONST char *cur = password;
    int len = strlen(password);
    int randum = (int)theTime;	/* This is random enough */

    if (!(result = (char*)malloc(13*((strlen(password)+7)/8) + 1)))
	outofmem(__FILE__, "HTAA_encryptPasswd");

    *result = (char)0;
    while (len > 0) {
	salt[0] = salt_chars[randum%64];
	salt[1] = salt_chars[(randum/64)%64];
	salt[2] = (char)0;

	strncpy(chunk, cur, 8);
	chunk[8] = (char)0;

	tmp = crypt((char*)password, salt);  /*crypt() doesn't change its args*/
	strcat(result, tmp);

	cur += 8;
	len -= 8;
    } /* while */

    return result;
}



/* PUBLIC						HTAA_passwdMatch()
**		VERIFY THE CORRECTNESS OF A GIVEN PASSWORD
**		AGAINST A ONE-WAY ENCRYPTED FORM OF PASSWORD.
** ON ENTRY:
**	password	is cleartext password.
**	encrypted	is one-way encrypted password, as returned
**			by function HTAA_encryptPasswd().
**			This is typically read from the password
**			file.
**
** ON EXIT:
**	returns		YES, if password matches the encrypted one.
**			NO, if not, or if either parameter is NULL.
** FIX:
**	Only the length of original encrypted password is
**	checked -- longer given passwords are accepted if
**	common length is correct (but not shorter).
**	This is to allow interoperation of servers and clients
**	who have a hard-coded limit of 8 to password.
*/
PUBLIC BOOL HTAA_passwdMatch ARGS2(CONST char *, password,
				   CONST char *, encrypted)
{
    char *result;
    size_t len;
    int status;

    if (!password || !encrypted)
	return NO;

    len = 13*((strlen(password)+7)/8);
    if (len < strlen(encrypted))
	return NO;

    if (!(result = (char*)malloc(len + 1)))
	outofmem(__FILE__, "HTAA_encryptPasswd");

    *result = (char)0;
    while (len != 0) {
	char salt[3];
	char chunk[9];
	CONST char *cur1 = password;
	CONST char *cur2 = encrypted;
	char *tmp;

	salt[0] = *cur2;
	salt[1] = *(cur2+1);
	salt[2] = (char)0;

	strncpy(chunk, cur1, 8);
	chunk[8] = (char)0;

	tmp = crypt((char*)password, salt);
	strcat(result, tmp);

	cur1 += 8;
	cur2 += 13;
	len -= 13;
    } /* while */

    status = strncmp(result, encrypted, strlen(encrypted));

    CTRACE(tfp, "%s `%s' (encrypted: `%s') with: `%s' => %s\n",
		"HTAA_passwdMatch: Matching password:",
		password, result, encrypted,
		(status==0 ? "OK" : "INCORRECT"));

    FREE(result);

    if (status==0)
	return YES;
    else
	return NO;
}


/* PUBLIC					HTAAFile_readPasswdRec()
**			READ A RECORD FROM THE PASSWORD FILE
** ON ENTRY:
**	fp		open password file
**	out_username	buffer to put the read username, must be at
**			least MAX_USERNAME_LEN+1 characters long.
**	out_passwd	buffer to put the read password, must be at
**			least MAX_PASSWORD_LEN+1 characters long.
** ON EXIT:
**	returns		EOF on end of file,
**			otherwise the number of read fields
**			(i.e. in a correct case returns 2).
**	out_username	contains the null-terminated read username.
**	out_password	contains the null-terminated read password.
**
** FORMAT OF PASSWORD FILE:
**	username:password:maybe real name or other stuff
**				(may include even colons)
**
**	There may be whitespace (blanks or tabs) in the beginning and
**	the end of each field. They are ignored.
*/
PUBLIC int HTAAFile_readPasswdRec ARGS3(FILE *, fp,
					char *, out_username,
					char *, out_password)
{
    int terminator;
    
    terminator = HTAAFile_readField(fp, out_username, MAX_USERNAME_LEN);

    if (terminator == EOF) {				/* End of file */
	return EOF;
    }
    else if (terminator == CR  ||  terminator == LF) {	/* End of line */
	next_rec(fp);
	return 1;
    }
    else {
	HTAAFile_readField(fp, out_password, MAX_PASSWORD_LEN);
	next_rec(fp);
	return 2;
    }
}



/* PUBLIC						HTAA_checkPassword()
**		CHECK A USERNAME-PASSWORD PAIR
** ON ENTRY:
**	username	is a null-terminated string containing
**			the client's username.
**	password	is a null-terminated string containing
**			the client's corresponding password.
**	filename	is a null-terminated absolute filename
**			for password file.
**			If NULL or empty, the value of
**			PASSWD_FILE is used.
** ON EXIT:
**	returns		YES, if the username-password pair was correct.
**			NO, otherwise; also, if open fails.
*/
PUBLIC BOOL HTAA_checkPassword ARGS3(CONST char *, username,
				     CONST char *, password,
				     CONST char *, filename)
{
    FILE *fp = NULL;
    char user[MAX_USERNAME_LEN+1];
    char pw[MAX_PASSWORD_LEN+1];
    int status;
    
    if (filename && *filename)  fp = fopen(filename,"r");
    else			fp = fopen(PASSWD_FILE,"r");

    if (!fp) {
	CTRACE(tfp, "%s `%s'\n",
		    "HTAA_checkPassword: Unable to open password file",
		    (filename && *filename ? filename : PASSWD_FILE));
	return NO;
    }
    do {
	if (2 == (status = HTAAFile_readPasswdRec(fp,user,pw))) {
	    CTRACE(tfp, "HTAAFile_validateUser: %s \"%s\" %s \"%s:%s\"\n",
			"Matching username:", username,
			"against passwd record:", user, pw);
	    if (username  &&  user  &&  !strcmp(username,user)) {
		/* User's record found */
		if (*pw != '\0') { /* So password is required for this user */
		    if (!password ||
			!HTAA_passwdMatch(password,pw)) /* Check the password */
			status = EOF;	/* If wrong, indicate it with EOF */
		}
		break;  /* exit loop */
	    }  /* if username found */
	}  /* if record is ok */
    } while (status != EOF);

    fclose(fp);
    
    CTRACE(tfp, "HTAAFile_checkPassword: (%s,%s) %scorrect\n",
		       username, password, ((status != EOF) ? "" : "in"));

    if (status == EOF)  return NO;  /* We traversed to the end without luck */
    else                return YES; /* The user was found */
}

