import from HEAD:
[freeradius.git] / src / modules / rlm_otp / otp_pw_valid.c
1 /*
2  * $Id$
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 #include "ident.h"
25 RCSID("$Id$")
26
27 #include "autoconf.h"
28 #include "radiusd.h"
29 #include "modules.h"
30
31 #include "extern.h"
32 #include "otp.h"
33 #include "otp_pw_valid.h"
34
35 #include <errno.h>
36 #include <pthread.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/un.h>
42 #include <unistd.h>
43
44
45 /* transform otpd return codes into rlm return codes */
46 static int
47 otprc2rlmrc(int rc)
48 {
49   switch (rc) {
50     case OTP_RC_OK:                     return RLM_MODULE_OK;
51     case OTP_RC_USER_UNKNOWN:           return RLM_MODULE_REJECT;
52     case OTP_RC_AUTHINFO_UNAVAIL:       return RLM_MODULE_REJECT;
53     case OTP_RC_AUTH_ERR:               return RLM_MODULE_REJECT;
54     case OTP_RC_MAXTRIES:               return RLM_MODULE_USERLOCK;
55     case OTP_RC_NEXTPASSCODE:           return RLM_MODULE_USERLOCK;
56     case OTP_RC_SERVICE_ERR:            return RLM_MODULE_FAIL;
57     default:                            return RLM_MODULE_FAIL;
58   }
59 }
60
61 static otp_fd_t *otp_fd_head;
62 static pthread_mutex_t otp_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
63
64 /*
65  * Test for passcode validity by asking otpd.
66  *
67  * If challenge is supplied, it is used to generate the card response
68  * against which the passcode will be compared.  If challenge is not
69  * supplied, or if the comparison fails, synchronous responses are
70  * generated and tested.  NOTE: for async authentications, sync mode
71  * responses are still considered valid!  (Assuming module configuration
72  * allows sync mode.)
73  *
74  * Returns one of the RLM_MODULE_* codes.  passcode is filled in.
75  * NB: The returned passcode will contain the PIN!  DO NOT LOG!
76  */
77 int
78 otp_pw_valid(REQUEST *request, int pwe, const char *challenge,
79              const otp_option_t *opt, char passcode[OTP_MAX_PASSCODE_LEN + 1])
80 {
81   otp_request_t otp_request;
82   otp_reply_t   otp_reply;
83   VALUE_PAIR    *cvp, *rvp;
84   char          *username = request->username->strvalue;
85   int           rc;
86
87   if (request->username->length > OTP_MAX_USERNAME_LEN) {
88     (void) radlog(L_AUTH, "rlm_otp: username [%s] too long", username);
89     return RLM_MODULE_REJECT;
90   }
91   /* we already know challenge is short enough */
92
93   otp_request.version = 2;
94   (void) strcpy(otp_request.username, username);
95   (void) strcpy(otp_request.challenge, challenge);
96   otp_request.pwe.pwe = pwe;
97
98   /* otp_pwe_present() (done by caller) guarantees that both of these exist */
99   cvp = pairfind(request->packet->vps, pwattr[pwe - 1]);
100   rvp = pairfind(request->packet->vps, pwattr[pwe]);
101   /* this is just to quiet Coverity */
102   if (!rvp || !cvp)
103     return RLM_MODULE_REJECT;
104
105   /*
106    * Validate available vps based on pwe type.
107    * Unfortunately (?) otpd must do this also.
108    */
109   switch (otp_request.pwe.pwe) {
110   case PWE_PAP:
111     if (rvp->length > OTP_MAX_PASSCODE_LEN) {
112       (void) radlog(L_AUTH, "rlm_otp: passcode for [%s] too long", username);
113       return RLM_MODULE_REJECT;
114     }
115     (void) strcpy(otp_request.pwe.u.pap.passcode, rvp->strvalue);
116     break;
117
118   case PWE_CHAP:
119     if (cvp->length > 16) {
120       (void) radlog(L_AUTH, "rlm_otp: CHAP challenge for [%s] too long",
121                     username);
122       return RLM_MODULE_INVALID;
123     }
124     if (rvp->length != 17) {
125       (void) radlog(L_AUTH, "rlm_otp: CHAP response for [%s] wrong size",
126                     username);
127       return RLM_MODULE_INVALID;
128     }
129     (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->strvalue,
130                   cvp->length);
131     otp_request.pwe.u.chap.clen = cvp->length;
132     (void) memcpy(otp_request.pwe.u.chap.response, rvp->strvalue,
133                   rvp->length);
134     otp_request.pwe.u.chap.rlen = rvp->length;
135     break;
136
137   case PWE_MSCHAP:
138     if (cvp->length != 8) {
139       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP challenge for [%s] wrong size",
140                     username);
141       return RLM_MODULE_INVALID;
142     }
143     if (rvp->length != 50) {
144       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP response for [%s] wrong size",
145                     username);
146       return RLM_MODULE_INVALID;
147     }
148     (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->strvalue,
149                   cvp->length);
150     otp_request.pwe.u.chap.clen = cvp->length;
151     (void) memcpy(otp_request.pwe.u.chap.response, rvp->strvalue,
152                   rvp->length);
153     otp_request.pwe.u.chap.rlen = rvp->length;
154     break;
155
156   case PWE_MSCHAP2:
157     if (cvp->length != 16) {
158       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP2 challenge for [%s] wrong size",
159                     username);
160       return RLM_MODULE_INVALID;
161     }
162     if (rvp->length != 50) {
163       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP2 response for [%s] wrong size",
164                     username);
165       return RLM_MODULE_INVALID;
166     }
167     (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->strvalue,
168                   cvp->length);
169     otp_request.pwe.u.chap.clen = cvp->length;
170     (void) memcpy(otp_request.pwe.u.chap.response, rvp->strvalue,
171                   rvp->length);
172     otp_request.pwe.u.chap.rlen = rvp->length;
173     break;
174   } /* switch (otp_request.pwe.pwe) */
175
176   /* last byte must also be a terminator so otpd can verify length easily */
177   otp_request.username[OTP_MAX_USERNAME_LEN] = '\0';
178   otp_request.challenge[OTP_MAX_CHALLENGE_LEN] = '\0';
179   if (otp_request.pwe.pwe == PWE_PAP)
180     otp_request.pwe.u.pap.passcode[OTP_MAX_PASSCODE_LEN] = '\0';
181
182   otp_request.allow_sync = opt->allow_sync;
183   otp_request.allow_async = opt->allow_async;
184   otp_request.challenge_delay = opt->challenge_delay;
185   otp_request.resync = 1;
186
187   rc = otp_verify(opt, &otp_request, &otp_reply);
188   if (rc == OTP_RC_OK)
189     (void) strcpy(passcode, otp_reply.passcode);
190   return otprc2rlmrc(rc);
191 }
192
193 /*
194  * Verify an otp by asking otpd.
195  * Returns an OTP_* code, or -1 on system failure.
196  * Fills in reply.
197  */
198 static int
199 otp_verify(const otp_option_t *opt,
200            const otp_request_t *request, otp_reply_t *reply)
201 {
202   otp_fd_t *fdp;
203   int rc;
204   int tryagain = 2;
205
206 retry:
207   if (!tryagain--)
208     return -1;
209   fdp = otp_getfd(opt);
210   if (!fdp || fdp->fd == -1)
211     return -1;
212
213   if ((rc = otp_write(fdp, (const char *) request, sizeof(*request))) != 0) {
214     if (rc == EPIPE)
215       goto retry;       /* otpd disconnect */   /*TODO: pause */
216     else
217       return -1;
218   }
219
220   if ((rc = otp_read(fdp, (char *) reply, sizeof(*reply))) != sizeof(*reply)) {
221     if (rc == 0)
222       goto retry;       /* otpd disconnect */   /*TODO: pause */
223     else
224       return -1;
225   }
226
227   /* validate the reply */
228   if (reply->version != 1) {
229     (void) radlog(L_AUTH, "rlm_otp: otpd reply for [%s] invalid "
230                           "(version %d != 1)",
231                   request->username, reply->version);
232     otp_putfd(fdp, 1);
233     return -1;
234   }
235
236   if (reply->passcode[OTP_MAX_PASSCODE_LEN] != '\0') {
237     (void) radlog(L_AUTH, "rlm_otp: otpd reply for [%s] invalid (passcode)",
238                   request->username);
239     otp_putfd(fdp, 1);
240     return -1;
241   }
242
243   otp_putfd(fdp, 0);
244   return reply->rc;
245 }
246
247 /*
248  * Full read with logging, and close on failure.
249  * Returns nread on success, 0 on EOF, -1 on other failures.
250  */
251 static int
252 otp_read(otp_fd_t *fdp, char *buf, size_t len)
253 {
254   ssize_t n;
255   size_t nread = 0;     /* bytes read into buf */
256
257   while (nread < len) {
258     if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
259       if (errno == EINTR) {
260         continue;
261       } else {
262         (void) radlog(L_ERR, "rlm_otp: %s: read from otpd: %s",
263                       __func__, strerror(errno));
264         otp_putfd(fdp, 1);
265         return -1;
266       }
267     }
268     if (!n) {
269       (void) radlog(L_ERR, "rlm_otp: %s: otpd disconnect", __func__);
270       otp_putfd(fdp, 1);
271       return 0;
272     }
273     nread += n;
274   } /* while (more to read) */
275
276   return nread;
277 }
278
279 /*
280  * Full write with logging, and close on failure.
281  * Returns 0 on success, errno on failure.
282  */
283 static int
284 otp_write(otp_fd_t *fdp, const char *buf, size_t len)
285 {
286   size_t nleft = len;
287   ssize_t nwrote;
288
289   while (nleft) {
290     if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
291       if (errno == EINTR || errno == EPIPE) {
292         continue;
293       } else {
294         (void) radlog(L_ERR, "rlm_otp: %s: write to otpd: %s",
295                       __func__, strerror(errno));
296         otp_putfd(fdp, 1);
297         return errno;
298       }
299     }
300     nleft -= nwrote;
301   }
302
303   return 0;
304 }
305
306 /* connect to otpd and return fd */
307 static int
308 otp_connect(const char *path)
309 {
310   int fd;
311   struct sockaddr_un sa;
312   size_t sp_len;                /* sun_path length (strlen) */
313
314   /* setup for unix domain socket */
315   sp_len = strlen(path);
316   if (sp_len > sizeof(sa.sun_path) - 1) {
317     (void) radlog(L_ERR, "rlm_otp: %s: rendezvous point name too long",
318                   __func__);
319     return -1;
320   }
321   sa.sun_family = AF_UNIX;
322   (void) strcpy(sa.sun_path, path);
323
324   /* connect to otpd */
325   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
326     (void) radlog(L_ERR, "rlm_otp: %s: socket: %s", __func__, strerror(errno));
327     return -1;
328   }
329   if (connect(fd, (struct sockaddr *) &sa,
330               sizeof(sa.sun_family) + sp_len) == -1) {
331     (void) radlog(L_ERR, "rlm_otp: %s: connect(%s): %s",
332                   __func__, path, strerror(errno));
333     (void) close(fd);
334     return -1;
335   }
336   return fd;
337 }
338
339 /*
340  * Retrieve an fd (from pool) to use for otpd connection.
341  * It'd be simpler to use TLS but FR can have lots of threads
342  * and we don't want to waste fd's that way.
343  * We can't have a global fd because we'd then be pipelining
344  * requests to otpd and we have no way to demultiplex
345  * the responses.
346  */
347 static otp_fd_t *
348 otp_getfd(const otp_option_t *opt)
349 {
350   int rc;
351   otp_fd_t *fdp;
352
353   /* walk the connection pool looking for an available fd */
354   for (fdp = otp_fd_head; fdp; fdp = fdp->next) {
355     rc = otp_pthread_mutex_trylock(&fdp->mutex);
356     if (!rc)
357       if (!strcmp(fdp->path, opt->otpd_rp))     /* could just use == */
358         break;
359   }
360
361   if (!fdp) {
362     /* no fd was available, add a new one */
363     fdp = rad_malloc(sizeof(*fdp));
364     otp_pthread_mutex_init(&fdp->mutex, NULL);
365     otp_pthread_mutex_lock(&fdp->mutex);
366     /* insert new fd at head */
367     otp_pthread_mutex_lock(&otp_fd_head_mutex);
368     fdp->next = otp_fd_head;
369     otp_fd_head = fdp;
370     otp_pthread_mutex_unlock(&otp_fd_head_mutex);
371     /* initialize */
372     fdp->path = opt->otpd_rp;
373     fdp->fd = -1;
374   }
375
376   /* establish connection */
377   if (fdp->fd == -1)
378     fdp->fd = otp_connect(fdp->path);
379
380   return fdp;
381 }
382
383 /* release fd, and optionally disconnect from otpd */
384 static void
385 otp_putfd(otp_fd_t *fdp, int disconnect)
386 {
387   if (disconnect) {
388     (void) close(fdp->fd);
389     fdp->fd = -1;
390   }
391
392   /* make connection available to another thread */
393   otp_pthread_mutex_unlock(&fdp->mutex);
394 }