f2181330444d8534e7c4f944e87b7c83cb39df5c
[freeradius.git] / src / modules / rlm_otp / otp_radstate.c
1 /*
2  * otp_radstate.c
3  * $Id$
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  * Copyright 2005,2006 TRI-D Systems, Inc.
21  */
22
23 #ifdef FREERADIUS
24 #define _LRAD_MD4_H
25 #define _LRAD_SHA1_H
26 #endif
27 #include "otp.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$";
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  * '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.
47  *
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.
54  *
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
61  * better protection.
62  *
63  * Our state, then, is
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.
70  *
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.
77  *
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.
80  *
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).
85  */
86 int
87 otp_gen_state(char **rad_state, unsigned char **raw_state,
88               const unsigned char challenge[OTP_MAX_CHALLENGE_LEN],
89               size_t clen,
90               int32_t flags, int32_t when, const unsigned char key[16])
91 {
92   HMAC_CTX hmac_ctx;
93   unsigned char hmac[MD5_DIGEST_LENGTH];
94   char *p;
95   char *state;
96
97   /*
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.
101    */
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);
108
109   /*
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.
113    */
114   state = rad_malloc(clen * 2 +                 /* challenge */
115                      8 +                        /* flags     */
116                      8 +                        /* time      */
117                      sizeof(hmac) * 2 +         /* hmac      */
118                      1);                        /* '\0'      */
119   p = state;
120   /* Add the challenge. */
121   (void) otp_keyblock2keystring(p, challenge, clen, otp_hex_conversion);
122   p += clen * 2;
123   /* Add the flags and time. */
124   (void) otp_keyblock2keystring(p, (unsigned char *) &flags, 4,
125                                 otp_hex_conversion);
126   p += 8;
127   (void) otp_keyblock2keystring(p, (unsigned char *) &when, 4,
128                                 otp_hex_conversion);
129   p += 8;
130   /* Add the hmac. */
131   (void) otp_keyblock2keystring(p, hmac, 16, otp_hex_conversion);
132
133   /*
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".
137    */
138   if (rad_state) {
139     *rad_state = rad_malloc(2 +                 /* "0x" */
140                             strlen(state) * 2 +
141                             1);                 /* '\0' */
142     (void) sprintf(*rad_state, "0x");
143     p = *rad_state + 2;
144     (void) otp_keyblock2keystring(p, state, strlen(state), otp_hex_conversion);
145   }
146
147   if (raw_state)
148     *raw_state = state;
149   else
150     free(state);
151
152   return 0;
153 }