/*
* auth.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "auth.h"
#include "pap.h"
#include "chap.h"
#include "lcp.h"
#include "custom.h"
#include "log.h"
/*
* INTERNAL FUNCTIONS
*/
static void AuthTimeout(void *arg);
static int AuthGetExternalPassword(AuthData auth);
/*
* AuthStart()
*
* Initialize authorization info for a link
*/
void
AuthStart(void)
{
Auth a = &lnk->lcp.auth;
/* What auth protocols were negotiated by LCP? */
a->self_to_peer = lnk->lcp.peer_auth;
a->peer_to_self = lnk->lcp.want_auth;
a->chap.recv_alg = lnk->lcp.want_chap_alg;
a->chap.xmit_alg = lnk->lcp.peer_chap_alg;
Log(LG_AUTH, ("%s: auth: peer wants %s, I want %s",
Pref(&lnk->lcp.fsm),
a->self_to_peer ? ProtoName(a->self_to_peer) : "nothing",
a->peer_to_self ? ProtoName(a->peer_to_self) : "nothing"));
/* Is there anything to do? */
if (!a->self_to_peer && !a->peer_to_self) {
LcpAuthResult(TRUE);
return;
}
/* Start global auth timer */
TimerInit(&a->timer, "AuthTimer",
LCP_AUTH_TIMEOUT * SECONDS, AuthTimeout, NULL);
TimerStart(&a->timer);
/* Start my auth to him */
switch (a->self_to_peer) {
case 0:
break;
case PROTO_PAP:
PapStart(&a->pap, AUTH_SELF_TO_PEER);
break;
case PROTO_CHAP:
ChapStart(&a->chap, AUTH_SELF_TO_PEER);
break;
default:
assert(0);
}
/* Start his auth to me */
switch (a->peer_to_self) {
case 0:
break;
case PROTO_PAP:
PapStart(&a->pap, AUTH_PEER_TO_SELF);
break;
case PROTO_CHAP:
ChapStart(&a->chap, AUTH_PEER_TO_SELF);
break;
default:
assert(0);
}
}
/*
* AuthFinish()
*
* Authorization is finished, so continue one way or the other
*/
void
AuthFinish(int which, int ok, AuthData auth)
{
Auth const a = &lnk->lcp.auth;
switch (which) {
case AUTH_SELF_TO_PEER:
a->self_to_peer = 0;
break;
case AUTH_PEER_TO_SELF:
a->peer_to_self = 0;
if (ok) {
/* Save authorization name */
snprintf(lnk->peer_authname, sizeof(lnk->peer_authname),
"%s", auth->authname);
/* Save IP address info for this peer */
lnk->peer_allow = auth->range;
lnk->range_valid = auth->range_valid;
}
break;
default:
assert(0);
}
/* Notify external auth program if needed */
if (which == AUTH_PEER_TO_SELF && auth->external) {
ExecCmd(LG_AUTH, "%s %s %s", auth->extcmd,
ok ? "-y" : "-n", auth->authname);
}
/* Did auth fail (in either direction)? */
if (!ok) {
AuthStop();
LcpAuthResult(FALSE);
return;
}
/* Did auth succeed (in both directions)? */
if (!a->peer_to_self && !a->self_to_peer) {
AuthStop();
LcpAuthResult(TRUE);
return;
}
}
/*
* AuthStop()
*
* Stop the authorization process
*/
void
AuthStop(void)
{
Auth a = &lnk->lcp.auth;
TimerStop(&a->timer);
PapStop(&a->pap);
ChapStop(&a->chap);
}
/*
* AuthGetData()
*
* Returns -1 if not found and sets *whyFail to the failure code
*/
int
AuthGetData(AuthData auth, int complain, int *whyFail)
{
FILE *fp;
int ac;
char *av[20];
char *line;
/* Default to generic failure reason */
if (whyFail)
*whyFail = AUTH_FAIL_INVALID_LOGIN;
/* Check authname, must be non-empty */
if (*auth->authname == 0) {
if (complain)
Log(LG_AUTH, ("mpd: empty auth name"));
return(-1);
}
/* Use manually configured login and password, if given */
if (*bund->conf.password && !strcmp(auth->authname, bund->conf.authname)) {
snprintf(auth->password, sizeof(auth->password), "%s", bund->conf.password);
memset(&auth->range, 0, sizeof(auth->range));
auth->range_valid = auth->external = FALSE;
return(0);
}
/* Search secrets file */
if ((fp = OpenConfFile(SECRET_FILE)) == NULL)
return(-1);
while ((line = ReadFullLine(fp, NULL)) != NULL) {
memset(av, 0, sizeof(av));
ac = ParseLine(line, av, sizeof(av) / sizeof(*av));
Freee(line);
if (ac >= 2
&& (strcmp(av[0], auth->authname) == 0
|| (av[1][0] == '!' && strcmp(av[0], "*") == 0))) {
if (av[1][0] == '!') { /* external auth program */
snprintf(auth->extcmd, sizeof(auth->extcmd), "%s", av[1] + 1);
auth->external = TRUE;
if (AuthGetExternalPassword(auth) == -1) {
FreeArgs(ac, av);
fclose(fp);
return(-1);
}
} else {
snprintf(auth->password, sizeof(auth->password), "%s", av[1]);
*auth->extcmd = '\0';
auth->external = FALSE;
}
memset(&auth->range, 0, sizeof(auth->range));
auth->range_valid = FALSE;
if (ac >= 3)
auth->range_valid = ParseAddr(av[2], &auth->range);
FreeArgs(ac, av);
fclose(fp);
return(0);
}
FreeArgs(ac, av);
}
fclose(fp);
#ifdef IA_CUSTOM
return(CustomAuthData(auth, whyFail));
#else
return(-1); /* Invalid */
#endif
}
/*
* AuthPreChecks()
*
*/
int
AuthPreChecks(AuthData auth, int complain, int *whyFail)
{
/* check max. number of logins */
if (bund->conf.max_logins != 0) {
int ac;
u_long num = 0;
for(ac = 0; ac < gNumBundles; ac++)
if (gBundles[ac]->open)
if (!strcmp(gBundles[ac]->peer_authname, auth->authname))
num++;
if (num >= bund->conf.max_logins) {
if (complain) {
Log(LG_AUTH, (" Name: \"%s\" max. number of logins exceeded",
auth->authname));
}
*whyFail = AUTH_FAIL_ACCT_DISABLED;
return (-1);
}
}
return (0);
}
/*
* AuthTimeout()
*
* Timer expired for the whole authorization process
*/
static void
AuthTimeout(void *ptr)
{
Log(LG_AUTH, ("%s: authorization timer expired", Pref(&lnk->lcp.fsm)));
AuthStop();
LcpAuthResult(FALSE);
}
/*
* AuthFailMsg()
*/
const char *
AuthFailMsg(int proto, int alg, int whyFail)
{
static char buf[64];
const char *mesg;
if (proto == PROTO_CHAP
&& (alg == CHAP_ALG_MSOFT || alg == CHAP_ALG_MSOFTv2)) {
int mscode;
switch (whyFail) {
case AUTH_FAIL_ACCT_DISABLED:
mscode = MSCHAP_ERROR_ACCT_DISABLED;
break;
case AUTH_FAIL_NO_PERMISSION:
mscode = MSCHAP_ERROR_NO_DIALIN_PERMISSION;
break;
case AUTH_FAIL_RESTRICTED_HOURS:
mscode = MSCHAP_ERROR_RESTRICTED_LOGON_HOURS;
break;
case AUTH_FAIL_INVALID_PACKET:
case AUTH_FAIL_INVALID_LOGIN:
case AUTH_FAIL_NOT_EXPECTED:
default:
mscode = MSCHAP_ERROR_AUTHENTICATION_FAILURE;
break;
}
if (bund->radius.mschap_error != NULL) {
snprintf(buf, sizeof(buf), bund->radius.mschap_error);
} else {
snprintf(buf, sizeof(buf), "E=%d R=0", mscode);
}
mesg = buf;
} else {
switch (whyFail) {
case AUTH_FAIL_ACCT_DISABLED:
mesg = AUTH_MSG_ACCT_DISAB;
break;
case AUTH_FAIL_NO_PERMISSION:
mesg = AUTH_MSG_NOT_ALLOWED;
break;
case AUTH_FAIL_RESTRICTED_HOURS:
mesg = AUTH_MSG_RESTR_HOURS;
break;
case AUTH_FAIL_NOT_EXPECTED:
mesg = AUTH_MSG_NOT_EXPECTED;
break;
case AUTH_FAIL_INVALID_PACKET:
mesg = AUTH_MSG_BAD_PACKET;
break;
case AUTH_FAIL_INVALID_LOGIN:
default:
mesg = AUTH_MSG_INVALID;
break;
}
}
return(mesg);
}
/*
* AuthGetExternalPassword()
*
* Run the named external program to fill in the password for the user
* mentioned in the AuthData
* -1 on error (can't fork, no data read, whatever)
*/
static int
AuthGetExternalPassword(AuthData a)
{
char cmd[AUTH_MAX_PASSWORD + 5 + AUTH_MAX_AUTHNAME];
int ok = 0;
FILE *fp;
int len;
snprintf(cmd, sizeof(cmd), "%s %s", a->extcmd, a->authname);
Log(LG_AUTH, ("Invoking external auth program: %s", cmd));
if ((fp = popen(cmd, "r")) == NULL) {
Perror("Popen");
return (-1);
}
if (fgets(a->password, sizeof(a->password), fp) != NULL) {
len = strlen(a->password); /* trim trailing newline */
if (len > 0 && a->password[len - 1] == '\n')
a->password[len - 1] = '\0';
ok = (*a->password != '\0');
} else {
if (ferror(fp))
Perror("Error reading from external auth program");
}
if (!ok)
Log(LG_AUTH, ("External auth program failed for user \"%s\"", a->authname));
pclose(fp);
return (ok ? 0 : -1);
}
syntax highlighted by Code2HTML, v. 0.9.1