~yolanda.robla/ubuntu/saucy/freeradius/dep-8-tests

« back to all changes in this revision

Viewing changes to src/modules/rlm_smsotp/rlm_smsotp.c

  • Committer: Bazaar Package Importer
  • Author(s): Josip Rodin
  • Date: 2009-11-23 03:57:37 UTC
  • mto: This revision was merged to the branch mainline in revision 7.
  • Revision ID: james.westby@ubuntu.com-20091123035737-snauioz5r9tf8sdr
Tags: upstream-2.1.7+dfsg
ImportĀ upstreamĀ versionĀ 2.1.7+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * rlm_smsotp.c
 
3
 *
 
4
 * Version:     $Id: rlm_smsotp.c,v 0.2 2009/02/03 08:06:44 wofflger Exp $
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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
 
19
 *
 
20
 * Copyright 2000,2006  The FreeRADIUS server project
 
21
 * Copyright 2009  Siemens AG, Holger Wolff holger.wolff@siemens.com
 
22
 */
 
23
 
 
24
#include <freeradius-devel/ident.h>
 
25
RCSID("$Id: rlm_smsotp.c,v 0.2 2009/02/03 08:06:44 wofflger Exp $")
 
26
 
 
27
#include <freeradius-devel/radiusd.h>
 
28
#include <freeradius-devel/modules.h>
 
29
#include <sys/un.h>
 
30
 
 
31
#include "rlm_smsotp.h"
 
32
 
 
33
/*
 
34
 *      A mapping of configuration file names to internal variables.
 
35
 *
 
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
 
40
 *      buffer over-flows.
 
41
 */
 
42
 
 
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 },
 
47
 
 
48
  { NULL, -1, 0, NULL, NULL }           /* end the list */
 
49
};
 
50
 
 
51
 
 
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 */
 
59
 
 
60
 
 
61
/*
 
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.
 
66
 *
 
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.
 
70
 */
 
71
static int smsotp_instantiate(CONF_SECTION *conf, void **instance)
 
72
{
 
73
        rlm_smsotp_t *data;
 
74
 
 
75
        /*
 
76
         *      Set up a storage area for instance data
 
77
         */
 
78
        data = rad_malloc(sizeof(*data));
 
79
        if (!data) {
 
80
                return -1;
 
81
        }
 
82
        memset(data, 0, sizeof(*data));
 
83
 
 
84
        /*
 
85
         *      If the configuration parameters can't be parsed, then
 
86
         *      fail.
 
87
         */
 
88
        if (cf_section_parse(conf, data, module_config) < 0) {
 
89
                free(data);
 
90
                return -1;
 
91
        }
 
92
 
 
93
        *instance = data;
 
94
 
 
95
        return 0;
 
96
}
 
97
 
 
98
/*
 
99
 *      Authenticate the user with the given password.
 
100
 */
 
101
static int smsotp_authenticate(void *instance, REQUEST *request)
 
102
{
 
103
        VALUE_PAIR *state;
 
104
        VALUE_PAIR *reply;
 
105
        rlm_smsotp_t *opt = instance;
 
106
        char SocketReply[1000];
 
107
        int SocketReplyLen;
 
108
 
 
109
        /* quiet the compiler */
 
110
        instance = instance;
 
111
        request = request;
 
112
 
 
113
  smsotp_fd_t *fdp;
 
114
 
 
115
  fdp = smsotp_getfd(instance);
 
116
  if (!fdp || fdp->fd == -1)
 
117
    return RLM_MODULE_FAIL;
 
118
 
 
119
        /* Get greeting */
 
120
        SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
 
121
 
 
122
        /*
 
123
         *  Look for the 'state' attribute.
 
124
         */
 
125
        state = pairfind(request->packet->vps, PW_STATE);
 
126
        if (state != NULL) {
 
127
                DEBUG("rlm_smsotp: Found reply to access challenge");
 
128
                
 
129
                /* set username */
 
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));
 
134
                
 
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));
 
140
                
 
141
                /* set uuid */
 
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));
 
146
                
 
147
                /* now check the otp */
 
148
                smsotp_write(fdp, "get check result\n", 17);
 
149
                SocketReplyLen = smsotp_read(fdp, (char *) SocketReply, sizeof(SocketReply));
 
150
                
 
151
                /* end the sesssion */
 
152
                smsotp_write(fdp, "quit\n", 5);
 
153
                smsotp_putfd(fdp, 1);
 
154
                
 
155
                (void) radlog(L_AUTH, "rlm_smsotp: SocketReply is %s ",SocketReply);
 
156
                
 
157
                if (strcmp(SocketReply,"OK") == 0)
 
158
                        return RLM_MODULE_OK;
 
159
                return RLM_MODULE_FAIL;
 
