import from HEAD:
authorfcusack <fcusack>
Thu, 1 Jun 2006 19:02:16 +0000 (19:02 +0000)
committerfcusack <fcusack>
Thu, 1 Jun 2006 19:02:16 +0000 (19:02 +0000)
update rlm_otp implementation to talk to otpd instead of
doing the otp work directly

1  2 
src/modules/rlm_otp/extern.h
src/modules/rlm_otp/otp_mppe.c
src/modules/rlm_otp/otp_pw_valid.c
src/modules/rlm_otp/otp_pwe.c
src/modules/rlm_otp/otp_rlm.c

index 0000000,fbc167b..3dc2c1a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,97 +1,97 @@@
 -#include <freeradius-devel/autoconf.h>
 -#include <freeradius-devel/radiusd.h>
 -#include <freeradius-devel/modules.h>
+ /*
+  * $Id$
+  *
+  *   This program is free software; you can redistribute it and/or modify
+  *   it under the terms of the GNU General Public License as published by
+  *   the Free Software Foundation; either version 2 of the License, or
+  *   (at your option) any later version.
+  *
+  *   This program is distributed in the hope that it will be useful,
+  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *   GNU General Public License for more details.
+  *
+  *   You should have received a copy of the GNU General Public License
+  *   along with this program; if not, write to the Free Software
+  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+  *
+  * Copyright 2001,2002  Google, Inc.
+  * Copyright 2005,2006 TRI-D Systems, Inc.
+  */
+ #ifndef EXTERN_H
+ #define EXTERN_H
++#include <autoconf.h>
++#include <radiusd.h>
++#include <modules.h>
+ #include <sys/types.h>
+ #include <pthread.h>
+ #include "otp.h"      /* OTP_MAX_CHALLENGE_LEN, otp_pwe_t */
+ /* otpd rendezvous point */
+ #define OTP_OTPD_RP "/var/run/otpd/socket"
+ /* Default prompt for presentation of challenge */
+ #define OTP_CHALLENGE_PROMPT "Challenge: %s\n Response: "
+ /*
+  * You shouldn't change anything past this point
+  */
+ /* struct used for instance/option data */
+ typedef struct otp_option_t {
+   const char *name;   /* instance name for otp_token_authorize()         */
+   char *otpd_rp;      /* otpd rendezvous point                           */
+   char *chal_prompt;  /* text to present challenge to user, must have %s */
+   int challenge_len;  /* challenge length, min 5 digits                  */
+   int challenge_delay;        /* max delay time for response, in seconds         */
+   int allow_sync;     /* useful to override pwdfile card_type settings   */
+   int allow_async;    /* C/R mode allowed?                               */
+   int mschapv2_mppe_policy;   /* whether or not do to mppe for mschapv2  */
+   int mschapv2_mppe_types;    /* key type/length for mschapv2/mppe       */
+   int mschap_mppe_policy;     /* whether or not do to mppe for mschap    */
+   int mschap_mppe_types;      /* key type/length for mschap/mppe         */
+ } otp_option_t;
+ /* otp_mppe.c */
+ void otp_mppe(REQUEST *, otp_pwe_t, const otp_option_t *, const char *);
+ /* otp_pw_valid.c */
+ extern int otp_pw_valid(REQUEST *, int, const char *, const otp_option_t *,
+                         char []);
+ /* otp_radstate.c */
+ #define OTP_MAX_RADSTATE_LEN 2 + (OTP_MAX_CHALLENGE_LEN * 2 + 8 + 8 + 32)*2 + 1
+ extern int otp_gen_state(char [OTP_MAX_RADSTATE_LEN],
+                          unsigned char [OTP_MAX_RADSTATE_LEN],
+                          const unsigned char [OTP_MAX_CHALLENGE_LEN], size_t,
+                          int32_t, int32_t, const unsigned char [16]);
+ /* otp_pwe.c */
+ extern int pwattr[8];
+ extern void otp_pwe_init(void);
+ extern otp_pwe_t otp_pwe_present(const REQUEST *);
+ /* otp_util.c */
+ extern void otp_get_random(char *, size_t);
+ extern void otp_async_challenge(char [OTP_MAX_CHALLENGE_LEN + 1], int);
+ extern int otp_a2x(const char *, unsigned char *);
+ extern void otp_x2a(const unsigned char *, size_t, char *);
+ extern void _otp_pthread_mutex_init(pthread_mutex_t *,
+                                     const pthread_mutexattr_t *, const char *);
+ extern void _otp_pthread_mutex_lock(pthread_mutex_t *, const char *);
+ extern int _otp_pthread_mutex_trylock(pthread_mutex_t *, const char *);
+ extern void _otp_pthread_mutex_unlock(pthread_mutex_t *, const char *);
+ #define otp_pthread_mutex_init(a, b) _otp_pthread_mutex_init((a), (b), __func__)
+ #define otp_pthread_mutex_lock(a) _otp_pthread_mutex_lock((a), __func__)
+ #define otp_pthread_mutex_trylock(a) _otp_pthread_mutex_trylock((a), __func__)
+ #define otp_pthread_mutex_unlock(a) _otp_pthread_mutex_unlock((a), __func__)
+ #endif /* EXTERN_H */
index 0000000,0f2adc0..62e9681
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,473 +1,473 @@@
 -#include <freeradius-devel/rad_assert.h>
+ /*
+  * $Id$
+  *
+  *   This program is free software; you can redistribute it and/or modify
+  *   it under the terms of the GNU General Public License as published by
+  *   the Free Software Foundation; either version 2 of the License, or
+  *   (at your option) any later version.
+  *
+  *   This program is distributed in the hope that it will be useful,
+  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *   GNU General Public License for more details.
+  *
+  *   You should have received a copy of the GNU General Public License
+  *   along with this program; if not, write to the Free Software
+  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+  *
+  * Copyright 2001,2002  Google, Inc.
+  * Copyright 2005,2006 TRI-D Systems, Inc.
+  */
+ static const char rcsid[] = "$Id$";
+ /* avoid inclusion of these FR headers which conflict w/ OpenSSL */
+ #define _LRAD_MD4_H
+ #define _LRAD_SHA1_H
 -      char *username = request->username->vp_strvalue;
