#include <freeradius-devel/ident.h>
RCSID("$Id: rlm_smsotp.c,v 0.2 2009/02/03 08:06:44 wofflger Exp $")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <sys/un.h>
#include "rlm_smsotp.h"
static const CONF_PARSER module_config[] = {
{ "socket", PW_TYPE_STRING_PTR, offsetof(rlm_smsotp_t, smsotp_socket), NULL, SMSOTP_SOCKET },
{ "challenge_message", PW_TYPE_STRING_PTR, offsetof(rlm_smsotp_t, smsotp_challengemessage), NULL, SMSOTP_CHALLENGEMESSAGE },
{ "challenge_type", PW_TYPE_STRING_PTR, offsetof(rlm_smsotp_t, smsotp_authtype), NULL, SMSOTP_AUTHTYPE },
{ NULL, -1, 0, NULL, NULL }
};
static int smsotp_connect(const char *path);
static smsotp_fd_t * smsotp_getfd(const rlm_smsotp_t *opt);
static void smsotp_putfd(smsotp_fd_t *fdp, int disconnect);
static int smsotp_read(smsotp_fd_t *fdp, char *buf, size_t len);
static int smsotp_write(smsotp_fd_t *fdp, const char *buf, size_t len);
static int smsotp_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_smsotp_t *data;
data = rad_malloc(sizeof(*data));
if (!data) {
return -1;
}
memset(data, 0, sizeof(*data));
if (cf_section_parse(conf, data, module_config) < 0) {
free(data);
return -1;
}
*instance = data;
return 0;
}
static int smsotp_authenticate(void *instance, REQUEST *request)
{
VALUE_PAIR *state;
VALUE_PAIR *reply;
rlm_smsotp_t *opt = instance;
char SocketReply[1000];
int SocketReplyLen;
instance = instance;
request = request;
smsotp_fd_t *fdp;
fdp = smsotp_getfd(instance);
if (!fdp || fdp->fd == -1)
return RLM_MODULE_FAIL;
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
state = pairfind(request->packet->vps, PW_STATE);
if (state != NULL) {
DEBUG("rlm_smsotp: Found reply to access challenge");
smsotp_write(fdp, "check otp for ", 14);
smsotp_write(fdp, (const char *) request->username->vp_strvalue, sizeof(request->username->vp_strvalue));
smsotp_write(fdp, "\n", 1);
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
smsotp_write(fdp, "user otp is ", 12);
smsotp_write(fdp, (const char *) request->password->vp_strvalue, sizeof(request->password->vp_strvalue));
smsotp_write(fdp, "\n", 1);
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
smsotp_write(fdp, "otp id is ", 10);
smsotp_write(fdp, (const char *) state->vp_strvalue, 36);
smsotp_write(fdp, "\n", 1);
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
smsotp_write(fdp, "get check result\n", 17);
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
smsotp_write(fdp, "quit\n", 5);
smsotp_putfd(fdp, 1);
(void) radlog(L_AUTH, "rlm_smsotp: SocketReply is %s ",SocketReply);
if (strcmp(SocketReply,"OK") == 0)
return RLM_MODULE_OK;
return RLM_MODULE_FAIL;
}
DEBUG("rlm_smsotp: Generate OTP");
smsotp_write(fdp, "generate otp for ", 17);
smsotp_write(fdp, (const char *) request->username->vp_strvalue, sizeof(request->username->vp_strvalue));
smsotp_write(fdp, "\n", 1);
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
smsotp_write(fdp, "quit\n", 5);
smsotp_putfd(fdp, 1);
(void) radlog(L_AUTH, "rlm_smsotp: Uniq id is %s ",SocketReply);
if (strcmp(SocketReply,"FAILED") == 0) {
return RLM_MODULE_FAIL;
} else {
reply = pairmake("Reply-Message", opt->smsotp_challengemessage, T_OP_EQ);
pairadd(&request->reply->vps, reply);
state = pairmake("State", SocketReply, T_OP_EQ);
pairadd(&request->reply->vps, state);
request->reply->code = PW_ACCESS_CHALLENGE;
DEBUG("rlm_smsotp: Sending Access-Challenge.");
return RLM_MODULE_HANDLED;
}
}
static int smsotp_authorize(void *instance, REQUEST *request)
{
VALUE_PAIR *state;
rlm_smsotp_t *opt = instance;
instance = instance;
request = request;
state = pairfind(request->packet->vps, PW_STATE);
if (state != NULL) {
DEBUG("rlm_smsotp: Found reply to access challenge (AUTZ), Adding Auth-Type '%s'",opt->smsotp_authtype);
pairdelete(&request->config_items, PW_AUTH_TYPE);
pairadd(&request->config_items, pairmake("Auth-Type", opt->smsotp_authtype, T_OP_SET));
}
return RLM_MODULE_OK;
}
static int smsotp_detach(void *instance)
{
free(instance);
return 0;
}
static smsotp_fd_t *smsotp_fd_head = NULL;
static pthread_mutex_t smsotp_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
static int smsotp_connect(const char *path)
{
int fd;
struct sockaddr_un sa;
size_t sp_len;
sp_len = strlen(path);
if (sp_len > sizeof(sa.sun_path) - 1) {
(void) radlog(L_ERR, "rlm_smsotp: %s: socket name too long", __func__);
return -1;
}
sa.sun_family = AF_UNIX;
(void) strcpy(sa.sun_path, path);
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
(void) radlog(L_ERR, "rlm_smsotp: %s: socket: %s", __func__, strerror(errno));
return -1;
}
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa.sun_family) + sp_len) == -1) {
(void) radlog(L_ERR, "rlm_smsotp: %s: connect(%s): %s", __func__, path, strerror(errno));
(void) close(fd);
return -1;
}
return fd;
}
static smsotp_fd_t * smsotp_getfd(const rlm_smsotp_t *opt)
{
int rc;
smsotp_fd_t *fdp;
for (fdp = smsotp_fd_head; fdp; fdp = fdp->next) {
rc = smsotp_pthread_mutex_trylock(&fdp->mutex);
if (!rc)
if (!strcmp(fdp->path, opt->smsotp_socket))
break;
}
if (!fdp) {
fdp = rad_malloc(sizeof(*fdp));
smsotp_pthread_mutex_init(&fdp->mutex, NULL);
smsotp_pthread_mutex_lock(&fdp->mutex);
smsotp_pthread_mutex_lock(&smsotp_fd_head_mutex);
fdp->next = smsotp_fd_head;
smsotp_fd_head = fdp;
smsotp_pthread_mutex_unlock(&smsotp_fd_head_mutex);
fdp->path = opt->smsotp_socket;
fdp->fd = -1;
}
if (fdp->fd == -1)
fdp->fd = smsotp_connect(fdp->path);
return fdp;
}
static void smsotp_putfd(smsotp_fd_t *fdp, int disconnect)
{
if (disconnect) {
(void) close(fdp->fd);
fdp->fd = -1;
}
smsotp_pthread_mutex_unlock(&fdp->mutex);
}
static int smsotp_read(smsotp_fd_t *fdp, char *buf, size_t len)
{
ssize_t n;
size_t nread = 0;
fd_set rfds;
struct timeval tv;
int retval;
FD_ZERO(&rfds);
FD_SET(fdp->fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
while (nread < len) {
if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
if (errno == EINTR) {
continue;
} else {
(void) radlog(L_ERR, "rlm_smsotp: %s: read from socket: %s", __func__, strerror(errno));
smsotp_putfd(fdp, 1);
return -1;
}
}
if (!n) {
(void) radlog(L_ERR, "rlm_smsotp: %s: socket disconnect", __func__);
smsotp_putfd(fdp, 1);
return 0;
}
nread += n;
retval = select(1, &rfds, NULL, NULL, &tv);
if (!retval) {
buf[nread]= '\0';
break;
}
}
return nread;
}
static int smsotp_write(smsotp_fd_t *fdp, const char *buf, size_t len)
{
size_t nleft = len;
ssize_t nwrote;
while (nleft) {
if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
if (errno == EINTR || errno == EPIPE) {
continue;
} else {
(void) radlog(L_ERR, "rlm_smsotp: %s: write to socket: %s", __func__, strerror(errno));
smsotp_putfd(fdp, 1);
return errno;
}
}
nleft -= nwrote;
}
return 0;
}
static void _smsotp_pthread_mutex_init(pthread_mutex_t *mutexp, const pthread_mutexattr_t *attr, const char *caller)
{
int rc;
if ((rc = pthread_mutex_init(mutexp, attr))) {
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_init: %s", caller, strerror(rc));
exit(1);
}
}
static void _smsotp_pthread_mutex_lock(pthread_mutex_t *mutexp, const char *caller)
{
int rc;
if ((rc = pthread_mutex_lock(mutexp))) {
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_lock: %s", caller, strerror(rc));
exit(1);
}
}
static int _smsotp_pthread_mutex_trylock(pthread_mutex_t *mutexp, const char *caller)
{
int rc;
rc = pthread_mutex_trylock(mutexp);
if (rc && rc != EBUSY) {
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_trylock: %s", caller, strerror(rc));
exit(1);
}
return rc;
}
static void _smsotp_pthread_mutex_unlock(pthread_mutex_t *mutexp, const char *caller)
{
int rc;
if ((rc = pthread_mutex_unlock(mutexp))) {
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_unlock: %s", caller, strerror(rc));
exit(1);
}
}
module_t rlm_smsotp = {
RLM_MODULE_INIT,
"smsotp",
RLM_TYPE_THREAD_SAFE,
smsotp_instantiate,
smsotp_detach,
{
smsotp_authenticate,
smsotp_authorize,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
},
};