3
* $Id: otp_radstate.c,v 1.4.2.1 2005/12/08 01:30:51 fcusack 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.
20
* Copyright 2005 TRI-D Systems, Inc.
30
#include <openssl/des.h> /* des_cblock */
31
#include <openssl/md5.h>
32
#include <openssl/hmac.h>
35
static const char rcsid[] = "$Id: otp_radstate.c,v 1.4.2.1 2005/12/08 01:30:51 fcusack 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 in 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, but 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->chal_delay). We could keep
62
* track of all challenges issued over that time interval for
66
* (challenge + flags + time + hmac(challenge + resync + time, key)),
67
* where '+' denotes concatentation, 'challenge' is ...
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
* otp_instantiate(). This means that only the server which generates a
72
* challenge can verify it; this should be OK if your NAS's load balance
73
* across RADIUS servers using 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
otp_gen_state(char **ascii_state, unsigned char **raw_state,
85
const unsigned char challenge[OTP_MAX_CHALLENGE_LEN],
87
int32_t flags, int32_t when, const unsigned char key[16])
90
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, clen);
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(clen + 8 + sizeof(hmac));
109
(void) memcpy(p, challenge, clen);
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
clen * 2 + /* challenge */
128
sizeof(hmac) * 2 + /* hmac */
130
(void) sprintf(*ascii_state, "0x");
131
p = *ascii_state + 2;
133
/* Add the challenge. */
134
(void) otp_keyblock2keystring(p, challenge, clen, otp_hex_conversion);
137
/* Add the flags and time. */
138
(void) otp_keyblock2keystring(p, (unsigned char *) &flags, 4,
141
(void) otp_keyblock2keystring(p, (unsigned char *) &when, 4,
146
(void) otp_keyblock2keystring(p, hmac, 16, otp_hex_conversion);