++#include <rad_assert.h>
+ #include "extern.h"
+ #include "otp.h"
+ #include "otp_mppe.h"
+ #include <openssl/des.h>
+ #include <openssl/md4.h>
+ #include <openssl/md5.h>
+ #include <openssl/sha.h>
+ #include <string.h>
+ /*
+  * Add MPPE attributes to a request, if required.
+  */
+ void
+ otp_mppe(REQUEST *request, otp_pwe_t pwe, const otp_option_t *opt,
+          const char *passcode)
+ {
+   VALUE_PAIR **avp = &request->reply->vps;
+   VALUE_PAIR *cvp, *rvp, *vp;
+   cvp = pairfind(request->packet->vps, pwattr[pwe - 1]);
+   rvp = pairfind(request->packet->vps, pwattr[pwe]);
+   switch (pwe) {
+   case PWE_PAP:
+   case PWE_CHAP:
+     return;
+   case PWE_MSCHAP:
+     /* First, set some related attributes. */
+     vp = pairmake("MS-MPPE-Encryption-Policy",
+                   otp_mppe_policy[opt->mschap_mppe_policy], T_OP_EQ);
+     rad_assert(vp != NULL);
+     pairadd(avp, vp);
+     vp = pairmake("MS-MPPE-Encryption-Types",
+                   otp_mppe_types[opt->mschap_mppe_types], T_OP_EQ);
+     rad_assert(vp != NULL);
+     pairadd(avp, vp);
+     /* If no MPPE, we're done. */
+     if (!opt->mschap_mppe_policy)
+       return;
+     /*
+      * Generate the MS-CHAP-MPPE-Keys attribute.  This is not specified
+      * anywhere -- RFC 2548, par. 2.4.1 is the authority but it has
+      * typos and omissions that make this unimplementable.  The
+      * code here is based on experimental results provided by
+      * Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp>.
+      * We only support 128-bit keys derived from the NT hash; 40-bit
+      * and 56-bit keys are derived from the LM hash, which besides
+      * being deprecated, has severe security problems.
+      */
+     {
+       size_t i, passcode_len;
+       unsigned char password_unicode[2 * OTP_MAX_PASSCODE_LEN];
+       unsigned char password_md[MD4_DIGEST_LENGTH];
+       unsigned char mppe_keys[32];
+       /*                    0x    ASCII(mppe_keys)      '\0' */
+       char mppe_keys_string[2 + (2 * sizeof(mppe_keys)) + 1];
+       /* Zero the LM-Key sub-field (and padding). */
+       (void) memset(mppe_keys, 0, sizeof(mppe_keys));
+       /*
+        * The NT-Key sub-field is MD4(MD4(unicode(password))).
+        * Start by hashing the unicode passcode.
+        * This is broken because unicode chars are machine-ordered,
+        * but the spec (RFC 2433) doesn't say how to prepare
+        * the password for md4 (other than by example values).
+        */
+       passcode_len = strlen(passcode);
+       for (i = 0; i < passcode_len; ++i) {
+         /* Set the high order 8 bits to 0 (little-endian) */
+         password_unicode[i * 2] = *passcode++;
+         password_unicode[i * 2 + 1] = 0;
+       }
+       /* first md4 */
+       (void) MD4(password_unicode, 2 * passcode_len, password_md);
+       /* second md4 */
+       (void) MD4(password_md, MD4_DIGEST_LENGTH, &mppe_keys[8]);
+ #if 0 /* encoding now handled in lib/radius.c:rad_pwencode() */
+       {
+         unsigned char md5_md[MD5_DIGEST_LENGTH];
+         unsigned char encode_buf[AUTH_VECTOR_LEN + MAX_STRING_LEN];
+         int secretlen;
+         /* Now we must encode the key as User-Password is encoded. */
+         secretlen = strlen(request->secret);
+         (void) memcpy(encode_buf, request->secret, secretlen);
+         (void) memcpy(encode_buf + secretlen, request->packet->vector,
+                       AUTH_VECTOR_LEN);
+         (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN, md5_md);
+         for (i = 0; i < 16; ++i)
+           mppe_keys[i] ^= md5_md[i];
+         (void) memcpy(encode_buf + secretlen, mppe_keys, MD5_DIGEST_LENGTH);
+         (void) MD5(encode_buf, secretlen + MD5_DIGEST_LENGTH, md5_md);
+         for (i = 0; i < 16; ++i)
+           mppe_keys[i + 16] ^= md5_md[i];
+       }
+ #endif /* 0 */
+       /* Whew.  Now stringify it for pairmake(). */
+       mppe_keys_string[0] = '0';
+       mppe_keys_string[1] = 'x';
+       for (i = 0; i < 32; ++i)
+         (void) sprintf(&mppe_keys_string[i*2+2], "%02X", mppe_keys[i]);
+       vp = pairmake("MS-CHAP-MPPE-Keys", mppe_keys_string, T_OP_EQ);
+       rad_assert(vp != NULL);
+       pairadd(avp, vp);
+     } /* (doing mppe) */
+   break; /* PWE_MSCHAP */
+   case PWE_MSCHAP2:
+   {
+     size_t i;
+     unsigned char password_md_md[MD4_DIGEST_LENGTH];
+     /*
+      * MS-CHAPv2 requires mutual authentication; we must prove
+      * that we know the secret.  This is a bit circuitous: set
+      * MD1 = SHA(MD4(MD4(unicode(password)))|NT_RESPONSE|MAGIC1),
+      * MD2 = MSB8(SHA(PEER_CHALLENGE|MS_CHAP_CHALLENGE|USERNAME)),
+      * and finally use SHA(MD1|MD2|MAGIC2) as the authenticator.
+      * The authenticator is returned as the string "S=<auth>",
+      * <auth> is the authenticator expressed as [uppercase] ASCII.
+      * See RFC 2759.
+      */
+     {
+       size_t passcode_len;
+       unsigned char password_unicode[2 * OTP_MAX_PASSCODE_LEN];
+       unsigned char password_md[MD4_DIGEST_LENGTH];
+       SHA_CTX ctx;
+       unsigned char md1[SHA_DIGEST_LENGTH];
+       unsigned char md2[SHA_DIGEST_LENGTH];
+       unsigned char auth_md[SHA_DIGEST_LENGTH];
+       /*                  S=  (  ASCII(auth_md)   )  \0 */
+       char auth_md_string[2 + (2 * sizeof(auth_md)) + 1];
+       /*
+        * ugh.  The ASCII authenticator (auth_md_string) is sent
+        * along with a single (useless) binary byte (the ID).
+        * So we must "stringify" it again (for pairmake()) since the
+        * binary byte requires the attribute to be of type "octets".
+        */
+       /*                    0x  (ID) ( ASCII("S="ASCII(auth_md))) */
+       char auth_octet_string[2 + 2 + (2 * sizeof(auth_md_string))];
 -      SHA1_Update(&ctx, rvp->vp_strvalue + 26, 24);
++      char *username = request->username->strvalue;
+       int username_len = request->username->length;
+       /* "Magic server to client signing constant" */
+       unsigned char magic1[39] =
+         { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+           0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+           0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+           0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
+       /* "Pad to make it do more than one iteration" */
+       unsigned char magic2[41] =
+         { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+           0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+           0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+           0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+           0x6E };
+       /*
+        * Start by hashing the unicode passcode.
+        * This is broken because unicode chars are machine-ordered,
+        * but the spec (RFC 2759) doesn't say how to prepare
+        * the password for md4 (other than by example values).
+        */
+       passcode_len = strlen(passcode);
+       for (i = 0; i < passcode_len; ++i) {
+         /* Set the high order 8 bits to 0 (little-endian) */
+         password_unicode[i * 2] = *passcode++;
+         password_unicode[i * 2 + 1] = 0;
+       }
+       /* first md4 */
+       (void) MD4(password_unicode, 2 * passcode_len, password_md);
+       /* second md4 */
+       (void) MD4(password_md, MD4_DIGEST_LENGTH, password_md_md);
+       /* MD1 */
+       SHA1_Init(&ctx);
+       SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH);
 -      SHA1_Update(&ctx, rvp->vp_strvalue + 2, 16);
 -      SHA1_Update(&ctx, cvp->vp_strvalue, 16);
++      SHA1_Update(&ctx, rvp->strvalue + 26, 24);
+       SHA1_Update(&ctx, magic1, sizeof(magic1));
+       SHA1_Final(md1, &ctx);
+       /* MD2 */
+       SHA1_Init(&ctx);
 -      (void) sprintf(&auth_octet_string[2], "%02X", rvp->vp_strvalue[0]);
++      SHA1_Update(&ctx, rvp->strvalue + 2, 16);
++      SHA1_Update(&ctx, cvp->strvalue, 16);
+       SHA1_Update(&ctx, username, username_len);
+       SHA1_Final(md2, &ctx);
+       /* The Authenticator */
+       SHA1_Init(&ctx);
+       SHA1_Update(&ctx, md1, SHA_DIGEST_LENGTH);
+       SHA1_Update(&ctx, md2, 8);
+       SHA1_Update(&ctx, magic2, sizeof(magic2));
+       SHA1_Final(auth_md, &ctx);
+       /* String conversion. */
+       auth_md_string[0] = 'S';
+       auth_md_string[1] = '=';
+       for (i = 0; i < sizeof(auth_md); ++i)
+         (void) sprintf(&auth_md_string[i * 2 + 2], "%02X", auth_md[i]);
+       /* And then octet conversion.  Ugh! */
+       auth_octet_string[0] = '0';
+       auth_octet_string[1] = 'x';
 -      SHA1_Update(&ctx, rvp->vp_strvalue + 26, 24);
++      (void) sprintf(&auth_octet_string[2], "%02X", rvp->strvalue[0]);
+       for (i = 0; i < sizeof(auth_md_string) - 1; ++i)
+         (void) sprintf(&auth_octet_string[i * 2 +4], "%02X", auth_md_string[i]);
+       vp = pairmake("MS-CHAP2-Success", auth_octet_string, T_OP_EQ);
+       rad_assert(vp != NULL);
+       pairadd(avp, vp);
+     } /* Generate mutual auth info. */
+     /*
+      * Now, set some MPPE related attributes.
+      */
+     vp = pairmake("MS-MPPE-Encryption-Policy",
+                   otp_mppe_policy[opt->mschapv2_mppe_policy], T_OP_EQ);
+     rad_assert(vp != NULL);
+     pairadd(avp, vp);
+     vp = pairmake("MS-MPPE-Encryption-Types",
+                   otp_mppe_types[opt->mschapv2_mppe_types], T_OP_EQ);
+     rad_assert(vp != NULL);
+     pairadd(avp, vp);
+     /* If no MPPE, we're done. */
+     if (!opt->mschapv2_mppe_policy)
+       return;
+     /*
+      * Generate the MPPE initial session key, per RFC 3079.
+      * (Although, RFC 2548 leaves us guessing at how to generate this.)
+      * For MS-CHAPv2 we support all key lengths (40-, 56- and 128-bit),
+      * although MPPE via RADIUS supports only 40- and 128-bit keys.
+      * This is a bit more complicated than MS-CHAP.  Start by generating
+      * a "master session key"
+      *    MSB16(SHA(NTPasswordHashHash|NT_RESPONSE|MAGIC1)), where
+      * NTPasswordHashHash is MD4(MD4(unicode(password))), NT_RESPONSE
+      * is from the MS-CHAP2-Response attribute, and MAGIC1 is a
+      * constant from RFC 3079.  Then, we derive asymmetric send/receive
+      * keys from the master session key.  The "master send key" is
+      *     MSBx(SHA(MASTERKEY|SHSPAD1|MAGIC3|SHSPAD2)),
+      * and the "master receive key" is
+      *     MSBx(SHA(MASTERKEY|SHSPAD1|MAGIC2|SHSPAD2)), where
+      * MASTERKEY is the "master session key" generated above, and the
+      * other values are constants from RFC 3079.  MSBx is the x-most
+      * significant bytes, where x is 5, 7, or 16 as appropriate for
+      * the desired key length.  We always generate 16 byte (128-bit)
+      * keys, the NAS is required to truncate as needed.
+      */
+     {
+       /* These constants and key vars are named from RFC 3079. */
+       /* "This is the MPPE Master Key" */
+       unsigned char Magic1[27] =
+         { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+           0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+           0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+       /* "On the client side, this is the send key; "
+          "on the server side, it is the receive key." */
+       unsigned char Magic2[84] =
+         { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+           0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+           0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+           0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+           0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+           0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+           0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+           0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+           0x6b, 0x65, 0x79, 0x2e };
+       /* "On the client side, this is the receive key; "
+          "on the server side, it is the send key." */
+       unsigned char Magic3[84] =
+         { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+           0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+           0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+           0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+           0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+           0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+           0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+           0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+           0x6b, 0x65, 0x79, 0x2e };
+       unsigned char SHSpad1[40] =
+         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       unsigned char SHSpad2[40] =
+         { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+           0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+           0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+           0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
+       unsigned char MasterKey[16];
+       unsigned char MasterSendKey[16];
+       unsigned char MasterReceiveKey[16];
+       SHA_CTX ctx;
+       unsigned char sha_md[SHA_DIGEST_LENGTH];
+ #if 0 /* salting/encoding now handled in lib/radius.c:tunnel_pwencode() */
+       unsigned char md5_md[MD5_DIGEST_LENGTH];
+       /*   From RFC 2548:           S                 R           A */
+       unsigned char encode_buf[MAX_STRING_LEN + AUTH_VECTOR_LEN + 2];
+       int secretlen;
+       /* A useless value required by RFC 2548. */
+       unsigned char salt[2];
+       unsigned char mppe_key[32]; /* 1 + 16 + padding */
+       /*                           0x   (   ASCII(salt)  ) */
+       unsigned char mppe_key_string[2 + (2 * sizeof(salt)) +
+       /*                            (   ASCII(mppe_key)  )  \0 */
+                                     (2 * sizeof(mppe_key)) + 1];
+ #else /* 0 */
+       /*                           0x   (   ASCII(mppe_key)   )  \0 */
+       unsigned char mppe_key_string[2 + (2 * sizeof(MasterKey)) + 1];
+ #endif /* else !0 */
+       /* Generate the master session key. */
+       SHA1_Init(&ctx);
+       SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH);
++      SHA1_Update(&ctx, rvp->strvalue + 26, 24);
+       SHA1_Update(&ctx, Magic1, sizeof(Magic1));
+       SHA1_Final(sha_md, &ctx);
+       (void) memcpy(MasterKey, sha_md, 16);
+       /* Generate the master send key. */
+       SHA1_Init(&ctx);
+       SHA1_Update(&ctx, MasterKey, 16);
+       SHA1_Update(&ctx, SHSpad1, 40);
+       SHA1_Update(&ctx, Magic3, sizeof(Magic3));
+       SHA1_Update(&ctx, SHSpad2, 40);
+       SHA1_Final(sha_md, &ctx);
+       (void) memcpy(MasterSendKey, sha_md, 16);
+       /* Generate the master receive key. */
+       SHA1_Init(&ctx);
+       SHA1_Update(&ctx, MasterKey, 16);
+       SHA1_Update(&ctx, SHSpad1, 40);
+       SHA1_Update(&ctx, Magic2, sizeof(Magic3));
+       SHA1_Update(&ctx, SHSpad2, 40);
+       SHA1_Final(sha_md, &ctx);
+       (void) memcpy(MasterReceiveKey, sha_md, 16);
+       /*
+        * Now, generate the MS-MPPE-Send-Key attribute.
+        */
+ #if 0
+       /* Setup the salt value. */
+       salt[0] = 0x80;
+       salt[1] = 0x01;
+       /* Encode the key. */
+       (void) memset(mppe_key, 0, sizeof(mppe_key));
+       mppe_key[0] = 16; /* length */
+       (void) memcpy(&mppe_key[1], MasterSendKey, 16);
+       secretlen = strlen(request->secret);
+       (void) memcpy(encode_buf, request->secret, secretlen);
+       (void) memcpy(encode_buf + secretlen, request->packet->vector,
+                     AUTH_VECTOR_LEN);
+       (void) memcpy(encode_buf + secretlen + 16, salt, 2);
+       (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN + 2, md5_md);
+       for (i = 0; i < 16; ++i)
+         mppe_key[i] ^= md5_md[i];
+       (void) memcpy(encode_buf + secretlen, mppe_key, 16);
+       (void) MD5(encode_buf, secretlen + 16, md5_md);
+       for (i = 0; i < 16; ++i)
+         mppe_key[i + 16] ^= md5_md[i];
+       /* Whew.  Now stringify it for pairmake(). */
+       mppe_key_string[0] = '0';
+       mppe_key_string[1] = 'x';
+       (void) sprintf(&mppe_key_string[2], "%02X", salt[0]);
+       (void) sprintf(&mppe_key_string[4], "%02X", salt[1]);
+       for (i = 0; i < sizeof(mppe_key); ++i)
+         (void) sprintf(&mppe_key_string[i*2+6], "%02X", mppe_key[i]);
+ #else /* 0 */
+       mppe_key_string[0] = '0';
+       mppe_key_string[1] = 'x';
+       for (i = 0; i < sizeof(MasterSendKey); ++i)
+         (void) sprintf(&mppe_key_string[i*2+2], "%02X", MasterSendKey[i]);
+ #endif /* else !0 */
+       vp = pairmake("MS-MPPE-Send-Key", mppe_key_string, T_OP_EQ);
+       rad_assert(vp != NULL);
+       pairadd(avp, vp);
+       /*
+        * Generate the MS-MPPE-Recv-Key attribute.
+        */
+ #if 0
+       /* Setup the salt value. */
+       salt[0] = 0x80;
+       salt[1] = 0x02;
+       /* Encode the key. */
+       (void) memset(mppe_key, 0, sizeof(mppe_key));
+       mppe_key[0] = 16; /* length */
+       (void) memcpy(&mppe_key[1], MasterReceiveKey, 16);
+       secretlen = strlen(request->secret);
+       (void) memcpy(encode_buf, request->secret, secretlen);
+       (void) memcpy(encode_buf + secretlen, request->packet->vector,
+                     AUTH_VECTOR_LEN);
+       (void) memcpy(encode_buf + secretlen + 16, salt, 2);
+       (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN + 2, md5_md);
+       for (i = 0; i < 16; ++i)
+         mppe_key[i] ^= md5_md[i];
+       (void) memcpy(encode_buf + secretlen, mppe_key, 16);
+       (void) MD5(encode_buf, secretlen + 16, md5_md);
+       for (i = 0; i < 16; ++i)
+         mppe_key[i + 16] ^= md5_md[i];
+       /* Whew.  Now stringify it for pairmake(). */
+       mppe_key_string[0] = '0';
+       mppe_key_string[1] = 'x';
+       (void) sprintf(&mppe_key_string[2], "%02X", salt[0]);
+       (void) sprintf(&mppe_key_string[4], "%02X", salt[1]);
+       for (i = 0; i < sizeof(mppe_key); ++i)
+         (void) sprintf(&mppe_key_string[i*2+6], "%02X", mppe_key[i]);
+ #else /* 0 */
+       mppe_key_string[0] = '0';
+       mppe_key_string[1] = 'x';
+       for (i = 0; i < sizeof(MasterReceiveKey); ++i)
+         (void) sprintf(&mppe_key_string[i*2+2], "%02X", MasterReceiveKey[i]);
+ #endif /* else !0 */
+       vp = pairmake("MS-MPPE-Recv-Key", mppe_key_string, T_OP_EQ);
+       rad_assert(vp != NULL);
+       pairadd(avp, vp);
+     } /* (doing mppe) */
+   } /* PWE_MSCHAP2 */
+   break; /* PWE_MSCHAP2 */
+   } /* switch (pwe) */
+   return;
+ }
index 0000000,e4ed5ef..18ceb1d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,377 +1,377 @@@
 -#include <freeradius-devel/autoconf.h>
 -#include <freeradius-devel/radiusd.h>
 -#include <freeradius-devel/modules.h>
+ /*
+  * $Id$
+  *
+  * Passcode verification function (otpd client) for rlm_otp.
+  *
+  *   This program is free software; you can redistribute it and/or modify
+  *   it under the terms of the GNU General Public License as published by
+  *   the Free Software Foundation; either version 2 of the License, or
+  *   (at your option) any later version.
+  *
+  *   This program is distributed in the hope that it will be useful,
+  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *   GNU General Public License for more details.
+  *
+  *   You should have received a copy of the GNU General Public License
+  *   along with this program; if not, write to the Free Software
+  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+  *
+  *
+  * Copyright 2006 TRI-D Systems, Inc.
+  */
+ static const char rcsid[] = "$Id$";
 -  char                *username = request->username->vp_strvalue;
++#include <autoconf.h>
++#include <radiusd.h>
++#include <modules.h>
+ #include "extern.h"
+ #include "otp.h"
+ #include "otp_pw_valid.h"
+ #include <errno.h>
+ #include <pthread.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ #include <unistd.h>
+ /* transform otpd return codes into rlm return codes */
+ static int
+ otprc2rlmrc(int rc)
+ {
+   switch (rc) {
+     case OTP_RC_OK:                     return RLM_MODULE_OK;
+     case OTP_RC_USER_UNKNOWN:           return RLM_MODULE_REJECT;
+     case OTP_RC_AUTHINFO_UNAVAIL:       return RLM_MODULE_REJECT;
+     case OTP_RC_AUTH_ERR:               return RLM_MODULE_REJECT;
+     case OTP_RC_MAXTRIES:               return RLM_MODULE_USERLOCK;
+     case OTP_RC_SERVICE_ERR:            return RLM_MODULE_FAIL;
+     default:                            return RLM_MODULE_FAIL;
+   }
+ }
+ static otp_fd_t *otp_fd_head;
+ static pthread_mutex_t otp_fd_head_mutex = PTHREAD_MUTEX_INITIALIZER;
+ /*
+  * Test for passcode validity by asking otpd.
+  *
+  * If challenge is supplied, it is used to generate the card response
+  * against which the passcode will be compared.  If challenge is not
+  * supplied, or if the comparison fails, synchronous responses are
+  * generated and tested.  NOTE: for async authentications, sync mode
+  * responses are still considered valid!  (Assuming module configuration
+  * allows sync mode.)
+  *
+  * Returns one of the RLM_MODULE_* codes.  passcode is filled in.
+  * NB: The returned passcode will contain the PIN!  DO NOT LOG!
+  */
+ int
+ otp_pw_valid(REQUEST *request, int pwe, const char *challenge,
+              const otp_option_t *opt, char passcode[OTP_MAX_PASSCODE_LEN + 1])
+ {
+   otp_request_t       otp_request;
+   otp_reply_t otp_reply;
+   VALUE_PAIR  *cvp, *rvp;
 -    (void) strcpy(otp_request.pwe.passcode, rvp->vp_strvalue);
++  char                *username = request->username->strvalue;
+   int         rc;
+   if (request->username->length > OTP_MAX_USERNAME_LEN) {
+     (void) radlog(L_AUTH, "rlm_otp: username [%s] too long\n", username);
+     return RLM_MODULE_REJECT;
+   }
+   /* we already know challenge is short enough */
+   otp_request.version = 1;
+   (void) strcpy(otp_request.username, username);
+   (void) strcpy(otp_request.challenge, challenge);
+   otp_request.pwe.pwe = pwe;
+   /* otp_pwe_present() (done by caller) guarantees that both of these exist */
+   rvp = pairfind(request->packet->vps, pwattr[pwe - 1]);
+   cvp = pairfind(request->packet->vps, pwattr[pwe]);
+   /*
+    * Validate available vps based on pwe type.
+    * Unfortunately (?) otpd must do this also.
+    */
+   switch (otp_request.pwe.pwe) {
+   case PWE_PAP:
+     if (rvp->length > OTP_MAX_PASSCODE_LEN) {
+       (void) radlog(L_AUTH, "rlm_otp: passcode for [%s] too long\n",
+                     username);
+       return RLM_MODULE_REJECT;
+     }
 -    (void) memcpy(otp_request.pwe.challenge, cvp->vp_strvalue, cvp->length);
++    (void) strcpy(otp_request.pwe.passcode, rvp->strvalue);
+     break;
+   case PWE_CHAP:
+     if (cvp->length > 16) {
+       (void) radlog(L_AUTH, "rlm_otp: CHAP challenge for [%s] too long\n",
+                     username);
+       return RLM_MODULE_INVALID;
+     }
+     if (rvp->length != 17) {
+       (void) radlog(L_AUTH, "rlm_otp: CHAP response for [%s] wrong size\n",
+                     username);
+       return RLM_MODULE_INVALID;
+     }
 -    (void) memcpy(otp_request.pwe.response, rvp->vp_strvalue, rvp->length);
++    (void) memcpy(otp_request.pwe.challenge, cvp->strvalue, cvp->length);
+     otp_request.pwe.clen = cvp->length;
 -    (void) memcpy(otp_request.pwe.challenge, cvp->vp_strvalue, cvp->length);
++    (void) memcpy(otp_request.pwe.response, rvp->strvalue, rvp->length);
+     otp_request.pwe.rlen = rvp->length;
+     break;
+   case PWE_MSCHAP:
+     if (cvp->length != 8) {
+       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP challenge for [%s] wrong size\n",
+                     username);
+       return RLM_MODULE_INVALID;
+     }
+     if (rvp->length != 50) {
+       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP response for [%s] wrong size\n",
+                     username);
+       return RLM_MODULE_INVALID;
+     }
 -    (void) memcpy(otp_request.pwe.response, rvp->vp_strvalue, rvp->length);
++    (void) memcpy(otp_request.pwe.challenge, cvp->strvalue, cvp->length);
+     otp_request.pwe.clen = cvp->length;
 -    (void) memcpy(otp_request.pwe.challenge, cvp->vp_strvalue, cvp->length);
++    (void) memcpy(otp_request.pwe.response, rvp->strvalue, rvp->length);
+     otp_request.pwe.rlen = rvp->length;
+     break;
+   case PWE_MSCHAP2:
+     if (cvp->length != 16) {
+       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP2 challenge for [%s] wrong size\n",
+                     username);
+       return RLM_MODULE_INVALID;
+     }
+     if (rvp->length != 50) {
+       (void) radlog(L_AUTH, "rlm_otp: MS-CHAP2 response for [%s] wrong size\n",
+                     username);
+       return RLM_MODULE_INVALID;
+     }
 -    (void) memcpy(otp_request.pwe.response, rvp->vp_strvalue, rvp->length);
