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