160
        }
 
161
 
 
162
        DEBUG("rlm_smsotp: Generate OTP");
 
163
  
 
164
        /* set username */
 
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));
 
169
 
 
170
        /* end the sesssion */
 
171
  smsotp_write(fdp, "quit\n", 5);
 
172
        smsotp_putfd(fdp, 1);
 
173
 
 
174
        (void) radlog(L_AUTH, "rlm_smsotp: Uniq id is %s ",SocketReply);
 
175
 
 
176
        /* check the return string */
 
177
        if (strcmp(SocketReply,"FAILED") == 0) { /* smsotp script returns a error */
 
178
                return RLM_MODULE_FAIL;
 
179
        } else {
 
180
                /*
 
181
                 *  Create the challenge, and add it to the reply.
 
182
                 */
 
183
                
 
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);
 
188
        
 
189
                /*
 
190
                 *  Mark the packet as an Access-Challenge packet.
 
191
                 *
 
192
                 *  The server will take care of sending it to the user.
 
193
                 */
 
194
                request->reply->code = PW_ACCESS_CHALLENGE;
 
195
                DEBUG("rlm_smsotp: Sending Access-Challenge.");
 
196
        
 
197
                return RLM_MODULE_HANDLED;
 
198
        }
 
199
}
 
200
 
 
201
/*
 
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.
 
206
 */
 
207
static int smsotp_authorize(void *instance, REQUEST *request)
 
208
{
 
209
        VALUE_PAIR *state;
 
210
        rlm_smsotp_t *opt = instance;
 
211
 
 
212
        /* quiet the compiler */
 
213
        instance = instance;
 
214
        request = request;
 
215
 
 
216
        /*
 
217
         *  Look for the 'state' attribute.
 
218
         */
 
219
        state = pairfind(request->packet->vps, PW_STATE);
 
220
        if (state != NULL) {
 
221
                DEBUG("rlm_smsotp: Found reply to access challenge (AUTZ), Adding Auth-Type '%s'",opt->smsotp_authtype);
 
222
                
 
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));
 
225
        }
 
226
 
 
227
        return RLM_MODULE_OK;
 
228
}
 
229
 
 
230
/*
 
231
 *      Only free memory we allocated.  The strings allocated via
 
232
 *      cf_section_parse() do not need to be freed.
 
233
 */
 
234
static int smsotp_detach(void *instance)
 
235
{
 
236
        free(instance);
 
237
        return 0;
 
238
}
 
239
 
 
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 */
 
244
 
 
245
/* socket functions begin */
 
246
/* connect to socket and return fd */
 
247
static int smsotp_connect(const char *path)
 
248
{
 
249
  int fd;
 
250
  struct sockaddr_un sa;
 
251
  size_t sp_len;                /* sun_path length (strlen) */
 
252
 
 
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__);
 
257
    return -1;
 
258
  }
 
259
  sa.sun_family = AF_UNIX;
 
260
  (void) strcpy(sa.sun_path, path);
 
261
 
 
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));
 
265
    return -1;
 
266
  }
 
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));
 
269
    (void) close(fd);
 
270
    return -1;
 
271
  }
 
272
  return fd;
 
273
}
 
274
 
 
275
/*
 
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
 
281
 * the responses.
 
282
 */
 
283
static smsotp_fd_t * smsotp_getfd(const rlm_smsotp_t *opt)
 
284
{
 
285
  int rc;
 
286
  smsotp_fd_t *fdp;
 
287
 
 
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);
 
291
    if (!rc)
 
292
      if (!strcmp(fdp->path, opt->smsotp_socket))       /* could just use == */
 
293
        break;
 
294
  }
 
295
 
 
296
  if (!fdp) {
 
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);
 
306
    /* initialize */
 
307
    fdp->path = opt->smsotp_socket;
 
308
    fdp->fd = -1;
 
309
  }
 
310
 
 
311
  /* establish connection */
 
312
  if (fdp->fd == -1)
 
313
    fdp->fd = smsotp_connect(fdp->path);
 
314
 
 
315
  return fdp;
 
316
}
 
317
 
 
318
/* release fd, and optionally disconnect from otpd */
 
319
static void smsotp_putfd(smsotp_fd_t *fdp, int disconnect)
 
320
{
 
321
  if (disconnect) {
 
322
    (void) close(fdp->fd);
 
323
    fdp->fd = -1;
 
324
  }
 
325
 
 
326
  /* make connection available to another thread */
 
327
  smsotp_pthread_mutex_unlock(&fdp->mutex);
 
328
}
 
329
 
 
330
/*
 
331
 * Full read with logging, and close on failure.
 
332
 * Returns nread on success, 0 on EOF, -1 on other failures.
 
333
 */
 
