import from HEAD:
[freeradius.git] / src / modules / rlm_otp / otp_rlm.c
index df0a0c0..cf411fc 100644 (file)
@@ -18,7 +18,7 @@
  *
  * Copyright 2000,2001,2002  The FreeRADIUS server project
  * Copyright 2001,2002  Google, Inc.
- * Copyright 2005 TRI-D Systems, Inc.
+ * Copyright 2005,2006 TRI-D Systems, Inc.
  */
 
 /*
@@ -49,7 +49,7 @@
 
 #include "otp.h"
 #ifdef FREERADIUS
-#include "modules.h"
+#include <modules.h>
 #endif
 
 static const char rcsid[] = "$Id$";
@@ -100,12 +100,9 @@ static const CONF_PARSER module_config[] = {
     offsetof(otp_option_t, mschap_mppe_policy), NULL, "2" },
   { "mschap_mppe_bits", PW_TYPE_INTEGER,
     offsetof(otp_option_t, mschap_mppe_types), NULL, "2" },
-#if 0
-  { "twindow_min", PW_TYPE_INTEGER, offsetof(otp_option_t, twindow_min),
-    NULL, "0" },
-  { "twindow_max", PW_TYPE_INTEGER, offsetof(otp_option_t, twindow_max),
-    NULL, "0" },
-#endif
+
+  { "debug", PW_TYPE_BOOLEAN, offsetof(otp_option_t, debug),
+    NULL, "no" },
 
   { NULL, -1, 0, NULL, NULL }          /* end the list */
 };
@@ -131,6 +128,7 @@ otprc2rlmrc(int rc)
 static int
 otp_instantiate(CONF_SECTION *conf, void **instance)
 {
+  const char *log_prefix = OTP_MODULE_NAME;
   otp_option_t *opt;
   char *p;
 
@@ -147,8 +145,9 @@ otp_instantiate(CONF_SECTION *conf, void **instance)
   /* Onetime initialization. */
   if (!ninstance) {
     /* Generate a random key, used to protect the State attribute. */
-    if (otp_get_random(-1, hmac_key, sizeof(hmac_key)) == -1) {
-      otp_log(OTP_LOG_ERR, "failed to obtain random data for hmac_key");
+    if (otp_get_random(-1, hmac_key, sizeof(hmac_key), log_prefix) == -1) {
+      otp_log(OTP_LOG_ERR, "%s: %s: failed to obtain random data for hmac_key",
+              log_prefix, __func__);
       free(opt);
       return -1;
     }
@@ -168,8 +167,8 @@ otp_instantiate(CONF_SECTION *conf, void **instance)
   if ((opt->chal_len < 5) || (opt->chal_len > OTP_MAX_CHALLENGE_LEN)) {
     opt->chal_len = 6;
     otp_log(OTP_LOG_ERR,
-            "invalid challenge_length, range 5-%d, using default of 6",
-            OTP_MAX_CHALLENGE_LEN);
+            "%s: %s: invalid challenge_length, range 5-%d, using default of 6",
+            log_prefix, __func__, OTP_MAX_CHALLENGE_LEN);
   }
 
   /* Enforce a single "%" sequence, which must be "%s" */
@@ -178,31 +177,51 @@ otp_instantiate(CONF_SECTION *conf, void **instance)
       strncmp(p,"%s",2)) {
     free(opt->chal_prompt);
     opt->chal_prompt = strdup(OTP_CHALLENGE_PROMPT);
-    otp_log(OTP_LOG_ERR, "invalid challenge_prompt, using default of \"%s\"",
-            OTP_CHALLENGE_PROMPT);
+    otp_log(OTP_LOG_ERR,
+            "%s: %s: invalid challenge_prompt, using default of \"%s\"",
+            log_prefix, __func__, OTP_CHALLENGE_PROMPT);
   }
 
   if (opt->softfail < 0) {
     opt->softfail = 5;
-    otp_log(OTP_LOG_ERR, "softfail must be at least 1 "
-            "(or 0 == infinite), using default of 5");
+    otp_log(OTP_LOG_ERR, "%s: %s: softfail must be at least 1 "
+                         "(or 0 == infinite), using default of 5",
+            log_prefix, __func__);
   }
 
   if (opt->hardfail < 0) {
     opt->hardfail = 0;
-    otp_log(OTP_LOG_ERR, "hardfail must be at least 1 "
-            "(or 0 == infinite), using default of 0");
+    otp_log(OTP_LOG_ERR, "%s: %s: hardfail must be at least 1 "
+                         "(or 0 == infinite), using default of 0",
+            log_prefix, __func__);
+  }
+
+  if (!opt->hardfail && opt->hardfail <= opt->softfail) {
+    /*
+     * This is noise if the admin leaves softfail alone, so it gets
+     * the default value of 5, and sets hardfail <= to that ... but
+     * in practice that will never happen.  Anyway, it is easily
+     * overcome with a softfail setting of 0.
+     *
+     * This is because we can't tell the difference between a default
+     * [softfail] value and an admin-configured one.
+     */
+    otp_log(OTP_LOG_ERR, "%s: %s: hardfail (%d) is less than softfail (%d), "
+                         "effectively disabling softfail",
+            log_prefix, __func__, opt->hardfail, opt->softfail);
   }
 
   if (opt->fast_sync && !opt->allow_sync) {
     opt->fast_sync = 0;
-    otp_log(OTP_LOG_INFO,
-            "fast_sync is yes, but allow_sync is no; disabling fast_sync");
+    otp_log(OTP_LOG_ERR, "%s: %s: fast_sync is yes, but allow_sync is no; "
+                         "disabling fast_sync",
+            log_prefix, __func__);
   }
 
   if (!opt->allow_sync && !opt->allow_async) {
     otp_log(OTP_LOG_ERR,
-            "at least one of {allow_async, allow_sync} must be set");
+            "%s: %s: at least one of {allow_async, allow_sync} must be set",
+            log_prefix, __func__);
     free(opt);
     return -1;
   }
