All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_smsotp.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 61a40c71174e995a08f9d9a1c243f524b8d33155 $
19  * @file rlm_smsotp.c
20  * @brief Supports OTP authentication using SMS.
21  *
22  * @copyright 2000,2006 The FreeRADIUS server project
23  * @copyright 2009 Siemens AG, Holger Wolff holger.wolff@siemens.com
24  */
25 RCSID("$Id: 61a40c71174e995a08f9d9a1c243f524b8d33155 $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <sys/un.h>
30 
31 typedef struct rlm_smsotp_t {
32  char const *socket;
33  char const *challenge;
34  char const *authtype;
36 } rlm_smsotp_t;
37 
38 static const CONF_PARSER module_config[] = {
39  { FR_CONF_OFFSET("socket", PW_TYPE_STRING, rlm_smsotp_t, socket), .dflt = "/var/run/smsotp_socket" },
40  { FR_CONF_OFFSET("challenge_message", PW_TYPE_STRING, rlm_smsotp_t, challenge), .dflt = "Enter Mobile PIN" },
41  { FR_CONF_OFFSET("challenge_type", PW_TYPE_STRING, rlm_smsotp_t, authtype), .dflt = "smsotp-reply" },
43 };
44 
45 static int _mod_conn_free(int *fdp)
46 {
47  close(*fdp);
48  return 0;
49 }
50 
51 static void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
52 {
53  int fd;
54  rlm_smsotp_t *inst = instance;
55  int *fdp;
56 
57  fd = fr_socket_client_unix(inst->socket, false);
58  if (fd < 0) {
59  ERROR("Failed opening SMSOTP file %s: %s", inst->socket, fr_syserror(errno));
60  return NULL;
61  }
62 
63  if (fr_socket_wait_for_connect(fd, timeout) < 0) {
64  ERROR("Failed connecting to SMSOTP file %s: %s", inst->socket, fr_syserror(errno));
65  return NULL;
66  }
67 
68  fdp = talloc_zero(ctx, int);
69  talloc_set_destructor(fdp, _mod_conn_free);
70  *fdp = fd;
71 
72  return fdp;
73 }
74 
75 /*
76  * Full read with logging, and close on failure.
77  * Returns nread on success, 0 on EOF, -1 on other failures.
78  */
79 static size_t read_all(int *fdp, char *buf, size_t len)
80 {
81  ssize_t n;
82  size_t total = 0;
83 
84  fd_set fds;
85  struct timeval tv;
86  int retval;
87 
88  FD_ZERO(&fds);
89  FD_SET(*fdp, &fds);
90  tv.tv_sec = 0;
91  tv.tv_usec = 0;
92 
93  while (total < len) {
94  n = read(*fdp, &buf[total], len - total);
95  if (n < 0) {
96  if (errno == EINTR) {
97  continue;
98  }
99  return -1;
100  }
101 
102  /*
103  * Socket was closed. Don't try to re-open it.
104  */
105  if (n == 0) return 0;
106  total += n;
107 
108  /*
109  * Check if there's more data. If not, return
110  * now.
111  */
112  retval = select(1, &fds, NULL, NULL, &tv);
113  if (!retval) {
114  buf[total]= '\0';
115  break;
116  }
117  }
118 
119  return total;
120 }
121 
122 
123 /*
124  * Write all of the data, taking care of EINTR, etc.
125  */
126 static int write_all(int *fdp, char const *buf, size_t len)
127 {
128  size_t left = len;
129  ssize_t n;
130 
131  while (left) {
132  n = write(*fdp, &buf[len - left], left);
133  if (n < 0) {
134  if ((errno == EINTR) || (errno == EPIPE)) {
135  continue;
136  }
137  return -1;
138  }
139  left -= n;
140  }
141 
142  return 0;
143 }
144 
145 
146 /*
147  * Do any per-module initialization that is separate to each
148  * configured instance of the module. e.g. set up connections
149  * to external databases, read configuration files, set up
150  * dictionary entries, etc.
151  *
152  * If configuration information is given in the config section
153  * that must be referenced in later calls, store a handle to it
154  * in *instance otherwise put a null pointer there.
155  */
156 static int mod_instantiate(CONF_SECTION *conf, void *instance)
157 {
158  rlm_smsotp_t *inst = instance;
159  struct sockaddr_un sa;
160  if (strlen(inst->socket) > (sizeof(sa.sun_path) - 1)) {
161  cf_log_err_cs(conf, "Socket filename is too long");
162  return -1;
163  }
164 
165  /*
166  * Initialize the socket pool.
167  */
168  inst->pool = module_connection_pool_init(conf, inst, mod_conn_create, NULL, NULL);
169  if (!inst->pool) {
170  return -1;
171  }
172 
173  return 0;
174 }
175 
176 /*
177  * Authenticate the user with the given password.
178  */
179 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
180 {
181  rlm_smsotp_t *inst = instance;
182  VALUE_PAIR *state;
183  int bufsize;
184  int *fdp;
186  char buffer[1000];
187  char output[1000];
188 
189  fdp = fr_connection_get(inst->pool);
190  if (!fdp) return RLM_MODULE_FAIL;
191 
192  /* Get greeting */
193  bufsize = read_all(fdp, buffer, sizeof(buffer));
194  if (bufsize <= 0) {
195  REDEBUG("Failed reading from socket");
196  goto done;
197  }
198 
199  /*
200  * Look for the 'state' attribute.
201  */
202 #define WRITE_ALL(_a,_b,_c) if (write_all(_a,_b,_c) < 0) goto done;
203  state = fr_pair_find_by_num(request->packet->vps, 0, PW_STATE, TAG_ANY);
204  if (state) {
205  RDEBUG("Found reply to access challenge");
206 
207  /* send username */
208  snprintf(output, sizeof(output), "check otp for %s\n",
209  request->username->vp_strvalue);
210  WRITE_ALL(fdp, output, strlen(output));
211 
212  (void) read_all(fdp, buffer, sizeof(buffer));
213 
214  /* send password */
215  snprintf(output, sizeof(output), "user otp is %s\n",
216  request->password->vp_strvalue);
217  WRITE_ALL(fdp, output, strlen(output));
218 
219  (void) read_all(fdp, buffer, sizeof(buffer));
220 
221  /* set uuid */
222  snprintf(output, sizeof(output), "otp id is %s\n",
223  state->vp_strvalue);
224  WRITE_ALL(fdp, output, strlen(output));
225 
226  (void) read_all(fdp, buffer, sizeof(buffer));
227 
228  /* now check the otp */
229  WRITE_ALL(fdp, "get check result\n", 17);
230 
231  (void) read_all(fdp, buffer, sizeof(buffer));
232 
233  /* end the session */
234  WRITE_ALL(fdp, "quit\n", 5);
235 
236  RDEBUG("answer is %s", buffer);
237  if (strcmp(buffer,"OK") == 0) {
238  rcode = RLM_MODULE_OK;
239  }
240 
241  goto done;
242  }
243 
244  RDEBUG("Generating OTP");
245 
246  /* set username */
247  snprintf(output, sizeof(output), "generate otp for %s\n",
248  request->username->vp_strvalue);
249  WRITE_ALL(fdp, output, strlen(output));
250 
251  (void) read_all(fdp, buffer, sizeof(buffer));
252 
253  /* end the session */
254  WRITE_ALL(fdp, "quit\n", 5);
255 
256  RDEBUG("Unique ID is %s", buffer);
257 
258  /* check the return string */
259  if (strcmp(buffer,"FAILED") == 0) { /* smsotp script returns a error */
260  goto done;
261  }
262 
263  /*
264  * Create the challenge, and add it to the reply.
265  */
266 
267  pair_make_reply("Reply-Message", inst->challenge, T_OP_EQ);
268  pair_make_reply("State", buffer, T_OP_EQ);
269 
270  /*
271  * Mark the packet as an Access-Challenge packet.
272  *
273  * The server will take care of sending it to the user.
274  */
275  request->reply->code = PW_CODE_ACCESS_CHALLENGE;
276  DEBUG("rlm_smsotp: Sending Access-Challenge");
277 
278  rcode = RLM_MODULE_HANDLED;
279 
280 done:
281  fr_connection_release(inst->pool, fdp);
282  return rcode;
283 }
284 
285 /*
286  * Find the named user in this modules database. Create the set
287  * of attribute-value pairs to check and reply with for this user
288  * from the database. The authentication code only needs to check
289  * the password, the rest is done here.
290  */
291 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
292 {
293  VALUE_PAIR *state;
294  rlm_smsotp_t *inst = instance;
295 
296  /*
297  * Look for the 'state' attribute.
298  */
299  state = fr_pair_find_by_num(request->packet->vps, 0, PW_STATE, TAG_ANY);
300  if (state != NULL) {
301  DEBUG("rlm_smsotp: Found reply to access challenge (AUTZ), Adding Auth-Type '%s'",inst->authtype);
302 
303  fr_pair_delete_by_num(&request->config, 0, PW_AUTH_TYPE, TAG_ANY); /* delete old auth-type */
304  pair_make_config("Auth-Type", inst->authtype, T_OP_SET);
305  }
306 
307  return RLM_MODULE_OK;
308 }
309 
310 
311 /*
312  * The module name should be the only globally exported symbol.
313  * That is, everything else should be 'static'.
314  *
315  * If the module needs to temporarily modify it's instantiation
316  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
317  * The server will then take care of ensuring that the module
318  * is single-threaded.
319  */
320 extern module_t rlm_smsotp;
321 module_t rlm_smsotp = {
323  .name = "smsotp",
324  .type = RLM_TYPE_THREAD_SAFE,
325  .inst_size = sizeof(rlm_smsotp_t),
326  .config = module_config,
327  .instantiate = mod_instantiate,
328  .methods = {
331  },
332 };
static int mod_instantiate(CONF_SECTION *conf, void *instance)
Definition: rlm_smsotp.c:156
RFC2865 - Access-Challenge.
Definition: radius.h:102
The module is OK, continue.
Definition: radiusd.h:91
Metadata exported by the module.
Definition: modules.h:134
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
int fr_socket_client_unix(char const *path, bool async)
#define RLM_MODULE_INIT
Definition: modules.h:86
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
static float timeout
Definition: radclient.c:43
#define inst
Definition: token.h:46
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull)
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
fr_connection_pool_t * module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_connection_create_t c, fr_connection_alive_t a, char const *prefix)
Initialise a module specific connection pool.
Definition: modules.c:1759
#define DEBUG(fmt,...)
Definition: log.h:175
#define pair_make_config(_a, _b, _c)
Definition: radiusd.h:547
static int _mod_conn_free(int *fdp)
Definition: rlm_smsotp.c:45
static bool done
Definition: radclient.c:53
char const * socket
Definition: rlm_smsotp.c:32
#define WRITE_ALL(_a, _b, _c)
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
fr_connection_pool_t * pool
Definition: rlm_smsotp.c:35
void void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt,...) CC_HINT(format(printf
0 methods index for authenticate section.
Definition: modules.h:41
Definition: token.h:45
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Definition: rlm_smsotp.c:51
void fr_pair_delete_by_num(VALUE_PAIR **head, unsigned int vendor, unsigned int attr, int8_t tag)
Delete matching pairs.
Definition: pair.c:797
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_smsotp.c:179
module_t rlm_smsotp
Definition: rlm_smsotp.c:321
unsigned int state
Definition: proto_bfd.c:200
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
static int write_all(int *fdp, char const *buf, size_t len)
Definition: rlm_smsotp.c:126
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
#define pair_make_reply(_a, _b, _c)
Definition: radiusd.h:546
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
Definition: connection.c:1291
A connection pool.
Definition: connection.c:85
struct rlm_smsotp_t rlm_smsotp_t
#define REDEBUG(fmt,...)
Definition: log.h:254
char const * challenge
Definition: rlm_smsotp.c:33
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
static size_t read_all(int *fdp, char *buf, size_t len)
Definition: rlm_smsotp.c:79
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
Definition: connection.c:1305
static const CONF_PARSER module_config[]
Definition: rlm_smsotp.c:38
String of printable characters.
Definition: radius.h:33
char const * authtype
Definition: rlm_smsotp.c:34
1 methods index for authorize section.
Definition: modules.h:42
#define RCSID(id)
Definition: build.h:135
int fr_socket_wait_for_connect(int sockfd, struct timeval const *timeout)
Wait for a socket to be connected, with an optional timeout.
Definition: socket.c:359
The module handled the request, so stop.
Definition: radiusd.h:92
#define RDEBUG(fmt,...)
Definition: log.h:243
#define ERROR(fmt,...)
Definition: log.h:145