implement resync_req.
authorfcusack <fcusack>
Wed, 2 Jan 2002 12:07:10 +0000 (12:07 +0000)
committerfcusack <fcusack>
Wed, 2 Jan 2002 12:07:10 +0000 (12:07 +0000)
        challenge_req now means challenge-without-resync,
        resync_req means challenge-with-resync.

raddb/x99.conf
src/modules/rlm_x99_token/x99.h
src/modules/rlm_x99_token/x99_rlm.c
src/modules/rlm_x99_token/x99_state.c

index 4e78a91..7bd40e5 100644 (file)
@@ -94,7 +94,8 @@ x99_token {
        # (see challenge_req, below).  allow_sync must be "yes" for
        # this to work.  If you are using pam_radius as a client,
        # and you have fast_sync set to "no", you probably want to use
-       # the "skip_passwd" option in pam_radius. (default: yes)
+       # the "skip_passwd" option in pam_radius.  See also resync_req.
+       #(default: yes)
        #fast_sync = yes
 
        # Whether or not to allow asynchronous ("pure" challenge/
@@ -114,6 +115,14 @@ x99_token {
        # (default: "challenge")
        #challenge_req = "challenge"
 
+       # Like challenge_req, except that using this as the password
+       # additionally resyncs the token.  You can set this to the same
+       # value as challenge_req if you want this behaviour all the time,
+       # eg if your tokens don't support async-without-resync.  When
+       # not doing fast_sync, the token is always resynced.
+       # (default: "resync")
+       #resync_req = "resync"
+
        # Tokens that are event synchronous can easily lose sync with
        # the server, eg if the user plays with the token they will
        # increment the token's event counter, leaving the server
index da9dc0a..86177bf 100644 (file)
@@ -36,6 +36,8 @@
 #define MAX_CHALLENGE_LEN 32
 /* Password that means "challenge me" in fast_sync mode */
 #define CHALLENGE_REQ "challenge"
+/* Password that means "challenge me and resync" in fast_sync mode */
+#define RESYNC_REQ "resync"
 
 /* Max event window size for sync modes */
 #define MAX_EWINDOW_SIZE 10
@@ -108,7 +110,8 @@ extern int x99_mac(const char *input, des_cblock output, des_cblock keyblock);
 /* x99_state.c */
 extern int x99_gen_state(char **ascii_state, unsigned char **raw_state,
                         const char challenge[MAX_CHALLENGE_LEN + 1],
-                        int32_t when, const unsigned char key[16]);
+                        int32_t flags, int32_t when,
+                        const unsigned char key[16]);
 
 /* x99_util.c */
 /* Character maps for generic hex and vendor specific decimal modes */
index 7fab545..943557d 100644 (file)
@@ -32,7 +32,6 @@
  */
 
 /*
- * TODO: add "resync" keyword (like "challenge").
  * TODO: all requests (success or fail) should take ~ the same amount of time.
  * TODO: x99_pwe: change add_vps to success_vps and fail_vps.
  * TODO: add Auth-Type config item if not present?
@@ -74,6 +73,7 @@ typedef struct x99_token_t {
     int fast_sync;     /* response-before-challenge mode                  */
     int allow_async;   /* C/R mode allowed?                               */
     char *chal_req;    /* keyword requesting challenge for fast_sync mode */
+    char *resync_req;  /* keyword requesting resync for fast_sync mode    */
     int ewindow_size;  /* sync mode event window size (right side value)  */
 #if 0
     int twindow_min;   /* sync mode time window left side                 */
@@ -105,6 +105,8 @@ static CONF_PARSER module_config[] = {
       NULL, "0" },
     { "challenge_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, chal_req),
       NULL, CHALLENGE_REQ },
+    { "resync_req", PW_TYPE_STRING_PTR, offsetof(x99_token_t, resync_req),
+      NULL, RESYNC_REQ },
     { "ewindow_size", PW_TYPE_INTEGER, offsetof(x99_token_t, ewindow_size),
       NULL, "0" },
 #if 0
@@ -244,6 +246,7 @@ x99_token_authorize(void *instance, REQUEST *request)
     x99_user_info_t user_info;
     int user_found, auth_type;
     int pwattr;
+    int32_t sflags = 0; /* flags for state */
     VALUE_PAIR *vp;
 
     /* Early exit if Auth-Type == reject */
@@ -295,7 +298,9 @@ x99_token_authorize(void *instance, REQUEST *request)
     if (inst->fast_sync &&
        ((user_info.card_id & X99_CF_SM) || !user_found)) {
 
-       if (x99_pw_valid(request, pwattr, inst->chal_req, NULL)) {
+       if ((x99_pw_valid(request, pwattr, inst->resync_req, NULL) &&
+               /* Set a bit indicating resync */ (sflags |= htonl(1))) ||
+           x99_pw_valid(request, pwattr, inst->chal_req, NULL)) {
            /*
             * Generate a challenge if requested.  We don't test for card
             * support [for async] because it's tricky for unknown users.
@@ -327,6 +332,10 @@ x99_token_authorize(void *instance, REQUEST *request)
     } /* if (fast_sync && card supports sync mode) */
 
 gen_challenge:
+    /* Set the resync bit by default if the user can't request it. */
+    if (!inst->fast_sync)
+       sflags |= htonl(1);
+
     /* Generate a random challenge. */
     if (x99_get_random(rnd_fd, rawchallenge, inst->chal_len) == -1) {
        radlog(L_ERR, "rlm_x99_token: autz: failed to obtain random data");
@@ -357,11 +366,12 @@ gen_challenge:
        }
        now = htonl(now);
 
-       if (x99_gen_state(&state, NULL, challenge, now, hmac_key) != 0) {
+       if (x99_gen_state(&state, NULL, challenge, sflags, now, hmac_key) != 0){
            radlog(L_ERR, "rlm_x99_token: autz: failed to generate state");
            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);
     }
@@ -399,6 +409,7 @@ x99_token_authenticate(void *instance, REQUEST *request)
     x99_user_info_t user_info;
     char *username;
     int i, failcount, pwattr, rc;
+    int32_t sflags = 0; /* flags from state */
     time_t last_auth;
 
     char challenge[MAX_CHALLENGE_LEN + 1];
@@ -437,7 +448,7 @@ x99_token_authenticate(void *instance, REQUEST *request)
 
            /* Extend expected length if state should have been protected. */
            if (user_info.card_id & X99_CF_AM)
-               e_length += 4 + 16; /* time + hmac */
+               e_length += 4 + 4 + 16; /* sflags + time + hmac */
 
            if (vp->length != e_length) {
                radlog(L_AUTH, "rlm_x99_token: auth: bad state for [%s]: "
@@ -452,8 +463,9 @@ x99_token_authenticate(void *instance, REQUEST *request)
            /* Verify the state. */
            (void) memset(challenge, 0, sizeof(challenge));
            (void) memcpy(challenge, vp->strvalue, inst->chal_len);
-           (void) memcpy(&then, vp->strvalue + inst->chal_len, 4);
-           if (x99_gen_state(NULL, &state, challenge, then, hmac_key) != 0) {
+           (void) memcpy(&sflags, vp->strvalue + inst->chal_len, 4);
+           (void) memcpy(&then, vp->strvalue + inst->chal_len + 4, 4);
+           if (x99_gen_state(NULL,&state,challenge,sflags,then,hmac_key) != 0){
                radlog(L_ERR, "rlm_x99_token: auth: failed to generate state");
                return RLM_MODULE_FAIL;
            }
@@ -591,26 +603,40 @@ good_state:
                           "in async mode", username);
        }
 
-       /*
-        * Resync the card.  The sync data doesn't mean anything for
-        * async-only cards, but we want the side effects of resetting
-        * the failcount and the last auth time.  We "fail-out" if we
-        * can't do this, because if we can't update the last auth time,
-        * we will be open to replay attacks over the lifetime of the
-        * State attribute (inst->maxdelay).
-        */
        rc = RLM_MODULE_OK;
-       if (x99_get_sync_data(inst->syncdir, username, user_info.card_id,
-                             1, 0, challenge, user_info.keyblock) != 0) {
-           radlog(L_ERR, "rlm_x99_token: auth: unable to get "
-                         "sync data e:%d t:%d for [%s] (for resync)",
-                         1, 0, username);
-           rc = RLM_MODULE_FAIL;
-       } else if (x99_set_sync_data(inst->syncdir, username, challenge,
-                                    user_info.keyblock) != 0) {
-           radlog(L_ERR, "rlm_x99_token: auth: unable to set sync "
-                         "data for [%s] (for resync)", username);
-           rc = RLM_MODULE_FAIL;
+       if (ntohl(sflags) & 1) {
+           /*
+            * Resync the card.  The sync data doesn't mean anything for
+            * async-only cards, but we want the side effects of resetting
+            * the failcount and the last auth time.  We "fail-out" if we
+            * can't do this, because if we can't update the last auth time,
+            * we will be open to replay attacks over the lifetime of the
+            * State attribute (inst->maxdelay).
+            */
+           if (x99_get_sync_data(inst->syncdir, username, user_info.card_id,
+                                 1, 0, challenge, user_info.keyblock) != 0) {
+               radlog(L_ERR, "rlm_x99_token: auth: unable to get "
+                             "sync data e:%d t:%d for [%s] (for resync)",
+                             1, 0, username);
+               rc = RLM_MODULE_FAIL;
+           } else if (x99_set_sync_data(inst->syncdir, username, challenge,
+                                        user_info.keyblock) != 0) {
+               radlog(L_ERR, "rlm_x99_token: auth: unable to set sync "
+                             "data for [%s] (for resync)", username);
+               rc = RLM_MODULE_FAIL;
+           }
+       } else {
+           /* Just update last_auth, failcount. */
+           if (x99_reset_failcount(inst->syncdir, username) != 0) {
+               radlog(L_ERR, "rlm_x99_token: auth: unable to reset failcount "
+                             "for [%s]", username);
+               /* NB: don't fail */
+           }
+           if (x99_upd_last_auth(inst->syncdir, username) != 0) {
+               radlog(L_ERR, "rlm_x99_token: auth: unable to update auth time "
+                             "for [%s]", username);
+               rc = RLM_MODULE_FAIL;
+           }
        }
        goto return_pw_valid;
     } /* if (user authenticated async) */
@@ -700,6 +726,7 @@ x99_token_detach(void *instance)
     free(inst->syncdir);
     free(inst->chal_text);
     free(inst->chal_req);
+    free(inst->resync_req);
     free(instance);
     return 0;
 }
index bfaa28c..43c74a5 100644 (file)
@@ -59,9 +59,11 @@ static const char rcsid[] = "$Id$";
  * track of all challenges issued over that time interval for
  * better protection.
  *
- * Our state, then, is (challenge + time + hmac(challenge + time, key)),
+ * Our state, then, is
+ *   (challenge + resync + time + hmac(challenge + resync + time, key)),
  * where '+' denotes concatentation, 'challenge' is the ASCII octets of
- * the challenge, 'time' is the 32-bit time (LSB if time_t is 64 bits)
+ * the challenge, 'flags' is a 32-bit value that can be used to record
+ * additional info, 'time' is the 32-bit time (LSB if time_t is 64 bits)
  * in network byte order, and 'key' is a random key, generated in
  * x99_token_init().  This means that only the server which generates
  * a challenge can verify it; this should be OK if your NAS's load balance
@@ -77,8 +79,8 @@ static const char rcsid[] = "$Id$";
  */
 int
 x99_gen_state(char **ascii_state, unsigned char **raw_state,
-             const char challenge[MAX_CHALLENGE_LEN + 1], int32_t when,
-             const unsigned char key[16])
+             const char challenge[MAX_CHALLENGE_LEN + 1], int32_t flags,
+             int32_t when, const unsigned char key[16])
 {
     HMAC_CTX hmac_ctx;
     unsigned char hmac[MD5_DIGEST_LENGTH];
@@ -92,16 +94,19 @@ x99_gen_state(char **ascii_state, unsigned char **raw_state,
      */
     HMAC_Init(&hmac_ctx, key, sizeof(key), EVP_md5());
     HMAC_Update(&hmac_ctx, challenge, strlen(challenge));
+    HMAC_Update(&hmac_ctx, (unsigned char *) &flags, 4);
     HMAC_Update(&hmac_ctx, (unsigned char *) &when, 4);
     HMAC_Final(&hmac_ctx, hmac, NULL);
     HMAC_cleanup(&hmac_ctx);
 
     /* Fill in raw_state if requested. */
     if (raw_state) {
-       *raw_state = rad_malloc(strlen(challenge) + 4 + sizeof(hmac));
+       *raw_state = rad_malloc(strlen(challenge) + 8 + sizeof(hmac));
        p = *raw_state;
        (void) memcpy(p, challenge, strlen(challenge));
        p += strlen(challenge);
+       (void) memcpy(p, &flags, 4);
+       p += 4;
        (void) memcpy(p, &when, 4);
        p += 4;
        (void) memcpy(p, hmac, sizeof(hmac));
@@ -115,6 +120,7 @@ x99_gen_state(char **ascii_state, unsigned char **raw_state,
     if (ascii_state) {
        *ascii_state = rad_malloc(2 +                           /* "0x"      */
                                  strlen(challenge) * 2 +       /* challenge */
+                                 8 +                           /* flags     */
                                  8 +                           /* time      */
                                  sizeof(hmac) * 2 +            /* hmac      */
                                  1);                           /* '\0'      */
@@ -133,13 +139,14 @@ x99_gen_state(char **ascii_state, unsigned char **raw_state,
            }
        }
 
-       /* Add the time. */
+       /* Add the flags and time. */
        {
            des_cblock cblock;
-           (void) memcpy(cblock, &when, 4);
+           (void) memcpy(cblock, &flags, 4);
+           (void) memcpy(&cblock[4], &when, 4);
            x99_keyblock_to_string(p, cblock, x99_hex_conversion);
        }
-       p += 8; /* discard lower 8 bytes, which are junk */
+       p += 16;
 
        /* Add the hmac. */
        x99_keyblock_to_string(p, hmac, x99_hex_conversion);