@@ -210,67 +229,67 @@ otp_instantiate(CONF_SECTION *conf, void **instance)
   if ((opt->ewindow_size > OTP_MAX_EWINDOW_SIZE) ||
     (opt->ewindow_size < 0)) {
     opt->ewindow_size = 0;
-    otp_log(OTP_LOG_ERR, "max ewindow_size is %d, using default of 0",
-            OTP_MAX_EWINDOW_SIZE);
+    otp_log(OTP_LOG_ERR, "%s: %s: max ewindow_size is %d, using default of 0",
+            log_prefix, __func__, OTP_MAX_EWINDOW_SIZE);
   }
 
   if (opt->rwindow_size && (opt->rwindow_size < opt->ewindow_size)) {
     opt->rwindow_size = 0;
-    otp_log(OTP_LOG_ERR, "rwindow_size must be at least as large as "
-                         "ewindow_size, using default of 0");
+    otp_log(OTP_LOG_ERR, "%s: %s: rwindow_size must be at least as large as "
+                         "ewindow_size, using default of 0",
+            log_prefix, __func__);
   }
 
   if (opt->rwindow_size && !opt->rwindow_delay) {
     opt->rwindow_size = 0;
-    otp_log(OTP_LOG_ERR, "rwindow_size is non-zero, "
-                         "but rwindow_delay is zero; disabling rwindow");
+    otp_log(OTP_LOG_ERR, "%s: %s: rwindow_size is non-zero, "
+                         "but rwindow_delay is zero; disabling rwindow",
+            log_prefix, __func__);
   }
 
   if ((opt->mschapv2_mppe_policy > 2) || (opt->mschapv2_mppe_policy < 0)) {
     opt->mschapv2_mppe_policy = 2;
-    otp_log(OTP_LOG_ERR, "invalid value for mschapv2_mppe, using default of 2");
+    otp_log(OTP_LOG_ERR,
+            "%s: %s: invalid value for mschapv2_mppe, using default of 2",
+            log_prefix, __func__);
   }
 
   if ((opt->mschapv2_mppe_types > 2) || (opt->mschapv2_mppe_types < 0)) {
     opt->mschapv2_mppe_types = 2;
     otp_log(OTP_LOG_ERR,
-            "invalid value for mschapv2_mppe_bits, using default of 2");
+            "%s: %s: invalid value for mschapv2_mppe_bits, using default of 2",
+            log_prefix, __func__);
   }
 
   if ((opt->mschap_mppe_policy > 2) || (opt->mschap_mppe_policy < 0)) {
     opt->mschap_mppe_policy = 2;
-    otp_log(OTP_LOG_ERR, "invalid value for mschap_mppe, using default of 2");
+    otp_log(OTP_LOG_ERR,
+            "%s: %s: invalid value for mschap_mppe, using default of 2",
+            log_prefix, __func__);
   }
 
   if (opt->mschap_mppe_types != 2) {
     opt->mschap_mppe_types = 2;
     otp_log(OTP_LOG_ERR,
-            "invalid value for mschap_mppe_bits, using default of 2");
-  }
-
-#if 0
-  if (opt->twindow_max - opt->twindow_min > OTP_MAX_TWINDOW_SIZE) {
-    opt->twindow_min = opt->twindow_max = 0;
-    otp_log(OTP_LOG_ERR, "max time window size is %d, using default of 0",
-            OTP_MAX_TWINDOW_SIZE);
-  }
-  if ((opt->twindow_min > 0) || (opt->twindow_max < 0) ||
-      (opt->twindow_max < opt->twindow_min)) {
-    opt->twindow_min = opt->twindow_max = 0;
-    otp_log(OTP_LOG_ERR, "invalid values for time window, using default of 0");
+            "%s: %s: invalid value for mschap_mppe_bits, using default of 2",
+            log_prefix, __func__);
   }
