All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
otp_pw_valid.c
Go to the documentation of this file.
1 /*
2  * $Id: bb7319ffc1a7c51a2038d6f5b6d47af24abf4657 $
3  *
4  * Passcode verification function (otpd client) for rlm_otp.
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  *
21  * Copyright 2006,2007 TRI-D Systems, Inc.
22  */
23 
24 RCSID("$Id: bb7319ffc1a7c51a2038d6f5b6d47af24abf4657 $")
25 
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 
29 #include "extern.h"
30 #include "otp.h"
31 #include "otp_pw_valid.h"
32 
33 #ifdef HAVE_PTHREAD_H
34 #include <pthread.h>
35 #endif
36 #include <sys/un.h>
37 
38 
39 /* transform otpd return codes into rlm return codes */
40 static int otprc2rlmrc(int rc)
41 {
42  switch (rc) {
43  case OTP_RC_OK: return RLM_MODULE_OK;
49  case OTP_RC_IPIN: return RLM_MODULE_REJECT;
51  default: return RLM_MODULE_FAIL;
52  }
53 }
54 
56 static pthread_mutex_t otp_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
57 
58 /*
59  * Test for passcode validity by asking otpd.
60  *
61  * If challenge is supplied, it is used to generate the card response
62  * against which the passcode will be compared. If challenge is not
63  * supplied, or if the comparison fails, synchronous responses are
64  * generated and tested. NOTE: for async authentications, sync mode
65  * responses are still considered valid! (Assuming module configuration
66  * allows sync mode.)
67  *
68  * Returns one of the RLM_MODULE_* codes. passcode is filled in.
69  * NB: The returned passcode will contain the PIN! DO NOT LOG!
70  */
71 int otp_pw_valid(REQUEST *request, int pwe, char const *challenge,
72  rlm_otp_t const *opt,
73  char passcode[OTP_MAX_PASSCODE_LEN + 1])
74 {
75  otp_request_t otp_request;
76  otp_reply_t otp_reply;
77  VALUE_PAIR *cvp, *rvp;
78  char const *username = request->username->vp_strvalue;
79  int rc;
80 
81  otp_request.version = 2;
82 
83  if (strlcpy(otp_request.username, username,
84  sizeof(otp_request.username)) >=
85  sizeof(otp_request.username)) {
86  AUTH("rlm_otp: username [%s] too long", username);
87  return RLM_MODULE_REJECT;
88  }
89  if (strlcpy(otp_request.challenge, challenge,
90  sizeof(otp_request.challenge)) >=
91  sizeof(otp_request.challenge)) {
92  AUTH("rlm_otp: challenge for [%s] too long", username);
93  return RLM_MODULE_REJECT;
94  }
95 
96  otp_request.pwe.pwe = pwe;
97 
98  /*
99  * otp_pwe_present() (done by caller) guarantees that both of
100  * these exist
101  */
102  cvp = fr_pair_find_by_num(request->packet->vps, pwattr[pwe - 1]->vendor, pwattr[pwe - 1]->attr, TAG_ANY);
103 
104  rvp = fr_pair_find_by_num(request->packet->vps, pwattr[pwe]->vendor, pwattr[pwe]->attr, TAG_ANY);
105 
106  /* this is just to quiet Coverity */
107  if (!rvp || !cvp) {
108  return RLM_MODULE_REJECT;
109  }
110 
111  /*
112  * Validate available vps based on pwe type.
113  * Unfortunately (?) otpd must do this also.
114  */
115  switch (otp_request.pwe.pwe) {
116  case PWE_NONE:
117  return RLM_MODULE_NOOP;
118 
119  case PWE_PAP:
120  if (strlcpy(otp_request.pwe.u.pap.passcode, rvp->vp_strvalue,
121  sizeof(otp_request.pwe.u.pap.passcode)) >=
122  sizeof(otp_request.pwe.u.pap.passcode)) {
123  AUTH("rlm_otp: passcode for [%s] too long",
124  username);
125 
126  return RLM_MODULE_REJECT;
127  }
128  break;
129 
130  case PWE_CHAP:
131  if (cvp->vp_length > 16) {
132  AUTH("rlm_otp: CHAP challenge for [%s] "
133  "too long", username);
134 
135  return RLM_MODULE_INVALID;
136  }
137 
138  if (rvp->vp_length != 17) {
139  AUTH("rlm_otp: CHAP response for [%s] "
140  "wrong size", username);
141 
142  return RLM_MODULE_INVALID;
143  }
144 
145  (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_octets,
146  cvp->vp_length);
147 
148  otp_request.pwe.u.chap.clen = cvp->vp_length;
149  (void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_octets,
150  rvp->vp_length);
151 
152  otp_request.pwe.u.chap.rlen = rvp->vp_length;
153  break;
154 
155  case PWE_MSCHAP:
156  if (cvp->vp_length != 8) {
157  AUTH("rlm_otp: MS-CHAP challenge for "
158  "[%s] wrong size", username);
159 
160  return RLM_MODULE_INVALID;
161  }
162 
163  if (rvp->vp_length != 50) {
164  AUTH("rlm_otp: MS-CHAP response for [%s] "
165  "wrong size", username);
166 
167  return RLM_MODULE_INVALID;
168  }
169  (void) memcpy(otp_request.pwe.u.chap.challenge,
170  cvp->vp_octets, cvp->vp_length);
171 
172  otp_request.pwe.u.chap.clen = cvp->vp_length;
173 
174  (void) memcpy(otp_request.pwe.u.chap.response,
175  rvp->vp_octets, rvp->vp_length);
176 
177  otp_request.pwe.u.chap.rlen = rvp->vp_length;
178  break;
179 
180  case PWE_MSCHAP2:
181  if (cvp->vp_length != 16) {
182  AUTH("rlm_otp: MS-CHAP2 challenge for "
183  "[%s] wrong size", username);
184 
185  return RLM_MODULE_INVALID;
186  }
187 
188  if (rvp->vp_length != 50) {
189  AUTH("rlm_otp: MS-CHAP2 response for [%s] "
190  "wrong size", username);
191 
192  return RLM_MODULE_INVALID;
193  }
194 
195  (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_octets,
196  cvp->vp_length);
197 
198  otp_request.pwe.u.chap.clen = cvp->vp_length;
199 
200  (void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_octets,
201  rvp->vp_length);
202  otp_request.pwe.u.chap.rlen = rvp->vp_length;
203  break;
204  } /* switch (otp_request.pwe.pwe) */
205 
206  /*
207  * last byte must also be a terminator so otpd can verify
208  * length easily.
209  */
210  otp_request.username[OTP_MAX_USERNAME_LEN] = '\0';
211  otp_request.challenge[OTP_MAX_CHALLENGE_LEN] = '\0';
212 
213  if (otp_request.pwe.pwe == PWE_PAP) {
214  otp_request.pwe.u.pap.passcode[OTP_MAX_PASSCODE_LEN] = '\0';
215  }
216 
217  otp_request.allow_sync = opt->allow_sync;
218  otp_request.allow_async = opt->allow_async;
219  otp_request.challenge_delay = opt->challenge_delay;
220  otp_request.resync = 1;
221 
222 
223  rc = otp_verify(opt, &otp_request, &otp_reply);
224  if (rc == OTP_RC_OK) {
225  (void) strcpy(passcode, otp_reply.passcode);
226  }
227 
228  return otprc2rlmrc(rc);
229 }
230 
231 /*
232  * Verify an otp by asking otpd.
233  * Returns an OTP_* code, or -1 on system failure.
234  * Fills in reply.
235  */
236 static int otp_verify(rlm_otp_t const *opt,
237  otp_request_t const *request, otp_reply_t *reply)
238 {
239  otp_fd_t *fdp;
240  int rc;
241  int tryagain = 2;
242 
243  retry:
244  if (!tryagain--) {
245  return -1;
246  }
247 
248  fdp = otp_getfd(opt);
249  if (!fdp || fdp->fd == -1) {
250  return -1;
251  }
252 
253  rc = otp_write(fdp, (char const *) request, sizeof(*request));
254  if (rc != sizeof(*request)) {
255  if (rc == 0) {
256  goto retry; /* otpd disconnect */ /*TODO: pause */
257  } else {
258  return -1;
259  }
260  }
261 
262  rc = otp_read(fdp, (char *) reply, sizeof(*reply));
263  if (rc != sizeof(*reply)) {
264  if (rc == 0) {
265  goto retry; /* otpd disconnect */ /*TODO: pause */
266  } else {
267  return -1;
268  }
269  }
270 
271  /* validate the reply */
272  if (reply->version != 1) {
273  AUTH("rlm_otp: otpd reply for [%s] invalid "
274  "(version %d != 1)", request->username, reply->version);
275 
276  otp_putfd(fdp, 1);
277  return -1;
278  }
279 
280  if (reply->passcode[OTP_MAX_PASSCODE_LEN] != '\0') {
281  AUTH("rlm_otp: otpd reply for [%s] invalid "
282  "(passcode)", request->username);
283 
284  otp_putfd(fdp, 1);
285  return -1;
286  }
287 
288  otp_putfd(fdp, 0);
289  return reply->rc;
290 }
291 
292 /*
293  * Full read with logging, and close on failure.
294  * Returns nread on success, 0 on EOF, -1 on other failures.
295  */
296 static int
297 otp_read(otp_fd_t *fdp, char *buf, size_t len)
298 {
299  ssize_t n;
300  size_t nread = 0; /* bytes read into buf */
301 
302  while (nread < len) {
303  n = read(fdp->fd, &buf[nread], len - nread);
304  if (n == -1) {
305  if (errno == EINTR) {
306  continue;
307  } else {
308  ERROR("rlm_otp: %s: read from otpd: %s",
309  __func__, fr_syserror(errno));
310  otp_putfd(fdp, 1);
311 
312  return -1;
313  }
314  }
315 
316  if (!n) {
317  ERROR("rlm_otp: %s: otpd disconnect", __func__);
318  otp_putfd(fdp, 1);
319 
320  return 0;
321  }
322 
323  nread += n;
324  } /* while (more to read) */
325 
326  return nread;
327 }
328 
329 /*
330  * Full write with logging, and close on failure.
331  * Returns number of bytes written on success, errno on failure.
332  */
333 static int otp_write(otp_fd_t *fdp, char const *buf, size_t len)
334 {
335  size_t nleft = len;
336  ssize_t nwrote;
337 
338  while (nleft) {
339  nwrote = write(fdp->fd, &buf[len - nleft], nleft);
340  if (nwrote == -1) {
341  if (errno == EINTR) {
342  continue;
343  } else {
344  ERROR("rlm_otp: %s: write to otpd: %s",
345  __func__, fr_syserror(errno));
346 
347  otp_putfd(fdp, 1);
348  return errno;
349 
350  }
351  }
352 
353  nleft -= nwrote;
354  }
355 
356  return len - nleft;
357 }
358 
359 /* connect to otpd and return fd */
360 static int otp_connect(char const *path)
361 {
362  int fd;
363  struct sockaddr_un sa;
364  size_t sp_len; /* sun_path length (strlen) */
365 
366  /* setup for unix domain socket */
367  sp_len = strlen(path);
368  if (sp_len > sizeof(sa.sun_path) - 1) {
369  ERROR("rlm_otp: %s: rendezvous point name too long",
370  __func__);
371 
372  return -1;
373  }
374  sa.sun_family = AF_UNIX;
375  (void) strcpy(sa.sun_path, path);
376 
377  /* connect to otpd */
378  fd = socket(PF_UNIX, SOCK_STREAM, 0);
379  if (fd == -1) {
380  ERROR("rlm_otp: %s: socket: %s", __func__,
381  fr_syserror(errno));
382 
383  return -1;
384  }
385  if (connect(fd, (struct sockaddr *) &sa,
386  sizeof(sa.sun_family) + sp_len) == -1) {
387 
388  ERROR("rlm_otp: %s: connect(%s): %s",
389  __func__, path, fr_syserror(errno));
390 
391  (void) close(fd);
392 
393  return -1;
394  }
395 
396  return fd;
397 }
398 
399 /*
400  * Retrieve an fd (from pool) to use for otpd connection.
401  * It'd be simpler to use TLS but FR can have lots of threads
402  * and we don't want to waste fd's that way.
403  * We can't have a global fd because we'd then be pipelining
404  * requests to otpd and we have no way to demultiplex
405  * the responses.
406  */
407 static otp_fd_t * otp_getfd(rlm_otp_t const *opt)
408 {
409  int rc;
410  otp_fd_t *fdp;
411 
412  /* walk the connection pool looking for an available fd */
413  for (fdp = otp_fd_head; fdp; fdp = fdp->next) {
414  rc = otp_pthread_mutex_trylock(&fdp->mutex);
415  if (!rc) {
416  if (!strcmp(fdp->path, opt->otpd_rp)) { /* could just use == */
417  break;
418  }
419  }
420  }
421 
422  if (!fdp) {
423  /* no fd was available, add a new one */
424  fdp = rad_malloc(sizeof(*fdp));
425  otp_pthread_mutex_init(&fdp->mutex, NULL);
427 
428  /* insert new fd at head */
429  otp_pthread_mutex_lock(&otp_fd_head_mutex);
430  fdp->next = otp_fd_head;
431  otp_fd_head = fdp;
432  otp_pthread_mutex_unlock(&otp_fd_head_mutex);
433 
434  /* initialize */
435  fdp->path = opt->otpd_rp;
436  fdp->fd = -1;
437  }
438 
439  /* establish connection */
440  if (fdp->fd == -1) {
441  fdp->fd = otp_connect(fdp->path);
442  }
443 
444  return fdp;
445 }
446 
447 /* release fd, and optionally disconnect from otpd */
448 static void otp_putfd(otp_fd_t *fdp, int disconnect)
449 {
450  if (disconnect) {
451  (void) close(fdp->fd);
452  fdp->fd = -1;
453  }
454 
455  /* make connection available to another thread */
457 }
Definition: otp.h:58
#define OTP_RC_AUTHINFO_UNAVAIL
Definition: otp.h:42
uint32_t challenge_delay
Max delay time for response, in seconds.
Definition: extern.h:50
char challenge[OTP_MAX_CHALLENGE_LEN+1]
USER challenge.
Definition: otp.h:78
const fr_dict_attr_t * pwattr[8]
Definition: otp_pwe.c:48
char const * otpd_rp
Otpd rendezvous point.
Definition: extern.h:43
#define AUTH(fmt,...)
Definition: log.h:139
pthread_mutex_t mutex
Definition: otp_pw_valid.h:32
#define OTP_RC_IPIN
Definition: otp.h:47
The module is OK, continue.
Definition: radiusd.h:91
void * rad_malloc(size_t size)
Definition: util.c:411
char passcode[OTP_MAX_PASSCODE_LEN+1]
Definition: otp.h:98
bool allow_async
C/R mode allowed?
Definition: extern.h:53
static int otp_write(otp_fd_t *fdp, char const *buf, size_t len)
Definition: otp_pw_valid.c:333
static otp_fd_t * otp_fd_head
Definition: otp_pw_valid.c:55
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:222
static otp_fd_t * otp_getfd(rlm_otp_t const *opt)
Definition: otp_pw_valid.c:407
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
Definition: otp.h:59
Definition: otp.h:60
#define otp_pthread_mutex_trylock(a)
Definition: extern.h:95
#define OTP_RC_OK
Definition: otp.h:40
static int otp_read(otp_fd_t *fdp, char *buf, size_t len)
Definition: otp_pw_valid.c:297
The module considers the request invalid.
Definition: radiusd.h:93
int version
Should be 2.
Definition: otp.h:76
int rc
Definition: otp.h:97
int otp_pw_valid(REQUEST *request, int pwe, char const *challenge, rlm_otp_t const *opt, char passcode[OTP_MAX_PASSCODE_LEN+1])
Definition: otp_pw_valid.c:71
#define OTP_RC_SERVICE_ERR
Definition: otp.h:45
Reject the request (user is locked out).
Definition: radiusd.h:94
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
#define OTP_RC_MAXTRIES
Definition: otp.h:44
char username[OTP_MAX_USERNAME_LEN+1]
Definition: otp.h:77
unsigned int attr
Attribute number.
Definition: dict.h:79
Immediately reject the request.
Definition: radiusd.h:89
int resync
Resync on async auth?
Definition: otp.h:92
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
static int otprc2rlmrc(int rc)
Definition: otp_pw_valid.c:40
#define otp_pthread_mutex_unlock(a)
Definition: extern.h:96
static pthread_mutex_t otp_fd_head_mutex
Definition: otp_pw_valid.c:56
#define OTP_RC_NEXTPASSCODE
Definition: otp.h:46
Module succeeded without doing anything.
Definition: radiusd.h:96
#define OTP_RC_USER_UNKNOWN
Definition: otp.h:41
char const * path
Definition: otp_pw_valid.h:33
bool allow_sync
Sync auth allowed?
Definition: otp.h:89
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
bool allow_async
Async auth allowed?
Definition: otp.h:88
int version
Should be 1.
Definition: otp.h:96
#define otp_pthread_mutex_lock(a)
Definition: extern.h:94
#define OTP_MAX_CHALLENGE_LEN
Definition: otp.h:35
#define OTP_MAX_PASSCODE_LEN
Definition: otp.h:52
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
static int otp_connect(char const *path)
Definition: otp_pw_valid.c:360
Definition: otp.h:57
static void otp_putfd(otp_fd_t *fdp, int disconnect)
Definition: otp_pw_valid.c:448
bool allow_sync
Useful to override pwdfile card_type settings.
Definition: extern.h:51
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
struct otp_request_t::@20 pwe
unsigned challenge_delay
Min delay between async auths.
Definition: otp.h:90
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
#define otp_pthread_mutex_init(a, b)
Definition: extern.h:93
#define RCSID(id)
Definition: build.h:135
#define OTP_MAX_USERNAME_LEN
Definition: otp.h:49
#define ERROR(fmt,...)
Definition: log.h:145
#define OTP_RC_AUTH_ERR
Definition: otp.h:43
struct otp_fd_t * next
Definition: otp_pw_valid.h:35
static int otp_verify(rlm_otp_t const *opt, otp_request_t const *request, otp_reply_t *reply)
Definition: otp_pw_valid.c:236