~ubuntu-branches/ubuntu/natty/freeradius/natty-updates

« back to all changes in this revision

Viewing changes to src/modules/rlm_x99_token/x99_state.c

  • Committer: Bazaar Package Importer
  • Author(s): Paul Hampson
  • Date: 2006-01-15 13:34:13 UTC
  • mto: (3.1.3 dapper) (4.1.3 sid) (1.1.14 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060115133413-zo1dslttvdoalqym
Tags: upstream-1.1.0
ImportĀ upstreamĀ versionĀ 1.1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * x99_state.c
3
 
 * $Id: x99_state.c,v 1.10 2003/08/22 14:59:48 phampson Exp $
4
 
 *
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.
9
 
 *
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.
14
 
 *
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
18
 
 *
19
 
 * Copyright 2001,2002  Google, Inc.
20
 
 */
21
 
 
22
 
#ifdef FREERADIUS
23
 
#define _LRAD_MD4_H
24
 
#define _LRAD_SHA1_H
25
 
#include "radiusd.h"
26
 
#endif
27
 
#include "x99.h"
28
 
 
29
 
#include <string.h>
30
 
#include <openssl/des.h> /* des_cblock */
31
 
#include <openssl/md5.h>
32
 
#include <openssl/hmac.h>
33
 
 
34
 
 
35
 
static const char rcsid[] = "$Id: x99_state.c,v 1.10 2003/08/22 14:59:48 phampson Exp $";
36
 
 
37
 
 
38
 
/*
39
 
 * Generate the State attribute, suitable for passing to pairmake().
40
 
 * challenge must be a null terminated string, and be sized at least
41
 
 * as large as indicated in the function definition.
42
 
 *
43
 
 * Returns 0 on success, non-zero otherwise.  For successful returns,
44
 
 * ascii_state (suitable for passing to pairmake()) and raw_state, if
45
 
 * non-NULL, will be pointing to allocated storage.  The caller is
46
 
 * responsible for freeing the storage.  raw_state will not be
47
 
 * null-terminated, the caller should know the expected size (any
48
 
 * variance is size is solely due to the length of the challenge arg).
49
 
 *
50
 
 * In the simplest implementation, we would just use the challenge as state.
51
 
 * Unfortunately, the RADIUS secret protects only the User-Password
52
 
 * attribute; an attacker that can remove packets from the wire and insert
53
 
 * new ones can simply insert a replayed state without having to know
54
 
 * the secret.  If not for an attacker that can remove packets from the
55
 
 * network, I believe trivial state to be secure.
56
 
 *
57
 
 * So, we have to make up for that deficiency by signing our state with
58
 
 * data unique to this specific request.  A NAS would use the Request
59
 
 * Authenticator, we don't know what that will be when the State is
60
 
 * returned to us, so we'll use the time.  So our replay prevention
61
 
 * is limited to a time interval (inst->maxdelay).  We could keep
62
 
 * track of all challenges issued over that time interval for
63
 
 * better protection.
64
 
 *
65
 
 * Our state, then, is
66
 
 *   (challenge + resync + time + hmac(challenge + resync + time, key)),
67
 
 * where '+' denotes concatentation, 'challenge' is the ASCII octets of
68
 
 * the challenge, 'flags' is a 32-bit value that can be used to record
69
 
 * additional info, 'time' is the 32-bit time (LSB if time_t is 64 bits)
70
 
 * in network byte order, and 'key' is a random key, generated in
71
 
 * x99_token_init().  This means that only the server which generates
72
 
 * a challenge can verify it; this should be OK if your NAS's load balance
73
 
 * across RADIUS servers by a "first available" algorithm.  If your
74
 
 * NAS's round-robin (ugh), you could use the RADIUS secret instead, but
75
 
 * read RFC 2104 first, and make very sure you really want to do this.
76
 
 *
77
 
 * Note that putting the time in network byte order is pointless, since
78
 
 * only "this" server will be able to verify the hmac, due to the unique
79
 
 * key.  But I've left it in there for future consideration of sync'd
80
 
 * keys across servers (eg, using the RADIUS secret, which is probably
81
 
 * not a good idea, or reading from a file, which might be OK.)
82
 
 */
83
 
int
84
 
x99_gen_state(char **ascii_state, unsigned char **raw_state,
85
 
              const char challenge[MAX_CHALLENGE_LEN + 1], int32_t flags,
86
 
              int32_t when, const unsigned char key[16])
87
 
{
88
 
    HMAC_CTX hmac_ctx;
89
 
    unsigned char hmac[MD5_DIGEST_LENGTH];
90
 
    char *p;
91
 
    int i;
92
 
 
93
 
    /*
94
 
     * Generate the hmac.  We already have a dependency on openssl for
95
 
     * DES, so we'll use it's hmac functionality also -- saves us from
96
 
     * having to collect the data to be signed into one contiguous piece.
97
 
     */
98
 
    HMAC_Init(&hmac_ctx, key, sizeof(key), EVP_md5());
99
 
    HMAC_Update(&hmac_ctx, challenge, strlen(challenge));
100
 
    HMAC_Update(&hmac_ctx, (unsigned char *) &flags, 4);
101
 
    HMAC_Update(&hmac_ctx, (unsigned char *) &when, 4);
102
 
    HMAC_Final(&hmac_ctx, hmac, NULL);
103
 
    HMAC_cleanup(&hmac_ctx);
104
 
 
105
 
    /* Fill in raw_state if requested. */
106
 
    if (raw_state) {
107
 
        *raw_state = rad_malloc(strlen(challenge) + 8 + sizeof(hmac));
108
 
        p = *raw_state;
109
 
        (void) memcpy(p, challenge, strlen(challenge));
110
 
        p += strlen(challenge);
111
 
        (void) memcpy(p, &flags, 4);
112
 
        p += 4;
113
 
        (void) memcpy(p, &when, 4);
114
 
        p += 4;
115
 
        (void) memcpy(p, hmac, sizeof(hmac));
116
 
    }
117
 
 
118
 
    /*
119
 
     * Fill in ascii_state if requested.  (pairmake() forces us to to this.)
120
 
     * "0x" is required for pairmake().  Note that each octet expands into
121
 
     * 2 hex digits in ASCII (0xAA -> 0x4141).
122
 
     */
123
 
    if (ascii_state) {
124
 
        *ascii_state = rad_malloc(2 +                           /* "0x"      */
125
 
                                  strlen(challenge) * 2 +       /* challenge */
126
 
                                  8 +                           /* flags     */
127
 
                                  8 +                           /* time      */
128
 
                                  sizeof(hmac) * 2 +            /* hmac      */
129
 
                                  1);                           /* '\0'      */
130
 
        (void) sprintf(*ascii_state, "0x");
131
 
        p = *ascii_state + 2;
132
 
 
133
 
        /* Add the challenge. */
134
 
        for (i = 0; i < MAX_CHALLENGE_LEN / sizeof(des_cblock); ++i) {
135
 
            x99_keyblock_to_string(p, challenge, x99_hex_conversion);
136
 
            if (strlen(challenge) > sizeof(des_cblock)) {
137
 
                challenge += sizeof(des_cblock);
138
 
                p += 2 * sizeof(des_cblock);
139
 
            } else {
140
 
                p += 2 * strlen(challenge);
141
 
                break;
142
 
            }
143
 
        }
144
 
 
145
 
        /* Add the flags and time. */
146
 
        {
147
 
            des_cblock cblock;
148
 
            (void) memcpy(cblock, &flags, 4);
149
 
            (void) memcpy(&cblock[4], &when, 4);
150
 
            x99_keyblock_to_string(p, cblock, x99_hex_conversion);
151
 
        }
152
 
        p += 16;
153
 
 
154
 
        /* Add the hmac. */
155
 
        x99_keyblock_to_string(p, hmac, x99_hex_conversion);
156
 
        p += 16;
157
 
        x99_keyblock_to_string(p, &hmac[8], x99_hex_conversion);
158
 
        p += 16;
159
 
        *p = '\0';
160
 
    }
161
 
 
162
 
    return 0;
163
 
}
164