-#endif
 
-  /* Set the instance name (for use with authorize()) */
+  /* set the instance name (for use with authorize()) */
   opt->name = cf_section_name2(conf);
   if (!opt->name)
     opt->name = cf_section_name1(conf);
   if (!opt->name) {
-    otp_log(OTP_LOG_CRIT, "no instance name (this can't happen)");
+    otp_log(OTP_LOG_CRIT, "%s: %s: no instance name (this can't happen)",
+            log_prefix, __func__);
     free(opt);
     return -1;
   }
 
+  /* set debug opt for portable debug output (see DEBUG definition) */
+  if (debug_flag)
+    opt->debug = 1;
+
   *instance = opt;
   return 0;
 }
@@ -281,6 +300,7 @@ static int
 otp_authorize(void *instance, REQUEST *request)
 {
   otp_option_t *inst = (otp_option_t *) instance;
+  const char *log_prefix = OTP_MODULE_NAME;
 
   char challenge[OTP_MAX_CHALLENGE_LEN + 1];   /* +1 for '\0' terminator */
   char *state;
@@ -300,7 +320,7 @@ otp_authorize(void *instance, REQUEST *request)
     auth_type_found = 0;
     if ((vp = pairfind(request->config_items, PW_AUTHTYPE)) != NULL) {
       auth_type_found = 1;
-      if (strcmp(vp->vp_strvalue, inst->name))
+      if (strcmp(vp->strvalue, inst->name))
         return RLM_MODULE_NOOP;
     }
   }
@@ -314,21 +334,23 @@ otp_authorize(void *instance, REQUEST *request)
   /* User-Name attribute required. */
   if (!request->username) {
     otp_log(OTP_LOG_AUTH,
-            "autz: Attribute \"User-Name\" required for authentication.");
+            "%s: %s: Attribute \"User-Name\" required for authentication.",
+            log_prefix, __func__);
     return RLM_MODULE_INVALID;
   }
 