++    (void) memcpy(otp_request.pwe.challenge, cvp->strvalue, cvp->length);
+     otp_request.pwe.clen = cvp->length;
++    (void) memcpy(otp_request.pwe.response, rvp->strvalue, rvp->length);
+     otp_request.pwe.rlen = rvp->length;
+     break;
+   } /* switch (otp_request.pwe.pwe) */
+   /* last byte must also be a terminator so otpd can verify length easily */
+   otp_request.username[OTP_MAX_USERNAME_LEN] = '\0';
+   otp_request.challenge[OTP_MAX_CHALLENGE_LEN] = '\0';
+   otp_request.pwe.passcode[OTP_MAX_PASSCODE_LEN] = '\0';
+   otp_request.allow_sync = opt->allow_sync;
+   otp_request.allow_async = opt->allow_async;
+   otp_request.challenge_delay = opt->challenge_delay;
+   otp_request.resync = 1;
+   rc = otp_verify(opt, &otp_request, &otp_reply);
+   if (rc == OTP_RC_OK)
+     (void) strcpy(passcode, otp_reply.passcode);
+   return otprc2rlmrc(rc);
+ }
+ /*
+  * Verify an otp by asking otpd.
+  * Returns an OTP_* code, or -1 on system failure.
+  * Fills in reply.
+  */
+ static int
+ otp_verify(const otp_option_t *opt,
+            const otp_request_t *request, otp_reply_t *reply)
+ {
+   otp_fd_t *fdp;
+   int rc;
+   int tryagain = 2;
+ retry:
+   if (!tryagain--)
+     return -1;
+   fdp = otp_getfd(opt);
+   if (!fdp || fdp->fd == -1)
+     return -1;
+   if ((rc = otp_write(fdp, (const char *) request, sizeof(*request))) != 0) {
+     if (rc == EPIPE)
+       goto retry;     /* otpd disconnect */   /*TODO: pause */
+     else
+       return -1;
+   }
+   if ((rc = otp_read(fdp, (char *) reply, sizeof(*reply))) != sizeof(*reply)) {
+     if (rc == 0)
+       goto retry;     /* otpd disconnect */   /*TODO: pause */
+     else
+       return -1;
+   }
+   /* validate the reply */
+   if (reply->version != 1) {
+     (void) radlog(L_AUTH, "rlm_otp: otpd reply for [%s] invalid "
+                           "(version %d != 1)",
+                   request->username, reply->version);
+     return -1;
+   }
+   if (reply->passcode[OTP_MAX_PASSCODE_LEN] != '\0') {
+     (void) radlog(L_AUTH, "rlm_otp: otpd reply for [%s] invalid (passcode)",
+                   request->username);
+     return -1;
+   }
+   return reply->rc;
+ }
+ /*
+  * Full read with logging, and close on failure.
+  * Returns nread on success, 0 on EOF, -1 on other failures.
+  */
+ static int
+ otp_read(otp_fd_t *fdp, char *buf, size_t len)
+ {
+   ssize_t n;
+   size_t nread = 0;   /* bytes read into buf */
+   while (nread < len) {
+     if ((n = read(fdp->fd, &buf[nread], len - nread)) == -1) {
+       if (errno == EINTR) {
+         continue;
+       } else {
+         (void) radlog(L_ERR, "rlm_otp: %s: read from otpd: %s",
+                       __func__, strerror(errno));
+         otp_putfd(fdp);
+         return -1;
+       }
+     }
+     if (!n) {
+       (void) radlog(L_ERR, "rlm_otp: %s: otpd disconnect", __func__);
+       otp_putfd(fdp);
+       return 0;
+     }
+     nread += n;
+   } /* while (more to read) */
+   return nread;
+ }
+ /*
+  * Full write with logging, and close on failure.
+  * Returns 0 on success, errno on failure.
+  */
+ static int
+ otp_write(otp_fd_t *fdp, const char *buf, size_t len)
+ {
+   size_t nleft = len;
+   ssize_t nwrote;
+   while (nleft) {
+     if ((nwrote = write(fdp->fd, &buf[len - nleft], nleft)) == -1) {
+       if (errno == EINTR || errno == EPIPE) {
+         continue;
+       } else {
+         (void) radlog(L_ERR, "rlm_otp: %s: write to otpd: %s",
+                       __func__, strerror(errno));
+         otp_putfd(fdp);
+         return errno;
+       }
+     }
+     nleft -= nwrote;
+   }
+   return 0;
+ }
+ /* connect to otpd and return fd */
+ static int
+ otp_connect(const char *path)
+ {
+   int fd;
+   struct sockaddr_un sa;
+   size_t sp_len;              /* sun_path length (strlen) */
+   /* setup for unix domain socket */
+   sp_len = strlen(path);
+   if (sp_len > sizeof(sa.sun_path) - 1) {
+     (void) radlog(L_ERR, "rlm_otp: %s: rendezvous point name too long",
+                   __func__);
+     return -1;
+   }
+   sa.sun_family = AF_UNIX;
+   (void) strcpy(sa.sun_path, path);
+     
+   /* connect to otpd */
+   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
+     (void) radlog(L_ERR, "rlm_otp: %s: socket: %s", __func__, strerror(errno));
+     return -1;
+   }
+   if (connect(fd, (struct sockaddr *) &sa,
+               sizeof(sa.sun_family) + sp_len) == -1) {
+     (void) radlog(L_ERR, "rlm_otp: %s: connect(%s): %s",
+                   __func__, path, strerror(errno));
+     (void) close(fd);
+     return -1;
+   }
+   return fd;
+ }
+ /*
+  * Retrieve an fd (from pool) to use for otpd connection.
+  * It'd be simpler to use TLS but FR can have lots of threads
+  * and we don't want to waste fd's that way.
+  * We can't have a global fd because we'd then be pipelining
+  * requests to otpd and we have no way to demultiplex
+  * the responses.
+  */
+ static otp_fd_t *
+ otp_getfd(const otp_option_t *opt)
+ {
+   int rc;
+   otp_fd_t *fdp;
+   /* walk the connection pool looking for an available fd */
+   for (fdp = otp_fd_head; fdp; fdp = fdp->next) {
+     rc = otp_pthread_mutex_trylock(&fdp->mutex);
+     if (!rc)
+       if (!strcmp(fdp->path, opt->otpd_rp))   /* could just use == */
+         break;
+   }
+   if (!fdp) {
+     /* no fd was available, add a new one */
+     fdp = rad_malloc(sizeof(*fdp));
+     otp_pthread_mutex_init(&fdp->mutex, NULL);
+     otp_pthread_mutex_lock(&fdp->mutex);
+     /* insert new fd at head */
+     otp_pthread_mutex_lock(&otp_fd_head_mutex);
+     fdp->next = otp_fd_head;
+     otp_fd_head = fdp;
+     otp_pthread_mutex_unlock(&otp_fd_head_mutex);
+     /* initialize */
+     fdp->path = opt->otpd_rp;
+     fdp->fd = -1;
+   }
+   /* establish connection */
+   if (fdp->fd == -1)
+     fdp->fd = otp_connect(fdp->path);
+   return fdp;
+ }
+ /* disconnect from otpd */
+ static void
+ otp_putfd(otp_fd_t *fdp)
+ {
+   (void) close(fdp->fd);
+   fdp->fd = -1;
+   /* make connection available to another thread */
+   otp_pthread_mutex_unlock(&fdp->mutex);
+ }
   * is not sufficient for X9.9 use.
   */
  
