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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * Copyright 2001,2002 Google, Inc.
20 * Copyright 2005,2006 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$";
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 * 'rad_state' (suitable for passing to pairmake()) and 'raw_state',
45 * if non-NULL, will be pointing to allocated storage. The caller is
46 * responsible for freeing the storage.
48 * In the simplest implementation, we would just use the challenge as state.
49 * Unfortunately, the RADIUS secret protects only the User-Password
50 * attribute; an attacker that can remove packets from the wire and insert
51 * new ones can simply insert a replayed state without having to know
52 * the secret. If not for an attacker that can remove packets from the
53 * network, I believe trivial state to be secure.
55 * So, we have to make up for that deficiency by signing our state with
56 * data unique to this specific request. A NAS would use the Request
57 * Authenticator, but we don't know what that will be when the State is
58 * returned to us, so we'll use the time. So our replay prevention
59 * is limited to a time interval (inst->chal_delay). We could keep
60 * track of all challenges issued over that time interval for
64 * (challenge + flags + time + hmac(challenge + resync + time, key)),
65 * where '+' denotes concatentation, 'challenge' is ... the challenge,
66 * 'flags' is a 32-bit value that can be used to record additional info,
67 * 'time' is the 32-bit time (LSB if time_t is 64 bits), and 'key' is a
68 * random key, generated in otp_instantiate(). 'flags' and 'time' are
69 * in network byte order.
71 * As the signing key is unique to each server, only the server which
72 * generates a challenge can verify it; this should be OK if your NAS's
73 * load balance across RADIUS servers using a "first available" algorithm.
74 * If your NAS's round-robin and don't "stick" to the same server if they
75 * see a State attribute (ugh), you could use the RADIUS secret instead,
76 * but read RFC 2104 first, and make very sure you really want to do this.
78 * Since only the "same server" can verify State, 'flags' and 'time' doesn't
79 * really need to be in network byte order, but we do it anyway.
81 * The State attribute is an octet string, however some versions of Cisco
82 * IOS and Catalyst OS (at least IOS 12.1(26)E4 and CatOS 7.6.12) treat it
83 * as an ASCII string (they only return data up to the first NUL byte).
84 * So we must handle state as an ASCII string (0x00 -> 0x3030).
87 otp_gen_state(char **rad_state, unsigned char **raw_state,
88 const unsigned char challenge[OTP_MAX_CHALLENGE_LEN],
90 int32_t flags, int32_t when, const unsigned char key[16])
93 unsigned char hmac[MD5_DIGEST_LENGTH];
98 * Generate the hmac. We already have a dependency on openssl for
99 * DES, so we'll use it's hmac functionality also -- saves us from
100 * having to collect the data to be signed into one contiguous piece.
102 HMAC_Init(&hmac_ctx, key, sizeof(key), EVP_md5());
103 HMAC_Update(&hmac_ctx, challenge, clen);
104 HMAC_Update(&hmac_ctx, (unsigned char *) &flags, 4);
105 HMAC_Update(&hmac_ctx, (unsigned char *) &when, 4);
106 HMAC_Final(&hmac_ctx, hmac, NULL);
107 HMAC_cleanup(&hmac_ctx);
110 * Generate the state. Note that it is in ASCII. The challenge
111 * value doesn't have to be ASCII encoded, as it is already
112 * ASCII, but we do it anyway, for consistency.
114 state = rad_malloc(clen * 2 + /* challenge */
117 sizeof(hmac) * 2 + /* hmac */
120 /* Add the challenge. */
121 (void) otp_keyblock2keystring(p, challenge, clen, otp_hex_conversion);
123 /* Add the flags and time. */
124 (void) otp_keyblock2keystring(p, (unsigned char *) &flags, 4,
127 (void) otp_keyblock2keystring(p, (unsigned char *) &when, 4,
131 (void) otp_keyblock2keystring(p, hmac, 16, otp_hex_conversion);
134 * Expand state (already ASCII) into ASCII again (0x31 -> 0x3331).
135 * pairmake() forces us to do this (it will reduce it back to binary),
136 * and to include a leading "0x".
139 *rad_state = rad_malloc(2 + /* "0x" */
142 (void) sprintf(*rad_state, "0x");
144 (void) otp_keyblock2keystring(p, state, strlen(state), otp_hex_conversion);