-  if ((data.pwattr = otp_pwe_present(request)) == 0) {
-    otp_log(OTP_LOG_AUTH, "autz: Attribute \"User-Password\" "
-            "or equivalent required for authentication.");
+  if ((data.pwattr = otp_pwe_present(request, log_prefix)) == 0) {
+    otp_log(OTP_LOG_AUTH, "%s: %s: Attribute \"User-Password\" "
+                          "or equivalent required for authentication.",
+            log_prefix, __func__);
     return RLM_MODULE_INVALID;
   }
 
   /* fast_sync mode (challenge only if requested) */
   if (inst->fast_sync) {
-    if ((!otp_pwe_cmp(&data, inst->resync_req) &&
+    if ((!otp_pwe_cmp(&data, inst->resync_req, log_prefix) &&
         /* Set a bit indicating resync */ (sflags |= htonl(1))) ||
-        !otp_pwe_cmp(&data, inst->chal_req)) {
+        !otp_pwe_cmp(&data, inst->chal_req, log_prefix)) {
       /*
        * Generate a challenge if requested.  Note that we do this
        * even if configuration doesn't allow async mode.
@@ -351,8 +373,9 @@ gen_challenge:
     sflags |= htonl(1);
 
   /* Generate a random challenge. */
-  if (otp_get_challenge(-1, challenge, inst->chal_len) == -1) {
-    otp_log(OTP_LOG_ERR, "autz: failed to obtain random challenge");
+  if (otp_async_challenge(-1, challenge, inst->chal_len, log_prefix) == -1) {
+    otp_log(OTP_LOG_ERR, "%s: %s: failed to obtain random challenge",
+            log_prefix, __func__);
     return RLM_MODULE_FAIL;
   }
 
@@ -369,19 +392,21 @@ gen_challenge:
     time_t now = time(NULL);
 
     if (sizeof(now) != 4 || sizeof(long) != 4) {
-      otp_log(OTP_LOG_ERR, "autz: only ILP32 arch is supported");
+      otp_log(OTP_LOG_ERR, "%s: %s: only ILP32 arch is supported",
+              log_prefix, __func__);
       return RLM_MODULE_FAIL;
     }
     now = htonl(now);
 
-    if (otp_gen_state(&state, NULL, challenge, sflags, now, hmac_key) != 0) {
-      otp_log(OTP_LOG_ERR, "autz: failed to generate state");
+    if (otp_gen_state(&state, NULL, challenge, inst->chal_len, sflags,
+                      now, hmac_key) != 0) {
+      otp_log(OTP_LOG_ERR, "%s: %s: failed to generate state",
+              log_prefix, __func__);
       return RLM_MODULE_FAIL;
     }
   } else {
-    /* x2 b/c pairmake() string->octet needs even num of digits */
-    state = rad_malloc(3 + inst->chal_len * 2);
-    (void) sprintf(state, "0x%s%s", challenge, challenge);
+    state = rad_malloc(5);
+    (void) sprintf(state, "0x00");
   }
   pairadd(&request->reply->vps, pairmake("State", state, T_OP_EQ));
   free(state);
@@ -417,12 +442,13 @@ static int
 otp_authenticate(void *instance, REQUEST *request)
 {
   otp_option_t *inst = (otp_option_t *) instance;
+  const char *log_prefix = OTP_MODULE_NAME;
 
   char *username;
   int rc;
   int resync = 0;      /* resync flag for async mode */
 
-  char challenge[OTP_MAX_CHALLENGE_LEN + 1];
+  unsigned char challenge[OTP_MAX_CHALLENGE_LEN];      /* cf. authorize() */
   VALUE_PAIR *add_vps = NULL;
 
   struct otp_pwe_cmp_t data = {
@@ -431,17 +457,21 @@ otp_authenticate(void *instance, REQUEST *request)
     .returned_vps      = &add_vps
   };
 
+  challenge[0] = '\0'; /* initialize for otp_pw_valid() */
+
   /* User-Name attribute required. */
   if (!request->username) {
     otp_log(OTP_LOG_AUTH,
-            "auth: Attribute \"User-Name\" required for authentication.");
+            "%s: %s: Attribute \"User-Name\" required for authentication.",
+            log_prefix, __func__);
     return RLM_MODULE_INVALID;
   }
-  username = request->username->vp_strvalue;
+  username = request->username->strvalue;
 
-  if ((data.pwattr = otp_pwe_present(request)) == 0) {
-    otp_log(OTP_LOG_AUTH, "auth: Attribute \"User-Password\" "
-                          "or equivalent required for authentication.");
+  if ((data.pwattr = otp_pwe_present(request, log_prefix)) == 0) {
+    otp_log(OTP_LOG_AUTH, "%s: %s: Attribute \"User-Password\" "
+                          "or equivalent required for authentication.",
+            log_prefix, __func__);
     return RLM_MODULE_INVALID;
   }
 
@@ -452,38 +482,41 @@ otp_authenticate(void *instance, REQUEST *request)
                                           OTP_MODULE_NAME, T_OP_EQ));
 
   /* Retrieve the challenge (from State attribute). */
-  challenge[0] = '\0';
   {
     VALUE_PAIR *vp;
     unsigned char      *state;
     int32_t            sflags = 0;     /* state flags */
-    time_t             then;           /* state timestamp */
+    int32_t            then;           /* state timestamp */
 
     if ((vp = pairfind(request->packet->vps, PW_STATE)) != NULL) {
-      int e_length = inst->chal_len;
+      int e_length;
 
-      /* Extend expected length if state should have been protected. */
+      /* set expected State length */
       if (inst->allow_async)
-        e_length += 4 + 4 + 16; /* sflags + time + hmac */
+        e_length += inst->chal_len + 4 + 4 + 16; /* see otp_gen_state() */
+      else
+        e_length = 1;
 
       if (vp->length != e_length) {
-        otp_log(OTP_LOG_AUTH, "auth: bad state for [%s]: length", username);
+        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. */
-        (void) memset(challenge, 0, sizeof(challenge));
-        (void) memcpy(challenge, vp->vp_strvalue, inst->chal_len);
-        (void) memcpy(&sflags, vp->vp_strvalue + inst->chal_len, 4);
-        (void) memcpy(&then, vp->vp_strvalue + inst->chal_len + 4, 4);
-        if (otp_gen_state(NULL, &state, challenge,
+        (void) memcpy(challenge, vp->strvalue, inst->chal_len);
+        (void) memcpy(&sflags, vp->strvalue + inst->chal_len, 4);
+        (void) memcpy(&then, vp->strvalue + inst->chal_len + 4, 4);
+        if (otp_gen_state(NULL, &state, challenge, inst->chal_len,
                           sflags, then, hmac_key) != 0) {
-          otp_log(OTP_LOG_ERR, "auth: failed to generate state");
+          otp_log(OTP_LOG_ERR, "%s: %s: failed to generate state",
+                  log_prefix, __func__);
           return RLM_MODULE_FAIL;
         }
-        if (memcmp(state, vp->vp_strvalue, vp->length)) {
-          otp_log(OTP_LOG_AUTH, "auth: bad state for [%s]: hmac", username);
+        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;
         }
@@ -492,7 +525,8 @@ otp_authenticate(void *instance, REQUEST *request)
         /* State is valid, but check expiry. */
         then = ntohl(then);
         if (time(NULL) - then > inst->chal_delay) {
-          otp_log(OTP_LOG_AUTH, "auth: bad state for [%s]: expired", username);
+          otp_log(OTP_LOG_AUTH, "%s: %s: bad state for [%s]: expired",
+                  log_prefix, __func__, username);
           return RLM_MODULE_REJECT;
         }
         resync = ntohl(sflags) & 1;
@@ -502,7 +536,7 @@ otp_authenticate(void *instance, REQUEST *request)
 
   /* do it */
   rc = otprc2rlmrc(otp_pw_valid(username, challenge, NULL, resync, inst,
-                                otp_pwe_cmp, &data, "auth"));
+                                otp_pwe_cmp, &data, log_prefix));
 
   /* Handle any vps returned from otp_pwe_cmp(). */
   if (rc == RLM_MODULE_OK) {
@@ -544,11 +578,10 @@ otp_detach(void *instance)
  *     is single-threaded.
  */
 module_t rlm_otp = {
-  RLM_MODULE_INIT,
   "otp",
   RLM_TYPE_THREAD_SAFE,                /* type */
+  NULL,                                /* initialization */
   otp_instantiate,             /* instantiation */
-  otp_detach,                  /* detach */
   {
     otp_authenticate,          /* authentication */
     otp_authorize,             /* authorization */
@@ -559,4 +592,6 @@ module_t rlm_otp = {
     NULL,                      /* post-proxy */
     NULL                       /* post-auth */
   },
+  otp_detach,                  /* detach */
+  NULL,                                /* destroy */
 };