- #ifdef FREERADIUS
+ static const char rcsid[] = "$Id$";
+ /* avoid inclusion of these FR headers which conflict w/ OpenSSL */
  #define _LRAD_MD4_H
  #define _LRAD_SHA1_H
 -#include <freeradius-devel/rad_assert.h>
 -#include <freeradius-devel/autoconf.h>
 -#include <freeradius-devel/radiusd.h>
 +#include <rad_assert.h>
- #endif
- #include "otp.h"
- #include "otp_pwe.h"
++#include <autoconf.h>
++#include <radiusd.h>
+ #include "extern.h"
  
  #include <openssl/des.h>
  #include <openssl/md4.h>
   * Copyright 2005,2006 TRI-D Systems, Inc.
   */
  
- /*
-  * STRONG WARNING SECTION:
-  *
-  * ANSI X9.9 has been withdrawn as a standard, due to the weakness of DES.
-  * An attacker can learn the token's secret by observing two
-  * challenge/response pairs.  See ANSI document X9 TG-24-1999
-  * <URL:http://www.x9.org/docs/TG24_1999.pdf>.
-  *
-  * Please read the accompanying docs.
-  */
+ static const char rcsid[] = "$Id$";
  
 -#include <freeradius-devel/autoconf.h>
 -#include <freeradius-devel/radiusd.h>
 -#include <freeradius-devel/modules.h>
