3
* $Id: otp_state.c,v 1.23.2.2 2006/01/10 14:33:16 nbk Exp $
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
* Copyright 2005 TRI-D Systems, Inc.
33
#include <sys/types.h>
37
#include <sys/socket.h>
38
#if defined(__linux__) || defined(__APPLE__)
43
#include "otp_state.h"
45
static const char rcsid[] = "$Id: otp_state.c,v 1.23.2.2 2006/01/10 14:33:16 nbk Exp $";
49
/* a single fd (no pool) */
50
static lsmd_fd_t lsmd_fd = { .fd = -1 };
51
#elif defined(FREERADIUS)
52
/* pointer to head of fd pool */
53
static lsmd_fd_t *lsmd_fd_head;
54
static pthread_mutex_t lsmd_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
58
* lock and retrieve state for a user
59
* returns 0 on success (but state may be empty!), -1 on failure
62
otp_state_get(const otp_option_t *opt, const char *username,
63
otp_user_state_t *user_state, const char *log_prefix)
66
char buf[1024]; /* state manager max len */
69
fdp = otp_state_getfd(opt, log_prefix);
70
if (!fdp || fdp->fd == -1)
73
user_state->fdp = fdp;
74
(void) sprintf(buf, "G %s", username); /* safe */
75
if (xwrite(fdp, buf, strlen(buf) + 1, log_prefix) == -1)
77
if ((buflen = xread(fdp, buf, sizeof(buf), log_prefix)) == -1)
79
if (otp_state_parse(buf, buflen, username, user_state, log_prefix) == -1)
87
* update and release state for a user
88
* returns 0 on success, -1 on failure
91
otp_state_put(const char *username, otp_user_state_t *user_state,
92
const char *log_prefix)
94
char buf[1024]; /* state manager max len */
97
size_t ulen = strlen(username);
99
if (!user_state->locked)
102
if ((len = otp_state_unparse(buf, sizeof(buf), username, user_state,
103
log_prefix)) == -1) {
107
if ((rc = xwrite(user_state->fdp, buf, len, log_prefix)) == -1)
109
if ((len = xread(user_state->fdp, buf, sizeof(buf), log_prefix)) == -1) {
114
/* validate the state manager response */
115
if ((size_t) len < 3 + ulen) {
116
otp_log(OTP_LOG_ERR, "%s: %s: state manager invalid PUT response for [%s]",
117
log_prefix, __func__, username);
121
if (!((buf[0] == 'A' || buf[0] == 'N') &&
123
!strncmp(username, &buf[2], ulen) &&
124
(buf[ulen + 2] == ' ' || buf[ulen + 2] == '\0'))) {
125
otp_log(OTP_LOG_ERR, "%s: %s: state manager invalid PUT response for [%s]",
126
log_prefix, __func__, username);
133
if (buf[ulen + 2] == '\0')
134
reason = (char *) "[no reason given]";
136
reason = &buf[ulen + 3];
137
otp_log(OTP_LOG_ERR, "%s: %s: state manager PUT rejected for [%s]: %s",
138
log_prefix, __func__, username, reason);
143
/* reset locked flag on successful PUT, to avoid further PUT's by caller */
144
user_state->locked = 0;
147
otp_state_putfd(user_state->fdp, 0, log_prefix);
153
* Parse the state manager response into user_state.
154
* Returns 0 on success, -1 on failure.
155
* "PUT"s state (releases lock, without update) on failure.
158
otp_state_parse(const char *buf, size_t buflen, const char *username,
159
otp_user_state_t *user_state, const char *log_prefix)
166
otp_log(OTP_LOG_ERR, "%s: %s: no state for [%s]",
167
log_prefix, __func__, username);
168
otp_state_putfd(user_state->fdp, 0, log_prefix);
172
* This guarantees there is a char after strchr(p, ':'),
173
* and that our 'q = strchr(p, ':'); *q++ = '\0', p = q;'
174
* idiom works (there is always a char after the ':').
176
if (buf[buflen - 1] != '\0') {
177
otp_log(OTP_LOG_ERR, "%s: %s: invalid state for [%s]",
178
log_prefix, __func__, username);
179
otp_state_putfd(user_state->fdp, 0, log_prefix);
183
/* Is this an ack or a nak? */
184
if (!(buf[0] == 'A' && buf[1] == ' ')) {
185
otp_log(OTP_LOG_INFO, "%s: %s: unable to lock state for [%s]",
186
log_prefix, __func__, username);
187
otp_state_putfd(user_state->fdp, 0, log_prefix);
190
user_state->locked = 1;
191
user_state->updated = 0; /* just release lock on failures */
194
* We don't do bounds checking for initial parsing,
195
* so state manager response must contain at least
196
* - ACK/NAK code + ' '
200
* - challenge + terminator.
201
* Beginning with the challenge we use strchr() and need
202
* no further bounds checking.
204
i = strlen(username);
205
/* 'A <username> V:<username>:C' + terminator */
206
if (buflen < 2 + i + 3 + i + 2 + 1) {
207
if (buflen < 2 + i + 1) {
208
otp_log(OTP_LOG_ERR, "%s: %s: invalid state data for [%s]",
209
log_prefix, __func__, username);
210
} else if (buflen == 2 + i + 1) {
211
otp_log(OTP_LOG_DEBUG, "%s: %s: null state data for [%s]",
212
log_prefix, __func__, username);
213
user_state->nullstate = 1;
216
otp_log(OTP_LOG_ERR, "%s: %s: short state data for [%s]",
217
log_prefix, __func__, username);
219
(void) otp_state_put(username, user_state, log_prefix);
222
user_state->nullstate = 0;
224
p = (char *) &buf[2]; /* username field of state manager response */
226
/* verify username (in state manager response, not state itself) */
227
if (!(strncmp(p, username, i) == 0 && p[i] == ' ')) {
228
otp_log(OTP_LOG_ERR, "%s: %s: state manager username mismatch for [%s]",
229
log_prefix, __func__, username);
230
(void) otp_state_put(username, user_state, log_prefix);
233
p += i; /* space after username */
234
p += 1; /* beginning of state */
237
if (!(p[0] == '5' && p[1] == ':')) {
238
otp_log(OTP_LOG_ERR, "%s: %s: state data unacceptable version for [%s]",
239
log_prefix, __func__, username);
240
(void) otp_state_put(username, user_state, log_prefix);
243
p += 2; /* username */
245
/* sanity check username */
246
if (!(strncmp(p, username, i) == 0 && p[i] == ':')) {
247
otp_log(OTP_LOG_ERR, "%s: %s: state data username mismatch for [%s]",
248
log_prefix, __func__, username);
249
(void) otp_state_put(username, user_state, log_prefix);
252
p += i + 1; /* challenge */
254
/* extract challenge */
255
if ((q = strchr(p, ':')) == NULL) {
256
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid challenge for [%s]",
257
log_prefix, __func__, username);
258
(void) otp_state_put(username, user_state, log_prefix);
262
if (strlen(p) > OTP_MAX_CHALLENGE_LEN * 2) {
263
otp_log(OTP_LOG_ERR, "%s: %s: state data challenge too long for [%s]",
264
log_prefix, __func__, username);
265
(void) otp_state_put(username, user_state, log_prefix);
268
user_state->clen = otp_keystring2keyblock(p, user_state->challenge);
269
if (user_state->clen < 0) {
270
otp_log(OTP_LOG_ERR, "%s: %s: state data challenge invalid for [%s]",
271
log_prefix, __func__, username);
272
(void) otp_state_put(username, user_state, log_prefix);
278
if ((q = strchr(p, ':')) == NULL) {
279
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid csd for [%s]",
280
log_prefix, __func__, username);
281
(void) otp_state_put(username, user_state, log_prefix);
285
if (strlen(p) > OTP_MAX_CSD_LEN) {
286
otp_log(OTP_LOG_ERR, "%s: %s: state data csd too long for [%s]",
287
log_prefix, __func__, username);
288
(void) otp_state_put(username, user_state, log_prefix);
291
(void) strcpy(user_state->csd, p);
295
if ((q = strchr(p, ':')) == NULL) {
296
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid rd for [%s]",
297
log_prefix, __func__, username);
298
(void) otp_state_put(username, user_state, log_prefix);
302
if (strlen(p) > OTP_MAX_RD_LEN) {
303
otp_log(OTP_LOG_ERR, "%s: %s: state data rd too long for [%s]",
304
log_prefix, __func__, username);
305
(void) otp_state_put(username, user_state, log_prefix);
308
(void) strcpy(user_state->rd, p);
309
p = q; /* failcount */
311
/* extract failcount */
312
if ((q = strchr(p, ':')) == NULL) {
313
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid failcount for [%s]",
314
log_prefix, __func__, username);
315
(void) otp_state_put(username, user_state, log_prefix);
319
if (sscanf(p, "%" SCNx32, &user_state->failcount) != 1) {
320
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid failcount for [%s]",
321
log_prefix, __func__, username);
322
(void) otp_state_put(username, user_state, log_prefix);
325
p = q; /* authtime */
327
/* extract authtime */
328
if ((q = strchr(p, ':')) == NULL) {
329
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid authtime for [%s]",
330
log_prefix, __func__, username);
331
(void) otp_state_put(username, user_state, log_prefix);
335
if (sscanf(p, "%" SCNx32, &user_state->authtime) != 1) {
336
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid authtime for [%s]",
337
log_prefix, __func__, username);
338
(void) otp_state_put(username, user_state, log_prefix);
341
p = q; /* mincardtime */
343
/* extract mincardtime */
344
if ((q = strchr(p, ':')) == NULL) {
345
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid mincardtime for [%s]",
346
log_prefix, __func__, username);
347
(void) otp_state_put(username, user_state, log_prefix);
351
if (sscanf(p, "%" SCNx32, &user_state->mincardtime) != 1) {
352
otp_log(OTP_LOG_ERR, "%s: %s: state data invalid mincardtime for [%s]",
353
log_prefix, __func__, username);
354
(void) otp_state_put(username, user_state, log_prefix);
362
* Format user_state into a state manager update request.
363
* Returns new (filled) buflen on success, -1 on failure.
366
otp_state_unparse(char *buf, size_t buflen, const char *username,
367
otp_user_state_t *user_state, const char *log_prefix)
370
char s[OTP_MAX_CHALLENGE_LEN * 2 + 1];
372
/* perhaps this isn't our job, but it's safe */
373
if (!user_state->locked)
376
if (user_state->updated)
377
(void) snprintf(buf, buflen, "P %s "
381
"%" PRIx32 ":%" PRIx32 ":"
383
/* 'P ', */ username,
384
/* '5:', */ username,
385
otp_keyblock2keystring(s, user_state->challenge,
388
user_state->csd, user_state->rd,
389
user_state->failcount, user_state->authtime,
390
user_state->mincardtime);
392
(void) snprintf(buf, buflen, "P %s", username);
393
buf[buflen - 1] = '\0';
395
if ((len = strlen(buf) + 1) == buflen) {
397
* Short by one, but the best we can do b/c of different snprintf()'s
398
* without a lot of work. Guaranteed anyway, due to small max
399
* username, challenge, csd len's, assuming maximally (1024) sized buf.
401
otp_log(OTP_LOG_ERR, "%s: %s: state data (unparse) too long for [%s]",
402
log_prefix, __func__, username);
411
* Full read with logging, and close on failure.
412
* Returns nread on success, -1 on failure.
413
* buf[nread - 1] is guaranteed to be '\0'.
416
xread(lsmd_fd_t *fdp, char *buf, size_t len, const char *log_prefix)
419
int nread = 0; /* bytes read into buf */
422
if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
423
if (errno == EAGAIN || errno == EINTR) {
426
otp_log(OTP_LOG_ERR, "%s: %s: read from state manager: %s",
427
log_prefix, __func__, strerror(errno));
428
otp_state_putfd(fdp, 1, log_prefix);
433
otp_log(OTP_LOG_ERR, "%s: %s: state manager disconnect",
434
log_prefix, __func__);
435
otp_state_putfd(fdp, 1, log_prefix);
441
* was last byte a NUL? (pipelining is not possible,
442
* so we only need to check the last byte to find
443
* the message boundary)
445
if (buf[nread - 1] == '\0')
452
* Full write with logging, and close on failure.
453
* Returns 0 on success, -1 on failure.
456
xwrite(lsmd_fd_t *fdp, const char *buf, size_t len, const char *log_prefix)
462
if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
463
if (errno != EINTR) {
464
otp_log(OTP_LOG_ERR, "%s: %s: write to state manager: %s",
465
log_prefix, __func__, strerror(errno));
466
otp_state_putfd(fdp, 1, log_prefix);
477
/* connect to state manager and return fd */
479
otp_state_connect(const char *path, const char *log_prefix)
482
struct sockaddr_un sa;
483
size_t sp_len; /* sun_path length (strlen) */
485
/* setup for unix domain socket */
486
sp_len = strlen(path);
487
if (sp_len > sizeof(sa.sun_path) - 1) {
488
otp_log(OTP_LOG_ERR, "%s: %s: rendezvous point name too long",
489
log_prefix, __func__);
492
sa.sun_family = AF_UNIX;
493
(void) strcpy(sa.sun_path, path);
495
/* connect to state manager */
496
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
497
otp_log(OTP_LOG_ERR, "%s: %s: socket: %s", log_prefix, __func__,
501
if (connect(fd, (struct sockaddr *) &sa,
502
sizeof(sa.sun_family) + sp_len) == -1) {
503
otp_log(OTP_LOG_ERR, "%s: %s: connect: %s", log_prefix, __func__,
513
/* retrieve fd (possibly opening a new one) to state manager */
515
otp_state_getfd(const otp_option_t *opt, const char *log_prefix)
517
lsmd_fd_t *fdp = &lsmd_fd;
519
/* return existing fd if open */
523
fdp->fd = otp_state_connect(opt->lsmd_rp, log_prefix);
528
/* disconnect from state manager */
530
otp_state_putfd(lsmd_fd_t *fdp, int close_p, const char *log_prefix)
532
/* for PAM we always close the fd; leaving it open is a leak */
533
(void) close(fdp->fd);
537
#elif defined(FREERADIUS)
539
* Retrieve fd (from pool) to state manager.
540
* It'd be simpler to use TLS but FR can have lots of threads
541
* and we don't want to waste fd's that way.
542
* We can't have a global fd because we'd then be pipelining
543
* requests to the state manager and we have no way to demultiplex
547
otp_state_getfd(const otp_option_t *opt, const char *log_prefix)
552
/* walk the connection pool looking for an available fd */
553
for (fdp = lsmd_fd_head; fdp; fdp = fdp->next) {
554
rc = pthread_mutex_trylock(&fdp->mutex);
558
otp_log(OTP_LOG_ERR, "%s: %s: pthread_mutex_trylock: %s",
559
log_prefix, __func__, strerror(errno));
565
/* no fd was available, add a new one */
566
if ((rc = pthread_mutex_lock(&lsmd_fd_head_mutex))) {
567
otp_log(OTP_LOG_ERR, "%s: %s: pthread_mutex_lock: %s",
568
log_prefix, __func__, strerror(errno));
571
fdp = rad_malloc(sizeof(*fdp));
572
if ((rc = pthread_mutex_init(&fdp->mutex, NULL))) {
573
otp_log(OTP_LOG_ERR, "%s: %s: pthread_mutex_init: %s",
574
log_prefix, __func__, strerror(errno));
578
if ((rc = pthread_mutex_lock(&fdp->mutex))) {
579
otp_log(OTP_LOG_ERR, "%s: %s: pthread_mutex_lock: %s",
580
log_prefix, __func__, strerror(errno));
584
fdp->next = lsmd_fd_head;
586
if ((rc = pthread_mutex_unlock(&lsmd_fd_head_mutex))) {
587
otp_log(OTP_LOG_ERR, "%s: %s: pthread_mutex_unlock: %s",
588
log_prefix, __func__, strerror(errno));
592
fdp->fd = otp_state_connect(opt->lsmd_rp, log_prefix);
598
/* disconnect from state manager */
600
otp_state_putfd(lsmd_fd_t *fdp, int close_p, const char *log_prefix)
604
/* close fd (used for errors) */
606
(void) close(fdp->fd);
609
/* make connection available to another thread */
610
if ((rc = pthread_mutex_unlock(&fdp->mutex))) {
611
otp_log(OTP_LOG_ERR, "%s: %s: pthread_mutex_unlock: %s",
612
log_prefix, __func__, strerror(errno));
617
#endif /* FREERADIUS */