3
* $Id: x99_state.c,v 1.10 2003/08/22 14:59:48 phampson 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 2001,2002 Google, Inc.
30
#include <openssl/des.h> /* des_cblock */
31
#include <openssl/md5.h>
32
#include <openssl/hmac.h>
35
static const char rcsid[] = "$Id: x99_state.c,v 1.10 2003/08/22 14:59:48 phampson Exp $";
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.
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).
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.
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
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.
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.)
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])
89
unsigned char hmac[MD5_DIGEST_LENGTH];
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.
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);
105
/* Fill in raw_state if requested. */
107
*raw_state = rad_malloc(strlen(challenge) + 8 + sizeof(hmac));
109
(void) memcpy(p, challenge, strlen(challenge));
110
p += strlen(challenge);
111
(void) memcpy(p, &flags, 4);
113
(void) memcpy(p, &when, 4);
115
(void) memcpy(p, hmac, sizeof(hmac));
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).
124
*ascii_state = rad_malloc(2 + /* "0x" */
125
strlen(challenge) * 2 + /* challenge */
128
sizeof(hmac) * 2 + /* hmac */
130
(void) sprintf(*ascii_state, "0x");
131
p = *ascii_state + 2;
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);
140
p += 2 * strlen(challenge);
145
/* Add the flags and time. */
148
(void) memcpy(cblock, &flags, 4);
149
(void) memcpy(&cblock[4], &when, 4);
150
x99_keyblock_to_string(p, cblock, x99_hex_conversion);
155
x99_keyblock_to_string(p, hmac, x99_hex_conversion);
157
x99_keyblock_to_string(p, &hmac[8], x99_hex_conversion);