++#include <autoconf.h>
++#include <radiusd.h>
++#include <modules.h>
+ #include "extern.h"
+ #include "otp.h"
  
+ #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/types.h>
@@@ -452,17 -300,17 +300,17 @@@ otp_authenticate(void *instance, REQUES
  
    /* User-Name attribute required. */
    if (!request->username) {
-     otp_log(OTP_LOG_AUTH,
-             "%s: %s: Attribute \"User-Name\" required for authentication.",
-             log_prefix, __func__);
+     (void) radlog(L_AUTH, "rlm_otp: %s: Attribute \"User-Name\" required "
+                           "for authentication.",
+                   __func__);
      return RLM_MODULE_INVALID;
    }
 -  username = request->username->vp_strvalue;
 +  username = request->username->strvalue;
  
-   if ((data.pwattr = otp_pwe_present(request, log_prefix)) == 0) {
-     otp_log(OTP_LOG_AUTH, "%s: %s: Attribute \"User-Password\" "
+   if ((pwe = otp_pwe_present(request)) == 0) {
+     (void) radlog(L_AUTH, "rlm_otp: %s: Attribute \"User-Password\" "
                            "or equivalent required for authentication.",
-             log_prefix, __func__);
+                   __func__);
      return RLM_MODULE_INVALID;
    }
  
      int32_t           then;           /* state timestamp       */
      int                       e_length;       /* expected State length */
  
-     if ((vp = pairfind(request->packet->vps, PW_STATE)) != NULL) {
-       /* set expected State length */
-       if (inst->allow_async)
-         e_length = inst->chal_len * 2 + 8 + 8 + 32; /* see otp_gen_state() */
-       else
-         e_length = 1;
-       if (vp->length != e_length) {
-         otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: length",
-                 log_prefix, __func__, username);
-         return RLM_MODULE_INVALID;
-       }
-       if (inst->allow_async) {
-         /*
-          * Verify the state.
-          */
-         rad_state = rad_malloc(e_length + 1);
-         raw_state = rad_malloc(e_length / 2);
-         /* ASCII decode */
-         (void) memcpy(rad_state, vp->strvalue, vp->length);
-         rad_state[e_length] = '\0';
-         (void) otp_keystring2keyblock(rad_state, raw_state);
-         free(rad_state);
-         
-         /* extract data from State */
-         (void) memcpy(challenge, raw_state, inst->chal_len);
-         (void) memcpy(&sflags, raw_state + inst->chal_len, 4);
-         (void) memcpy(&then, raw_state + inst->chal_len + 4, 4);
-         free(raw_state);
-         /* generate new state from returned input data */
-         if (otp_gen_state(NULL, &state, challenge, inst->chal_len,
-                           sflags, then, hmac_key) != 0) {
-           otp_log(OTP_LOG_ERR, "%s: %s: failed to generate state",
-                   log_prefix, __func__);
-           return RLM_MODULE_FAIL;
-         }
-         /* compare generated state against returned state to verify hmac */
-         if (memcmp(state, vp->strvalue, vp->length)) {
-           otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: hmac",
-                   log_prefix, __func__, username);
-           free(state);
-           return RLM_MODULE_REJECT;
-         }
-         free(state);
-         /* State is valid, but check expiry. */
-         then = ntohl(then);
-         if (time(NULL) - then > inst->chal_delay) {
-           otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: expired",
-                   log_prefix, __func__, username);
-           return RLM_MODULE_REJECT;
-         }
-         resync = ntohl(sflags) & 1;
-       } /* if (State should have been protected) */
-     } /* if (State present) */
-   } /* code block */
+     /* set expected State length */
+     e_length = inst->challenge_len * 2 + 8 + 8 + 32; /* see otp_gen_state() */
+     if (vp->length != e_length) {
+       (void) radlog(L_AUTH, "rlm_otp: %s: bad state for [%s]: length",
+                     __func__, username);
+       return RLM_MODULE_INVALID;
+     }
+     /*
+      * Verify the state.
+      */
+     /* ASCII decode; this is why OTP_MAX_RADSTATE_LEN has +1 */
 -    (void) memcpy(rad_state, vp->vp_strvalue, vp->length);
