4 * Passcode verification function (otpd client) for rlm_otp.
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.
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.
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
21 * Copyright 2006,2007 TRI-D Systems, Inc.
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
32 #include "otp_pw_valid.h"
40 /* transform otpd return codes into rlm return codes */
45 case OTP_RC_OK: return RLM_MODULE_OK;
46 case OTP_RC_USER_UNKNOWN: return RLM_MODULE_REJECT;
47 case OTP_RC_AUTHINFO_UNAVAIL: return RLM_MODULE_REJECT;
48 case OTP_RC_AUTH_ERR: return RLM_MODULE_REJECT;
49 case OTP_RC_MAXTRIES: return RLM_MODULE_USERLOCK;
50 case OTP_RC_NEXTPASSCODE: return RLM_MODULE_USERLOCK;
51 case OTP_RC_IPIN: return RLM_MODULE_REJECT;
52 case OTP_RC_SERVICE_ERR: return RLM_MODULE_FAIL;
53 default: return RLM_MODULE_FAIL;
57 static otp_fd_t *otp_fd_head;
58 static pthread_mutex_t otp_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
61 * Test for passcode validity by asking otpd.
63 * If challenge is supplied, it is used to generate the card response
64 * against which the passcode will be compared. If challenge is not
65 * supplied, or if the comparison fails, synchronous responses are
66 * generated and tested. NOTE: for async authentications, sync mode
67 * responses are still considered valid! (Assuming module configuration
70 * Returns one of the RLM_MODULE_* codes. passcode is filled in.
71 * NB: The returned passcode will contain the PIN! DO NOT LOG!
74 otp_pw_valid(REQUEST *request, int pwe, const char *challenge,
75 const otp_option_t *opt, char passcode[OTP_MAX_PASSCODE_LEN + 1])
77 otp_request_t otp_request;
78 otp_reply_t otp_reply;
79 VALUE_PAIR *cvp, *rvp;
80 char *username = request->username->vp_strvalue;
83 if (request->username->length > OTP_MAX_USERNAME_LEN) {
84 (void) radlog(L_AUTH, "rlm_otp: username [%s] too long", username);
85 return RLM_MODULE_REJECT;
87 /* we already know challenge is short enough */
89 otp_request.version = 2;
90 (void) strcpy(otp_request.username, username);
91 (void) strcpy(otp_request.challenge, challenge);
92 otp_request.pwe.pwe = pwe;
94 /* otp_pwe_present() (done by caller) guarantees that both of these exist */
95 cvp = pairfind(request->packet->vps, pwattr[pwe - 1]->attr, pwattr[pwe - 1]->vendor);
96 rvp = pairfind(request->packet->vps, pwattr[pwe]->attr, pwattr[pwe]->vendor);
97 /* this is just to quiet Coverity */
99 return RLM_MODULE_REJECT;
102 * Validate available vps based on pwe type.
103 * Unfortunately (?) otpd must do this also.
105 switch (otp_request.pwe.pwe) {
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;
111 (void) strcpy(otp_request.pwe.u.pap.passcode, rvp->vp_strvalue);
115 if (cvp->length > 16) {
116 (void) radlog(L_AUTH, "rlm_otp: CHAP challenge for [%s] too long",
118 return RLM_MODULE_INVALID;
120 if (rvp->length != 17) {
121 (void) radlog(L_AUTH, "rlm_otp: CHAP response for [%s] wrong size",
123 return RLM_MODULE_INVALID;
125 (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_strvalue,
127 otp_request.pwe.u.chap.clen = cvp->length;
128 (void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_strvalue,
130 otp_request.pwe.u.chap.rlen = rvp->length;
134 if (cvp->length != 8) {
135 (void) radlog(L_AUTH, "rlm_otp: MS-CHAP challenge for [%s] wrong size",
137 return RLM_MODULE_INVALID;
139 if (rvp->length != 50) {
140 (void) radlog(L_AUTH, "rlm_otp: MS-CHAP response for [%s] wrong size",
142 return RLM_MODULE_INVALID;
144 (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_strvalue,
146 otp_request.pwe.u.chap.clen = cvp->length;
147 (void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_strvalue,
149 otp_request.pwe.u.chap.rlen = rvp->length;
153 if (cvp->length != 16) {
154 (void) radlog(L_AUTH, "rlm_otp: MS-CHAP2 challenge for [%s] wrong size",
156 return RLM_MODULE_INVALID;
158 if (rvp->length != 50) {
159 (void) radlog(L_AUTH, "rlm_otp: MS-CHAP2 response for [%s] wrong size",
161 return RLM_MODULE_INVALID;
163 (void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_strvalue,
165 otp_request.pwe.u.chap.clen = cvp->length;
166 (void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_strvalue,
168 otp_request.pwe.u.chap.rlen = rvp->length;
170 } /* switch (otp_request.pwe.pwe) */
172 /* last byte must also be a terminator so otpd can verify length easily */
173 otp_request.username[OTP_MAX_USERNAME_LEN] = '\0';
174 otp_request.challenge[OTP_MAX_CHALLENGE_LEN] = '\0';
175 if (otp_request.pwe.pwe == PWE_PAP)
176 otp_request.pwe.u.pap.passcode[OTP_MAX_PASSCODE_LEN] = '\0';
178 otp_request.allow_sync = opt->allow_sync;
179 otp_request.allow_async = opt->allow_async;
180 otp_request.challenge_delay = opt->challenge_delay;
181 otp_request.resync = 1;
183 rc = otp_verify(opt, &otp_request, &otp_reply);
185 (void) strcpy(passcode, otp_reply.passcode);
186 return otprc2rlmrc(rc);
190 * Verify an otp by asking otpd.
191 * Returns an OTP_* code, or -1 on system failure.
195 otp_verify(const otp_option_t *opt,
196 const otp_request_t *request, otp_reply_t *reply)
205 fdp = otp_getfd(opt);
206 if (!fdp || fdp->fd == -1)
209 if (otp_write(fdp, (const char *) request, sizeof(*request)) != 0) {
211 goto retry; /* otpd disconnect */ /*TODO: pause */
216 if ((rc = otp_read(fdp, (char *) reply, sizeof(*reply))) != sizeof(*reply)) {
218 goto retry; /* otpd disconnect */ /*TODO: pause */
223 /* validate the reply */
224 if (reply->version != 1) {
225 (void) radlog(L_AUTH, "rlm_otp: otpd reply for [%s] invalid "
227 request->username, reply->version);
232 if (reply->passcode[OTP_MAX_PASSCODE_LEN] != '\0') {
233 (void) radlog(L_AUTH, "rlm_otp: otpd reply for [%s] invalid (passcode)",
244 * Full read with logging, and close on failure.
245 * Returns nread on success, 0 on EOF, -1 on other failures.
248 otp_read(otp_fd_t *fdp, char *buf, size_t len)
251 size_t nread = 0; /* bytes read into buf */
253 while (nread < len) {
254 if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
255 if (errno == EINTR) {
258 (void) radlog(L_ERR, "rlm_otp: %s: read from otpd: %s",
259 __func__, strerror(errno));
265 (void) radlog(L_ERR, "rlm_otp: %s: otpd disconnect", __func__);
270 } /* while (more to read) */
276 * Full write with logging, and close on failure.
277 * Returns 0 on success, errno on failure.
280 otp_write(otp_fd_t *fdp, const char *buf, size_t len)
286 if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
287 if (errno == EINTR) {
290 (void) radlog(L_ERR, "rlm_otp: %s: write to otpd: %s",
291 __func__, strerror(errno));
302 /* connect to otpd and return fd */
304 otp_connect(const char *path)
307 struct sockaddr_un sa;
308 size_t sp_len; /* sun_path length (strlen) */
310 /* setup for unix domain socket */
311 sp_len = strlen(path);
312 if (sp_len > sizeof(sa.sun_path) - 1) {
313 (void) radlog(L_ERR, "rlm_otp: %s: rendezvous point name too long",
317 sa.sun_family = AF_UNIX;
318 (void) strcpy(sa.sun_path, path);
320 /* connect to otpd */
321 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
322 (void) radlog(L_ERR, "rlm_otp: %s: socket: %s", __func__, strerror(errno));
325 if (connect(fd, (struct sockaddr *) &sa,
326 sizeof(sa.sun_family) + sp_len) == -1) {
327 (void) radlog(L_ERR, "rlm_otp: %s: connect(%s): %s",
328 __func__, path, strerror(errno));
336 * Retrieve an fd (from pool) to use for otpd connection.
337 * It'd be simpler to use TLS but FR can have lots of threads
338 * and we don't want to waste fd's that way.
339 * We can't have a global fd because we'd then be pipelining
340 * requests to otpd and we have no way to demultiplex
344 otp_getfd(const otp_option_t *opt)
349 /* walk the connection pool looking for an available fd */
350 for (fdp = otp_fd_head; fdp; fdp = fdp->next) {
351 rc = otp_pthread_mutex_trylock(&fdp->mutex);
353 if (!strcmp(fdp->path, opt->otpd_rp)) /* could just use == */
358 /* no fd was available, add a new one */
359 fdp = rad_malloc(sizeof(*fdp));
360 otp_pthread_mutex_init(&fdp->mutex, NULL);
361 otp_pthread_mutex_lock(&fdp->mutex);
362 /* insert new fd at head */
363 otp_pthread_mutex_lock(&otp_fd_head_mutex);
364 fdp->next = otp_fd_head;
366 otp_pthread_mutex_unlock(&otp_fd_head_mutex);
368 fdp->path = opt->otpd_rp;
372 /* establish connection */
374 fdp->fd = otp_connect(fdp->path);
379 /* release fd, and optionally disconnect from otpd */
381 otp_putfd(otp_fd_t *fdp, int disconnect)
384 (void) close(fdp->fd);
388 /* make connection available to another thread */
389 otp_pthread_mutex_unlock(&fdp->mutex);