334
static int smsotp_read(smsotp_fd_t *fdp, char *buf, size_t len)
 
335
{
 
336
  ssize_t n;
 
337
  size_t nread = 0;     /* bytes read into buf */
 
338
  
 
339
  fd_set rfds;
 
340
  struct timeval tv;
 
341
  int retval;
 
342
  FD_ZERO(&rfds);
 
343
  FD_SET(fdp->fd, &rfds);
 
344
  tv.tv_sec = 0;
 
345
  tv.tv_usec = 0;
 
346
 
 
347
  while (nread < len) {
 
348
    if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
 
349
      if (errno == EINTR) {
 
350
        continue;
 
351
      } else {
 
352
        (void) radlog(L_ERR, "rlm_smsotp: %s: read from socket: %s", __func__, strerror(errno));
 
353
        smsotp_putfd(fdp, 1);
 
354
        return -1;
 
355
      }
 
356
    }
 
357
    if (!n) {
 
358
      (void) radlog(L_ERR, "rlm_smsotp: %s: socket disconnect", __func__);
 
359
      smsotp_putfd(fdp, 1);
 
360
      return 0;
 
361
    }
 
362
    nread += n;
 
363
//    DEBUG("smsotp_read ... read more ?");
 
364
    
 
365
    // check if more data is avalible
 
366
                retval = select(1, &rfds, NULL, NULL, &tv);
 
367
                if (!retval) {
 
368
                        buf[nread]= '\0';
 
369
                        break;
 
370
                }
 
371
//    DEBUG("smsotp_read ... read more ! YES !");
 
372
 
 
373
  } /*while (more to read) */
 
374
 
 
375
  return nread;
 
376
}
 
377
 
 
378
/*
 
379
 * Full write with logging, and close on failure.
 
380
 * Returns 0 on success, errno on failure.
 
381
 */
 
382
static int smsotp_write(smsotp_fd_t *fdp, const char *buf, size_t len)
 
383
{
 
384
  size_t nleft = len;
 
385
  ssize_t nwrote;
 
386
 
 
387
  while (nleft) {
 
388
    if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
 
389
      if (errno == EINTR || errno == EPIPE) {
 
390
        continue;
 
391
      } else {
 
392
        (void) radlog(L_ERR, "rlm_smsotp: %s: write to socket: %s", __func__, strerror(errno));
 
393
        smsotp_putfd(fdp, 1);
 
394
        return errno;
 
395
      }
 
396
    }
 
397
    nleft -= nwrote;
 
398
  }
 
399
 
 
400
  return 0;
 
401
}
 
402
/* socket functions end */
 
403
 
 
404
 
 
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)
 
408
{
 
409
  int rc;
 
410
 
 
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));
 
413
    exit(1);
 
414
  }
 
415
}
 
416
 
 
417
/* guaranteed lock */
 
418
static void _smsotp_pthread_mutex_lock(pthread_mutex_t *mutexp, const char *caller)
 
419
{
 
420
  int rc;
 
421
 
 
422
  if ((rc = pthread_mutex_lock(mutexp))) {
 
423
    (void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_lock: %s", caller, strerror(rc));
 
424
    exit(1);
 
425
  }
 
426
}
 
427
 
 
428
/* guaranteed trylock */
 
429
static int _smsotp_pthread_mutex_trylock(pthread_mutex_t *mutexp, const char *caller)
 
430
{
 
431
  int rc;
 
432
 
 
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));
 
436
    exit(1);
 
437
  }
 
438
 
 
439
  return rc;
 
440
}
 
441
 
 
442
/* guaranteed unlock */
 
443
static void _smsotp_pthread_mutex_unlock(pthread_mutex_t *mutexp, const char *caller)
 
444
{
 
445
  int rc;
 
446
 
 
447
  if ((rc = pthread_mutex_unlock(mutexp))) {
 
448
    (void) radlog(L_ERR|L_CONS, "rlm_smsotp: %s: pthread_mutex_unlock: %s", caller, strerror(rc));
 
449
    exit(1);
 
450
  }
 
451
}
 
452
/* mutex functions end */
 
453
 
 
454
 
 
455
/*
 
456
 *      The module name should be the only globally exported symbol.
 
457
 *      That is, everything else should be 'static'.
 
458
 *
 
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.
 
463
 */
 
464
module_t rlm_smsotp = {
 
465
        RLM_MODULE_INIT,
 
466
        "smsotp",
 
467
        RLM_TYPE_THREAD_SAFE,           /* type */
 
468
        smsotp_instantiate,             /* instantiation */
 
469
        smsotp_detach,                  /* detach */
 
470
        {
 
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 */
 
478
                NULL                    /* post-auth */
 
479
        },
 
480
};