#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <time.h>
#include <event.h>
#include <stdarg.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include "debugutil.h"
#include "npppd_local.h"
#include "npppd_auth.h"
#include "net_utils.h"
#include "npppd_auth_local.h"
#include "npppd_radius.h"
npppd_auth_base *
npppd_auth_create(int auth_type, const char *name, void *_npppd)
{
npppd_auth_base *base;
NPPPD_AUTH_ASSERT(name != NULL);
switch (auth_type) {
case NPPPD_AUTH_TYPE_LOCAL:
if ((base = calloc(1, sizeof(npppd_auth_local))) != NULL) {
base->type = NPPPD_AUTH_TYPE_LOCAL;
strlcpy(base->name, name, sizeof(base->name));
base->npppd = _npppd;
return base;
}
break;
#ifdef USE_NPPPD_RADIUS
case NPPPD_AUTH_TYPE_RADIUS:
if ((base = calloc(1, sizeof(npppd_auth_radius))) != NULL) {
npppd_auth_radius *_this = (npppd_auth_radius *)base;
base->type = NPPPD_AUTH_TYPE_RADIUS;
strlcpy(base->name, name, sizeof(base->name));
base->npppd = _npppd;
if ((_this->rad_auth_setting =
radius_req_setting_create()) == NULL)
goto radius_fail;
if ((_this->rad_acct_setting =
radius_req_setting_create()) == NULL)
goto radius_fail;
return base;
radius_fail:
if (_this->rad_auth_setting != NULL)
radius_req_setting_destroy(
_this->rad_auth_setting);
if (_this->rad_acct_setting != NULL)
radius_req_setting_destroy(
_this->rad_acct_setting);
free(base);
return NULL;
}
break;
#endif
default:
NPPPD_AUTH_ASSERT(0);
break;
}
return NULL;
}
void
npppd_auth_dispose(npppd_auth_base *base)
{
base->disposing = 1;
return;
}
void
npppd_auth_destroy(npppd_auth_base *base)
{
if (base->disposing == 0)
npppd_auth_dispose(base);
npppd_auth_base_log(base, LOG_INFO, "Finalized");
switch(base->type) {
case NPPPD_AUTH_TYPE_LOCAL:
memset(base, 0, sizeof(npppd_auth_local));
break;
#ifdef USE_NPPPD_RADIUS
case NPPPD_AUTH_TYPE_RADIUS:
{
npppd_auth_radius *_this = (npppd_auth_radius *)base;
if (_this->rad_auth_setting != NULL)
radius_req_setting_destroy(_this->rad_auth_setting);
_this->rad_auth_setting = NULL;
if (_this->rad_acct_setting != NULL)
radius_req_setting_destroy(_this->rad_acct_setting);
_this->rad_acct_setting = NULL;
memset(base, 0, sizeof(npppd_auth_local));
break;
}
#endif
}
free(base);
return;
}
int
npppd_auth_reload(npppd_auth_base *base)
{
struct authconf *auth;
TAILQ_FOREACH(auth, &base->npppd->conf.authconfs, entry) {
if (strcmp(auth->name, base->name) == 0)
break;
}
if (auth == NULL)
return 1;
base->pppsuffix[0] = '\0';
if (auth->username_suffix != NULL)
strlcpy(base->pppsuffix, auth->username_suffix,
sizeof(base->pppsuffix));
base->eap_capable = auth->eap_capable;
base->strip_nt_domain = auth->strip_nt_domain;
base->strip_atmark_realm = auth->strip_atmark_realm;
base->has_users_file = 0;
base->radius_ready = 0;
base->user_max_session = auth->user_max_session;
if (strlen(auth->users_file_path) > 0) {
strlcpy(base->users_file_path, auth->users_file_path,
sizeof(base->users_file_path));
base->has_users_file = 1;
} else {
if (base->type == NPPPD_AUTH_TYPE_LOCAL) {
npppd_auth_base_log(base,
LOG_WARNING, "missing users_file property.");
goto fail;
}
}
switch (base->type) {
#ifdef USE_NPPPD_RADIUS
case NPPPD_AUTH_TYPE_RADIUS:
if (npppd_auth_radius_reload(base, auth) != 0)
goto fail;
break;
#endif
}
base->initialized = 1;
return 0;
fail:
base->initialized = 0;
base->has_users_file = 0;
base->radius_ready = 0;
return 1;
}
int
npppd_auth_get_user_password(npppd_auth_base *base,
const char *username, char *password, int *plpassword)
{
int retval, sz, lpassword;
npppd_auth_user *user;
NPPPD_AUTH_ASSERT(base != NULL);
NPPPD_AUTH_DBG((base, LOG_DEBUG, "%s(%s)", __func__, username));
user = NULL;
retval = 0;
if (base->has_users_file == 0) {
retval = -1;
goto out;
}
if ((user = npppd_auth_get_user(base, username)) == NULL) {
retval = 1;
goto out;
}
if (password == NULL && plpassword == NULL) {
retval = 0;
goto out;
}
if (plpassword == NULL) {
retval = -1;
goto out;
}
lpassword = strlen(user->password) + 1;
sz = *plpassword;
*plpassword = lpassword;
if (password == NULL) {
retval = 0;
goto out;
}
if (sz < lpassword) {
retval = 2;
goto out;
}
strlcpy(password, user->password, sz);
out:
free(user);
return retval;
}
int
npppd_auth_get_framed_ip(npppd_auth_base *base, const char *username,
struct in_addr *ip4address, struct in_addr *ip4netmask)
{
npppd_auth_user *user;
NPPPD_AUTH_ASSERT(base != NULL);
NPPPD_AUTH_DBG((base, LOG_DEBUG, "%s(%s)", __func__, username));
if (base->has_users_file == 0)
return -1;
if ((user = npppd_auth_get_user(base, username)) == NULL)
return 1;
if (user->framed_ip_address.s_addr != 0) {
*ip4address = user->framed_ip_address;
if (ip4netmask != NULL)
*ip4netmask = user->framed_ip_netmask;
free(user);
return 0;
}
free(user);
return 1;
}
int
npppd_auth_get_calling_number(npppd_auth_base *base, const char *username,
char *number, int *plnumber)
{
int retval, lcallnum, sz;
npppd_auth_user *user;
user = NULL;
retval = 0;
if (base->has_users_file == 0)
return -1;
if ((user = npppd_auth_get_user(base, username)) == NULL)
return 1;
if (number == NULL && plnumber == NULL) {
retval = 0;
goto out;
}
if (plnumber == NULL) {
retval = -1;
goto out;
}
lcallnum = strlen(user->calling_number) + 1;
sz = *plnumber;
*plnumber = lcallnum;
if (sz < lcallnum) {
retval = 2;
goto out;
}
strlcpy(number, user->calling_number, sz);
out:
free(user);
return retval;
}
int
npppd_auth_get_type(npppd_auth_base *base)
{
return base->type;
}
int
npppd_auth_is_usable(npppd_auth_base *base)
{
return (base->initialized != 0 && base->disposing == 0)? 1 : 0;
}
int
npppd_auth_is_ready(npppd_auth_base *base)
{
if (!npppd_auth_is_usable(base))
return 0;
switch(base->type) {
case NPPPD_AUTH_TYPE_LOCAL:
return (base->has_users_file)? 1 : 0;
case NPPPD_AUTH_TYPE_RADIUS:
return (base->has_users_file != 0 ||
base->radius_ready != 0)? 1 : 0;
}
NPPPD_AUTH_ASSERT(0);
return 0;
}
int
npppd_auth_is_disposing(npppd_auth_base *base)
{
return (base->disposing != 0)? 1 : 0;
}
int
npppd_auth_is_eap_capable(npppd_auth_base *base)
{
return (base->eap_capable != 0)? 1 : 0;
}
const char *
npppd_auth_get_name(npppd_auth_base *base)
{
return base->name;
}
const char *
npppd_auth_get_suffix(npppd_auth_base *base)
{
return base->pppsuffix;
}
const char *
npppd_auth_username_for_auth(npppd_auth_base *base, const char *username,
char *username_buffer)
{
const char *u0;
char *atmark, *u1;
u0 = NULL;
if (base->strip_nt_domain != 0) {
if ((u0 = strchr(username, '\\')) != NULL)
u0++;
}
if (u0 == NULL)
u0 = username;
u1 = username_buffer;
if (username_buffer != u0)
memmove(username_buffer, u0, MINIMUM(strlen(u0) + 1,
MAX_USERNAME_LENGTH));
if (base->strip_atmark_realm != 0) {
if ((atmark = strrchr(u1, '@')) != NULL)
*atmark = '\0';
}
return username_buffer;
}
int
npppd_auth_user_session_unlimited(npppd_auth_base *_this)
{
return (_this->user_max_session == 0) ? 1 : 0;
}
int
npppd_check_auth_user_max_session(npppd_auth_base *_this, int count)
{
if (!npppd_auth_user_session_unlimited(_this) &&
_this->user_max_session <= count)
return 1;
else
return 0;
}
static npppd_auth_user *
npppd_auth_get_user(npppd_auth_base *base, const char *username)
{
int lsuffix, lusername;
const char *un;
char buf[MAX_USERNAME_LENGTH];
npppd_auth_user *u;
un = username;
lsuffix = strlen(base->pppsuffix);
lusername = strlen(username);
if (lsuffix > 0 && lusername > lsuffix &&
strcmp(username + lusername - lsuffix, base->pppsuffix) == 0 &&
lusername - lsuffix < sizeof(buf)) {
memcpy(buf, username, lusername - lsuffix);
buf[lusername - lsuffix] = '\0';
un = buf;
}
if (priv_get_user_info(base->users_file_path, un, &u) == 0)
return u;
return NULL;
}
#ifdef USE_NPPPD_RADIUS
static int
npppd_auth_radius_reload(npppd_auth_base *base, struct authconf *auth)
{
npppd_auth_radius *_this = (npppd_auth_radius *)base;
radius_req_setting *rad;
struct radserver *server;
int i, nauth, nacct;
_this->rad_auth_setting->timeout =
(auth->data.radius.auth.timeout == 0)
? DEFAULT_RADIUS_TIMEOUT : auth->data.radius.auth.timeout;
_this->rad_acct_setting->timeout =
(auth->data.radius.acct.timeout == 0)
? DEFAULT_RADIUS_TIMEOUT : auth->data.radius.acct.timeout;
_this->rad_auth_setting->max_tries =
(auth->data.radius.auth.max_tries == 0)
? DEFAULT_RADIUS_MAX_TRIES : auth->data.radius.auth.max_tries;
_this->rad_acct_setting->max_tries =
(auth->data.radius.acct.max_tries == 0)
? DEFAULT_RADIUS_MAX_TRIES : auth->data.radius.acct.max_tries;
_this->rad_auth_setting->max_failovers =
(auth->data.radius.auth.max_failovers == 0)
? DEFAULT_RADIUS_MAX_FAILOVERS
: auth->data.radius.auth.max_failovers;
_this->rad_acct_setting->max_failovers =
(auth->data.radius.acct.max_failovers == 0)
? DEFAULT_RADIUS_MAX_FAILOVERS
: auth->data.radius.acct.max_failovers;
_this->rad_acct_setting->curr_server =
_this->rad_auth_setting->curr_server = 0;
rad = _this->rad_auth_setting;
for (i = 0; i < countof(rad->server); i++)
memset(&rad->server[i], 0, sizeof(rad->server[0]));
i = 0;
TAILQ_FOREACH(server, &auth->data.radius.auth.servers, entry) {
if (i >= countof(rad->server))
break;
memcpy(&rad->server[i].peer, &server->address,
server->address.ss_len);
if (((struct sockaddr_in *)&rad->server[i].peer)->sin_port
== 0)
((struct sockaddr_in *)&rad->server[i].peer)->sin_port
= htons(DEFAULT_RADIUS_AUTH_PORT);
strlcpy(rad->server[i].secret, server->secret,
sizeof(rad->server[i].secret));
rad->server[i].enabled = 1;
i++;
}
nauth = i;
rad = _this->rad_acct_setting;
for (i = 0; i < countof(rad->server); i++)
memset(&rad->server[i], 0, sizeof(rad->server[0]));
i = 0;
TAILQ_FOREACH(server, &auth->data.radius.acct.servers, entry) {
if (i >= countof(rad->server))
break;
memcpy(&rad->server[i].peer, &server->address,
server->address.ss_len);
if (((struct sockaddr_in *)&rad->server[i].peer)->sin_port
== 0)
((struct sockaddr_in *)&rad->server[i].peer)->sin_port
= htons(DEFAULT_RADIUS_ACCT_PORT);
strlcpy(rad->server[i].secret, server->secret,
sizeof(rad->server[i].secret));
rad->server[i].enabled = 1;
i++;
}
nacct = i;
for (i = 0; i < countof(_this->rad_auth_setting->server); i++) {
if (_this->rad_auth_setting->server[i].enabled)
base->radius_ready = 1;
}
npppd_auth_base_log(&_this->nar_base, LOG_INFO,
"Loaded configuration. %d authentication server%s, %d accounting "
"server%s.",
nauth, (nauth > 1)? "s" : "", nacct, (nacct > 1)? "s" : "");
if (nacct > 0 && _this->rad_acct_on == 0) {
radius_acct_on(base->npppd, _this->rad_acct_setting);
_this->rad_acct_on = 1;
}
return 0;
}
void *
npppd_auth_radius_get_radius_auth_setting(npppd_auth_radius *_this)
{
return _this->rad_auth_setting;
}
void *
npppd_auth_radius_get_radius_acct_setting(npppd_auth_radius *_this)
{
return _this->rad_acct_setting;
}
#endif
static int
npppd_auth_base_log(npppd_auth_base *_this, int prio, const char *fmt, ...)
{
int status;
char logbuf[BUFSIZ];
va_list ap;
NPPPD_AUTH_ASSERT(_this != NULL);
va_start(ap, fmt);
snprintf(logbuf, sizeof(logbuf), "realm name=%s %s",
_this->name, fmt);
status = vlog_printf(prio, logbuf, ap);
va_end(ap);
return status;
}