++    (void) memcpy(rad_state, vp->strvalue, vp->length);
+     rad_state[e_length] = '\0';
+     if (otp_a2x(rad_state, raw_state) == -1) {
+       (void) radlog(L_AUTH, "rlm_otp: %s: bad state for [%s]: not hex",
+                     __func__, username);
+       return RLM_MODULE_INVALID;
+     }
+     /* extract data from State */
+     (void) memcpy(challenge, raw_state, inst->challenge_len);
+     /* skip flag data */
+     (void) memcpy(&then, raw_state + inst->challenge_len + 4, 4);
+     /* generate new state from returned input data */
+     if (otp_gen_state(NULL, state, challenge, inst->challenge_len, 0,
+                       then, hmac_key) != 0) {
+       (void) radlog(L_ERR, "rlm_otp: %s: failed to generate state",
+                     __func__);
+       return RLM_MODULE_FAIL;
+     }
+     /* compare generated state against returned state to verify hmac */
 -    if (memcmp(state, vp->vp_strvalue, vp->length)) {
++    if (memcmp(state, vp->strvalue, vp->length)) {
+       (void) radlog(L_AUTH, "rlm_otp: %s: bad state for [%s]: hmac",
+                     __func__, username);
+       return RLM_MODULE_REJECT;
+     }
+     /* State is valid, but check expiry. */
+     then = ntohl(then);
+     if (time(NULL) - then > inst->challenge_delay) {
+       (void) radlog(L_AUTH, "rlm_otp: %s: bad state for [%s]: expired",
+                     __func__, username);
+       return RLM_MODULE_REJECT;
+     }
+   } /* if (State present) */
  
    /* do it */
