4
* Version: $Id: rlm_smsotp.c,v 0.2 2009/02/03 08:06:44 wofflger Exp $
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20
* Copyright 2000,2006 The FreeRADIUS server project
21
* Copyright 2009 Siemens AG, Holger Wolff holger.wolff@siemens.com
24
#include <freeradius-devel/ident.h>
25
RCSID("$Id: rlm_smsotp.c,v 0.2 2009/02/03 08:06:44 wofflger Exp $")
27
#include <freeradius-devel/radiusd.h>
28
#include <freeradius-devel/modules.h>
31
#include "rlm_smsotp.h"
34
* A mapping of configuration file names to internal variables.
36
* Note that the string is dynamically allocated, so it MUST
37
* be freed. When the configuration file parse re-reads the string,
38
* it free's the old one, and strdup's the new one, placing the pointer
39
* to the strdup'd string into 'config.string'. This gets around
43
static const CONF_PARSER module_config[] = {
44
{ "socket", PW_TYPE_STRING_PTR, offsetof(rlm_smsotp_t, smsotp_socket), NULL, SMSOTP_SOCKET },
45
{ "challenge_message", PW_TYPE_STRING_PTR, offsetof(rlm_smsotp_t, smsotp_challengemessage), NULL, SMSOTP_CHALLENGEMESSAGE },
46
{ "challenge_type", PW_TYPE_STRING_PTR, offsetof(rlm_smsotp_t, smsotp_authtype), NULL, SMSOTP_AUTHTYPE },
48
{ NULL, -1, 0, NULL, NULL } /* end the list */
52
/* socket forward declarations begin */
53
static int smsotp_connect(const char *path);
54
static smsotp_fd_t * smsotp_getfd(const rlm_smsotp_t *opt);
55
static void smsotp_putfd(smsotp_fd_t *fdp, int disconnect);
56
static int smsotp_read(smsotp_fd_t *fdp, char *buf, size_t len);
57
static int smsotp_write(smsotp_fd_t *fdp, const char *buf, size_t len);
58
/* socket forward declarations end */
62
* Do any per-module initialization that is separate to each
63
* configured instance of the module. e.g. set up connections
64
* to external databases, read configuration files, set up
65
* dictionary entries, etc.
67
* If configuration information is given in the config section
68
* that must be referenced in later calls, store a handle to it
69
* in *instance otherwise put a null pointer there.
71
static int smsotp_instantiate(CONF_SECTION *conf, void **instance)
76
* Set up a storage area for instance data
78
data = rad_malloc(sizeof(*data));
82
memset(data, 0, sizeof(*data));
85
* If the configuration parameters can't be parsed, then
88
if (cf_section_parse(conf, data, module_config) < 0) {
99
* Authenticate the user with the given password.
101
static int smsotp_authenticate(void *instance, REQUEST *request)
105
rlm_smsotp_t *opt = instance;
106
char SocketReply[1000];
109
/* quiet the compiler */
115
fdp = smsotp_getfd(instance);
116
if (!fdp || fdp->fd == -1)
117
return RLM_MODULE_FAIL;
120
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
123
* Look for the 'state' attribute.
125
state = pairfind(request->packet->vps, PW_STATE);
127
DEBUG("rlm_smsotp: Found reply to access challenge");
130
smsotp_write(fdp, "check otp for ", 14);
131
smsotp_write(fdp, (const char *) request->username->vp_strvalue, sizeof(request->username->vp_strvalue));
132
smsotp_write(fdp, "\n", 1);
133
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
135
/* set otp password */
136
smsotp_write(fdp, "user otp is ", 12);
137
smsotp_write(fdp, (const char *) request->password->vp_strvalue, sizeof(request->password->vp_strvalue));
138
smsotp_write(fdp, "\n", 1);
139
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
142
smsotp_write(fdp, "otp id is ", 10);
143
smsotp_write(fdp, (const char *) state->vp_strvalue, 36); /* smsotp_write(fdp, (const char *) state->vp_strvalue, sizeof(state->vp_strvalue)); */
144
smsotp_write(fdp, "\n", 1);
145
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
147
/* now check the otp */
148
smsotp_write(fdp, "get check result\n", 17);
149
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
151
/* end the sesssion */
152
smsotp_write(fdp, "quit\n", 5);
153
smsotp_putfd(fdp, 1);
155
(void) radlog(L_AUTH, "rlm_smsotp: SocketReply is %s ",SocketReply);
157
if (strcmp(SocketReply,"OK") == 0)
158
return RLM_MODULE_OK;
159
return RLM_MODULE_FAIL;
162
DEBUG("rlm_smsotp: Generate OTP");
165
smsotp_write(fdp, "generate otp for ", 17);
166
smsotp_write(fdp, (const char *) request->username->vp_strvalue, sizeof(request->username->vp_strvalue));
167
smsotp_write(fdp, "\n", 1);
168
SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
170
/* end the sesssion */
171
smsotp_write(fdp, "quit\n", 5);
172
smsotp_putfd(fdp, 1);
174
(void) radlog(L_AUTH, "rlm_smsotp: Uniq id is %s ",SocketReply);
176
/* check the return string */
177
if (strcmp(SocketReply,"FAILED") == 0) { /* smsotp script returns a error */
178
return RLM_MODULE_FAIL;
181
* Create the challenge, and add it to the reply.
184
reply = pairmake("Reply-Message", opt->smsotp_challengemessage, T_OP_EQ);
185
pairadd(&request->reply->vps, reply);
186
state = pairmake("State", SocketReply, T_OP_EQ);
187
pairadd(&request->reply->vps, state);
190
* Mark the packet as an Access-Challenge packet.
192
* The server will take care of sending it to the user.
194
request->reply->code = PW_ACCESS_CHALLENGE;
195
DEBUG("rlm_smsotp: Sending Access-Challenge.");
197
return RLM_MODULE_HANDLED;
202
* Find the named user in this modules database. Create the set
203
* of attribute-value pairs to check and reply with for this user
204
* from the database. The authentication code only needs to check
205
* the password, the rest is done here.
207
static int smsotp_authorize(void *instance, REQUEST *request)
210
rlm_smsotp_t *opt = instance;
212
/* quiet the compiler */
217
* Look for the 'state' attribute.
219
state = pairfind(request->packet->vps, PW_STATE);
221
DEBUG("rlm_smsotp: Found reply to access challenge (AUTZ), Adding Auth-Type '%s'",opt->smsotp_authtype);
223
pairdelete(&request->config_items, PW_AUTH_TYPE); /* delete old auth-type */
224
pairadd(&request->config_items, pairmake("Auth-Type", opt->smsotp_authtype, T_OP_SET));
227
return RLM_MODULE_OK;
231
* Only free memory we allocated. The strings allocated via
232
* cf_section_parse() do not need to be freed.
234
static int smsotp_detach(void *instance)
240
/* forward declarations */
241
static smsotp_fd_t *smsotp_fd_head = NULL;
242
static pthread_mutex_t smsotp_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
243
/* forward declarations end */
245
/* socket functions begin */
246
/* connect to socket and return fd */
247
static int smsotp_connect(const char *path)
250
struct sockaddr_un sa;
251
size_t sp_len; /* sun_path length (strlen) */
253
/* setup for unix domain socket */
254
sp_len = strlen(path);
255
if (sp_len > sizeof(sa.sun_path) - 1) {
256
(void) radlog(L_ERR, "rlm_smsotp: %s: socket name too long", __func__);
259
sa.sun_family = AF_UNIX;
260
(void) strcpy(sa.sun_path, path);
262
/* connect to socket */
263
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
264
(void) radlog(L_ERR, "rlm_smsotp: %s: socket: %s", __func__, strerror(errno));
267
if (connect(fd, (struct sockaddr *) &sa, sizeof(sa.sun_family) + sp_len) == -1) {
268
(void) radlog(L_ERR, "rlm_smsotp: %s: connect(%s): %s", __func__, path, strerror(errno));
276
* Retrieve an fd (from pool) to use for socket connection.
277
* It'd be simpler to use TLS but FR can have lots of threads
278
* and we don't want to waste fd's that way.
279
* We can't have a global fd because we'd then be pipelining
280
* requests to otpd and we have no way to demultiplex
283
static smsotp_fd_t * smsotp_getfd(const rlm_smsotp_t *opt)
288
/* walk the connection pool looking for an available fd */
289
for (fdp = smsotp_fd_head; fdp; fdp = fdp->next) {
290
rc = smsotp_pthread_mutex_trylock(&fdp->mutex);
292
if (!strcmp(fdp->path, opt->smsotp_socket)) /* could just use == */
297
/* no fd was available, add a new one */
298
fdp = rad_malloc(sizeof(*fdp));
299
smsotp_pthread_mutex_init(&fdp->mutex, NULL);
300
smsotp_pthread_mutex_lock(&fdp->mutex);
301
/* insert new fd at head */
302
smsotp_pthread_mutex_lock(&smsotp_fd_head_mutex);
303
fdp->next = smsotp_fd_head;
304
smsotp_fd_head = fdp;
305
smsotp_pthread_mutex_unlock(&smsotp_fd_head_mutex);
307
fdp->path = opt->smsotp_socket;
311
/* establish connection */
313
fdp->fd = smsotp_connect(fdp->path);
318
/* release fd, and optionally disconnect from otpd */
319
static void smsotp_putfd(smsotp_fd_t *fdp, int disconnect)
322
(void) close(fdp->fd);
326
/* make connection available to another thread */
327
smsotp_pthread_mutex_unlock(&fdp->mutex);
331
* Full read with logging, and close on failure.
332
* Returns nread on success, 0 on EOF, -1 on other failures.
334
static int smsotp_read(smsotp_fd_t *fdp, char *buf, size_t len)
337
size_t nread = 0; /* bytes read into buf */
343
FD_SET(fdp->fd, &rfds);
347
while (nread < len) {
348
if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
349
if (errno == EINTR) {
352
(void) radlog(L_ERR, "rlm_smsotp: %s: read from socket: %s", __func__, strerror(errno));
353
smsotp_putfd(fdp, 1);
358
(void) radlog(L_ERR, "rlm_smsotp: %s: socket disconnect", __func__);
359
smsotp_putfd(fdp, 1);
363
// DEBUG("smsotp_read ... read more ?");
365
// check if more data is avalible
366
retval = select(1, &rfds, NULL, NULL, &tv);
371
// DEBUG("smsotp_read ... read more ! YES !");
373
} /*while (more to read) */
379
* Full write with logging, and close on failure.
380
* Returns 0 on success, errno on failure.
382
static int smsotp_write(smsotp_fd_t *fdp, const char *buf, size_t len)
388
if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
389
if (errno == EINTR || errno == EPIPE) {
392
(void) radlog(L_ERR, "rlm_smsotp: %s: write to socket: %s", __func__, strerror(errno));
393
smsotp_putfd(fdp, 1);
402
/* socket functions end */
405
/* mutex functions begin*/
406
/* guaranteed initialization */
407
static void _smsotp_pthread_mutex_init(pthread_mutex_t *mutexp, const pthread_mutexattr_t *attr, const char *caller)
411
if ((rc = pthread_mutex_init(mutexp, attr))) {
412
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_init: %s", caller, strerror(rc));
417
/* guaranteed lock */
418
static void _smsotp_pthread_mutex_lock(pthread_mutex_t *mutexp, const char *caller)
422
if ((rc = pthread_mutex_lock(mutexp))) {
423
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_lock: %s", caller, strerror(rc));
428
/* guaranteed trylock */
429
static int _smsotp_pthread_mutex_trylock(pthread_mutex_t *mutexp, const char *caller)
433
rc = pthread_mutex_trylock(mutexp);
434
if (rc && rc != EBUSY) {
435
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_trylock: %s", caller, strerror(rc));
442
/* guaranteed unlock */
443
static void _smsotp_pthread_mutex_unlock(pthread_mutex_t *mutexp, const char *caller)
447
if ((rc = pthread_mutex_unlock(mutexp))) {
448
(void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_unlock: %s", caller, strerror(rc));
452
/* mutex functions end */
456
* The module name should be the only globally exported symbol.
457
* That is, everything else should be 'static'.
459
* If the module needs to temporarily modify it's instantiation
460
* data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
461
* The server will then take care of ensuring that the module
462
* is single-threaded.
464
module_t rlm_smsotp = {
467
RLM_TYPE_THREAD_SAFE, /* type */
468
smsotp_instantiate, /* instantiation */
469
smsotp_detach, /* detach */
471
smsotp_authenticate, /* authentication */
472
smsotp_authorize, /* authorization */
473
NULL, /* preaccounting */
474
NULL, /* accounting */
475
NULL, /* checksimul */
476
NULL, /* pre-proxy */
477
NULL, /* post-proxy */