/*
** Copyright 2000-2004 University of Illinois Board of Trustees
** Copyright 2000-2004 Mark D. Roth
** All rights reserved.
**
** auth.c - authentication code for libphclient
**
** Mark D. Roth <roth@feep.net>
*/
#include <internal.h>
#include <errno.h>
#include <pwd.h>
#include <sys/types.h>
#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_CRYPT_H
# include <crypt.h>
#endif
/* function prototypes */
typedef int (*ph_authfunc_t)(PH *, struct ph_auth *, char *, char *,
char *, size_t);
static int ph_passwd_auth(PH *, struct ph_auth *, char *, char *,
char *, size_t);
static int ph_email_auth(PH *, struct ph_auth *, char *, char *,
char *, size_t);
/* details of each auth type */
struct authtype
{
int at_type; /* auth type */
char *at_logincmd; /* server login command for this auth method */
char *at_passcmd; /* password or challenge response command */
ph_authfunc_t at_authfunc; /* function to modify auth token
(optional) */
int at_denycode; /* reply code sent from server to deny
authentication */
};
typedef struct authtype authtype_t;
static authtype_t authtypes[] = {
{ PH_AUTH_EMAIL, "login", "email", ph_email_auth, LR_NOEMAIL },
{ PH_AUTH_PASSWORD, "login", "answer", ph_passwd_auth, LR_ERROR },
{ PH_AUTH_CLEAR, "login", "clear", NULL, LR_ERROR },
{ 0, NULL, NULL, NULL, 0 }
};
void
ph_free_auth(struct ph_auth *auth)
{
if (auth->pa_alias != NULL)
free(auth->pa_alias);
if (auth->pa_authkey != NULL)
free(auth->pa_authkey);
free(auth);
}
/*
** ph_login() - login to the PH server.
** returns:
** 0 success
** -1 error (sets errno)
** PH_ERR_NOTLOG authentication denied
*/
int
ph_login(PH *ph, char *alias, int authtype, void *auth)
{
int i, code, authslot;
char response[PH_BUF_SIZE];
char challenge[PH_BUF_SIZE];
struct ph_auth *authdata;
ph_memblock_t *blockp;
#ifdef DEBUG
printf("ph_login(): alias=\"%s\", authtype=%d, auth=\"%s\")\n",
alias, authtype, auth);
#endif
/* find authtype entry */
for (authslot = 0; authtypes[authslot].at_type != 0; authslot++)
if (authtypes[authslot].at_type == authtype)
break;
if (authtypes[authslot].at_type == 0)
{
errno = EINVAL;
return -1;
}
/* old alias no longer valid */
if (ph->ph_auth != NULL)
ph_free_auth(ph->ph_auth);
ph->ph_auth = NULL;
/*
** free cached field info, since hidden fields are visible to
** different users
*/
ph_free_fieldinfo(ph);
if (ph_mmgr_malloc(ph->ph_mmgr, 0, 1, sizeof(struct ph_auth),
(ph_free_func_t)ph_free_auth, &blockp) == -1)
return -1;
authdata = (struct ph_auth *)ph_mmgr_ptr(blockp);
authdata->pa_alias = strdup(alias);
if (authdata->pa_alias == NULL)
return -1;
authdata->pa_authtype = authtype;
/* send login command */
if (ph_send_command(ph, "%s %s", authtypes[authslot].at_logincmd,
alias) == -1)
return -1;
/* read challenge */
do
{
if (ph_get_response(ph, &code, challenge,
sizeof(challenge)) == -1)
return -1;
}
while (code < LR_OK);
if (code != LR_LOGIN)
{
errno = EINVAL;
return -1;
}
/* determine response based on auth method */
if (authtypes[authslot].at_authfunc != NULL)
{
if ((*(authtypes[authslot].at_authfunc))(ph, authdata, auth,
challenge, response,
sizeof(response)) == -1)
return -1;
}
else
strlcpy(response, auth, sizeof(response));
/* send response */
i = ph_send_command(ph, "%s %s", authtypes[authslot].at_passcmd,
response);
if (i == -1)
return -1;
do
{
if (ph_get_response(ph, &code, NULL, 0) == -1)
return -1;
}
while (code < LR_OK);
/* successful login or login failed */
if (code == LR_OK)
{
ph->ph_auth = authdata;
ph_mmgr_free(blockp, 0);
return 0;
}
if (code == authtypes[authslot].at_denycode)
return PH_ERR_NOTLOG;
if (code == LR_TEMP)
errno = EAGAIN;
else
errno = EINVAL;
return -1;
}
/* plugin function for email authentication */
static int
ph_email_auth(PH *ph, struct ph_auth *auth, char *password, char *challenge,
char *response, size_t responselen)
{
struct passwd *pw;
#ifdef HAVE_GETPWUID_R
struct passwd pent;
size_t bufsize;
ph_memblock_t *blockp;
bufsize = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX);
if (ph_mmgr_malloc(ph->ph_mmgr, 0, 1, bufsize, free, &blockp) == -1)
return -1;
getpwuid_r(getuid(), &pent, ph_mmgr_ptr(blockp), bufsize, &pw);
#else /* ! HAVE_GETPWUID_R */
pw = getpwuid(getuid());
#endif /* HAVE_GETPWUID_R */
if (pw == NULL)
{
#ifdef HAVE_GETPWUID_R
ph_mmgr_free(blockp, 1);
#endif /* HAVE_GETPWUID_R */
return -1;
}
strlcpy(response, pw->pw_name, responselen);
#ifdef HAVE_GETPWUID_R
ph_mmgr_free(blockp, 1);
#endif /* HAVE_GETPWUID_R */
return 0;
}
/* structure and definitions used for encrypted password authentication */
#define _PH_PWD_ROTORSZ 256
#define _PH_PWD_MASK 0377
struct ph_pwcrypt
{
int n1, n2;
char t1[_PH_PWD_ROTORSZ], t2[_PH_PWD_ROTORSZ], t3[_PH_PWD_ROTORSZ];
};
/* plugin function for encrypted password authentication */
static int
ph_passwd_auth(PH *ph, struct ph_auth *auth, char *password, char *challenge,
char *response, size_t responselen)
{
int i, k, ic, seed, temp;
unsigned random;
char buf[13], scratch[PH_BUF_SIZE];
char *p, *q;
struct ph_pwcrypt *php;
#ifdef DEBUG
printf("ph_passwd_crypt(): challenge=\"%s\", password=\"%s\"\n",
challenge, (password ? password : "NULL"));
#endif
if (auth->pa_authkey == NULL)
{
auth->pa_authkey = (void *)malloc(sizeof(struct ph_pwcrypt));
if (auth->pa_authkey == NULL)
return -1;
}
php = (struct ph_pwcrypt *)auth->pa_authkey;
if (password != NULL)
{
/* initialize arrays */
memset(auth->pa_authkey, 0, sizeof(struct ph_pwcrypt));
strncpy(buf, crypt(password, password), 13);
seed = 123;
for (i = 0; i < 13; i++)
seed = seed * buf[i] + i;
for (i = 0; i < _PH_PWD_ROTORSZ; i++)
php->t1[i] = i;
for (i = 0; i < _PH_PWD_ROTORSZ; i++)
{
seed = 5 * seed + buf[i % 13];
random = seed % 65521;
k = _PH_PWD_ROTORSZ - 1 - i;
ic = (random & _PH_PWD_MASK) % (k + 1);
random >>= 8;
temp = php->t1[k];
php->t1[k] = php->t1[ic];
php->t1[ic] = temp;
if (php->t3[k] != 0)
continue;
ic = (random & _PH_PWD_MASK) % k;
while (php->t3[ic] != 0)
ic = (ic + 1) % k;
php->t3[k] = ic;
php->t3[ic] = k;
}
for (i = 0; i < _PH_PWD_ROTORSZ; i++)
php->t2[php->t1[i] & _PH_PWD_MASK] = i;
}
/* decode challenge into scratch */
for (p = challenge, q = scratch;
(q - scratch < sizeof(scratch)) && *p;
p++)
{
*q++ = php->t2[(php->t3[(php->t1[(*p + php->n1) & _PH_PWD_MASK] + php->n2) & _PH_PWD_MASK] - php->n2) & _PH_PWD_MASK] - php->n1;
php->n1++;
if (php->n1 == _PH_PWD_ROTORSZ)
{
php->n1 = 0;
php->n2++;
if (php->n2 == _PH_PWD_ROTORSZ)
php->n2 = 0;
}
}
/* encode as ASCII into response buffer */
k = q - scratch;
p = response;
q = scratch;
*p++ = (k & 077) + '#';
for (; (p - response < responselen) && (q - scratch < k); q += 3)
{
*p++ = ((*q >> 2) & 077) + '#';
*p++ = ((((*q << 4) & 060) | ((q[1] >> 4) & 017)) & 077) + '#';
*p++ = ((((q[1] << 2) & 074) | ((q[2] >> 6) & 03)) & 077) + '#';
*p++ = ((q[2] & 077) & 077) + '#';
}
*p = '\0';
return 0;
}
/*
** ph_logout() - logout of the PH server.
** returns:
** 0 success
** -1 error (sets errno)
** PH_ERR_NOTLOG not logged in
*/
int
ph_logout(PH *ph)
{
int code;
if (ph->ph_auth == NULL)
return PH_ERR_NOTLOG;
/* old alias now invalid */
ph_free_auth(ph->ph_auth);
ph->ph_auth = NULL;
/*
** free cached field info, since hidden fields are visible to
** different users
*/
ph_free_fieldinfo(ph);
if (ph_send_command(ph, "logout") == -1)
return -1;
do
{
if (ph_get_response(ph, &code, NULL, 0) == -1)
return -1;
}
while (code < LR_OK);
if (code == LR_ERROR)
return PH_ERR_NOTLOG;
if (code == LR_OK)
return 0;
errno = EINVAL;
return -1;
}
/*
** ph_suser() - assume another user's identity (heros only).
** returns:
** 0 success
** -1 error (sets errno)
** PH_ERR_NOTHERO not a hero
** PH_ERR_NOTLOG not logged in
*/
int
ph_suser(PH *ph, char *alias)
{
int code;
if (ph->ph_auth == NULL)
return PH_ERR_NOTLOG;
if (ph_send_command(ph, "suser %s", alias) == -1)
return -1;
do
{
if (ph_get_response(ph, &code, NULL, 0) == -1)
return -1;
}
while (code < LR_OK);
if (code == LR_AUTH)
return PH_ERR_NOTHERO;
if (code == LR_OK)
{
/*
** free cached field info, since hidden fields are visible to
** different users
*/
ph_free_fieldinfo(ph);
if (ph->ph_auth->pa_alias != NULL)
free(ph->ph_auth->pa_alias);
ph->ph_auth->pa_alias = strdup(alias);
if (ph->ph_auth->pa_alias != NULL)
return -1;
return 0;
}
errno = EINVAL;
return -1;
}
/*
** ph_passwd() - change the current user's PH password.
** returns:
** 0 success
** -1 error (sets errno)
** PH_ERR_NOTLOG not logged in
** (any value returned by ph_change())
*/
int
ph_passwd(PH *ph, char *newpass)
{
struct ph_fieldselector selector[2];
struct ph_fieldselector changes[2];
char encrypted_passwd[PH_BUF_SIZE];
int i;
#ifdef DEBUG
printf("==> ph_passwd(newpass=\"%s\")\n", newpass);
#endif
/* must be logged in to change password */
if (ph->ph_auth == NULL)
return PH_ERR_NOTLOG;
selector[0].pfs_field = "alias";
selector[0].pfs_operation = '=';
selector[0].pfs_value = ph->ph_auth->pa_alias;
memset(&(selector[1]), 0, sizeof(struct ph_fieldselector));
/* generate the encrypted password */
ph_passwd_auth(ph, ph->ph_auth, NULL, newpass,
encrypted_passwd, sizeof(encrypted_passwd));
#ifdef DEBUG
printf("encrypted_passwd = \"%s\"\n", encrypted_passwd);
#endif
changes[0].pfs_field = "password";
changes[0].pfs_operation = '=';
changes[0].pfs_value = encrypted_passwd;
memset(&(changes[1]), 0, sizeof(struct ph_fieldselector));
#ifdef DEBUG
puts("calling ph_change()...");
#endif
i = ph_change(ph, selector, changes, 0);
if (i == 1)
return 0;
return i;
}
/*
** ph_whoami() - determine the currently logged-in alias
** returns:
** pointer to the current alias success
** NULL not logged in
*/
char *
ph_whoami(PH *ph)
{
return (ph->ph_auth != NULL
? ph->ph_auth->pa_alias
: NULL);
}
syntax highlighted by Code2HTML, v. 0.9.1