-   rc = otprc2rlmrc(otp_pw_valid(username, challenge, NULL, resync, inst,
-                                 otp_pwe_cmp, &data, log_prefix));
-   /* Handle any vps returned from otp_pwe_cmp(). */
-   if (rc == RLM_MODULE_OK) {
-     pairadd(&request->reply->vps, add_vps);
-   } else {
-     pairfree(&add_vps);
-   }
+   rc = otp_pw_valid(request, pwe, challenge, inst, passcode);
+   /* Add MPPE data as needed. */
+   if (rc == RLM_MODULE_OK)
+     otp_mppe(request, pwe, inst, passcode);
    return rc;
  }
  
@@@ -587,10 -416,11 +416,10 @@@ otp_detach(void *instance
   *    is single-threaded.
   */
  module_t rlm_otp = {
 -  RLM_MODULE_INIT,
    "otp",
    RLM_TYPE_THREAD_SAFE,               /* type */
-   NULL,                               /* initialization */
++  NULL,
    otp_instantiate,            /* instantiation */
 -  otp_detach,                 /* detach */
    {
      otp_authenticate,         /* authentication */
      otp_authorize,            /* authorization */
      NULL,                     /* post-proxy */
      NULL                      /* post-auth */
    },
-   NULL,                               /* destroy */
 +  otp_detach,                 /* detach */
++  NULL,
  };