remove 'now' arg from cardops maxtwin() method
[freeradius.git] / src / modules / rlm_otp / cardops / cryptocard.c
1 /*
2  * cryptocard.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 2005 TRI-D Systems, Inc.
20  */
21
22 #include <inttypes.h>
23 #include <string.h>
24 #include <time.h>
25
26 #include "../otp.h"
27 #include "../otp_cardops.h"
28 #include "cryptocard.h"
29
30 /* Card name to feature mask mappings */
31 static struct {
32   const char *name;
33   uint32_t fm;
34 } card[] = {
35   { "cryptocard-h8-rc", CRYPTOCARD_H8_RC },
36   { "cryptocard-d8-rc", CRYPTOCARD_D8_RC },
37   { "cryptocard-h7-rc", CRYPTOCARD_H7_RC },
38   { "cryptocard-d7-rc", CRYPTOCARD_D7_RC },
39   { "cryptocard-h8-es", CRYPTOCARD_H8_ES },
40   { "cryptocard-d8-es", CRYPTOCARD_D8_ES },
41   { "cryptocard-h7-es", CRYPTOCARD_H7_ES },
42   { "cryptocard-d7-es", CRYPTOCARD_D7_ES },
43   { "cryptocard-h8-rs", CRYPTOCARD_H8_RS },
44   { "cryptocard-d8-rs", CRYPTOCARD_D8_RS },
45   { "cryptocard-h7-rs", CRYPTOCARD_H7_RS },
46   { "cryptocard-d7-rs", CRYPTOCARD_D7_RS },
47
48   { NULL, 0 }                                   /* end of list */
49 };
50
51
52 /*
53  * Convert card name to feature mask.
54  * Returns 0 on success, non-zero otherwise.
55  */
56 static int
57 cryptocard_name2fm(const char *name, uint32_t *featuremask)
58 {
59   int i;
60
61   for (i = 0; card[i].name; ++i) {
62     if (!strcasecmp(name, card[i].name)) {
63       *featuremask = card[i].fm;
64       return 0;
65     }
66   }
67   return 1;
68 }
69
70
71 /*
72  * Convert an ASCII keystring to a keyblock.
73  * Returns 0 on success, non-zero otherwise.
74  */
75 static int
76 cryptocard_keystring2keyblock(const char *keystring,
77                               unsigned char keyblock[OTP_MAX_KEY_LEN])
78 {
79   /* 64-bit DES key with optional line ending */
80   if ((strlen(keystring) & ~1) != 16)
81     return 1;
82
83   return otp_keystring2keyblock(keystring, keyblock);
84 }
85
86
87 /*
88  * Set nullstate.
89  * We don't currently support nullstate for CRYPTOCard, so return -1.
90  */
91 static int
92 cryptocard_nullstate(
93 #ifdef __GNUC__
94 __attribute__ ((unused))
95 #endif
96                       const otp_option_t *opt,
97 #ifdef __GNUC__
98 __attribute__ ((unused))
99 #endif
100                       const otp_user_info_t *user_info,
101 #ifdef __GNUC__
102 __attribute__ ((unused))
103 #endif
104                       otp_user_state_t *user_state, 
105 #ifdef __GNUC__
106 __attribute__ ((unused))
107 #endif
108                       time_t when,
109                       const char *log_prefix)
110 {
111   otp_log(OTP_LOG_ERR, "%s: null state not supported for CRYPTOCard",
112           log_prefix);
113   return -1;
114 }
115
116
117 /*
118  * Return a synchronous challenge.
119  * Returns 0 on success, non-zero otherwise.
120  */
121 static int
122 cryptocard_challenge(const otp_user_info_t *user_info,
123 #ifdef __GNUC__
124 __attribute__ ((unused))
125 #endif
126                      const char csd[OTP_MAX_CSD_LEN + 1],
127                      char challenge[OTP_MAX_CHALLENGE_LEN + 1],
128 #ifdef __GNUC__
129 __attribute__ ((unused))
130 #endif
131                      time_t when,
132 #ifdef __GNUC__
133 __attribute__ ((unused))
134 #endif
135                      int twin,
136 #ifdef __GNUC__
137 __attribute__ ((unused))
138 #endif
139                      int ewin,
140                      const char *log_prefix)
141 {
142   unsigned char output[8];
143   int i;
144
145   /* CRYPTOCard sync challenges are always 8 bytes. */
146   if (strlen(challenge) != 8)
147     return -1;
148
149   /* run x99 once on the challenge */
150   if (otp_x99_mac(challenge, 8, output, user_info->keyblock, log_prefix))
151     return -1;
152
153   /* convert the mac into the next challenge */
154   for (i = 0; i < 8; ++i) {
155     output[i] &= 0x0f;
156     if (output[i] > 9)
157       output[i] -= 10;
158     output[i] |= 0x30;
159   }
160   (void) memcpy(challenge, output, 8);
161   challenge[8] = '\0';
162
163   return 0;
164 }
165
166
167 /*
168  * Return the expected card response for a given challenge.
169  * Returns 0 on success, non-zero otherwise.
170  *
171  * The X9.9 MAC is used by CRYPTOcard in the following manner:
172  *
173  * 1. Convert the challenge to ASCII (eg "12345" -> 0x3132333435).
174  *    We don't actually do a conversion, the challenge is already ASCII.
175  *    Note that Secure Computing SafeWord Gold/Platinum tokens can use
176  *    "raw" challenge bytes.
177  * 2. Use the challenge as the plaintext input to the X9.9 MAC algorithm.
178  *
179  * 3. Convert the 32 bit MAC to ASCII (eg 0x1234567f -> "1234567f").
180  *    Note that SafeWord Gold/Platinum tokens can display a 64 bit MAC.
181  * 4. Possibly apply transformations on chars "a" thru "f".
182  * 5. Truncate the response for 7 digit display modes.
183  */
184 static int
185 cryptocard_response(otp_user_info_t *user_info,
186 #ifdef __GNUC__
187 __attribute__ ((unused))
188 #endif
189                     const char csd[OTP_MAX_CSD_LEN + 1],
190                     const char challenge[OTP_MAX_CHALLENGE_LEN + 1],
191                     char response[OTP_MAX_RESPONSE_LEN + 1],
192                     const char *log_prefix)
193 {
194   unsigned char output[8];
195   char l_response[17];
196   const char *conversion;
197
198   /* Step 1, 2. */
199   if (otp_x99_mac(challenge, strlen(challenge), output,
200                   user_info->keyblock, log_prefix) !=0)
201     return 1;
202
203   /* Setup for step 4. */
204   if (user_info->featuremask & OTP_CF_DD)
205     conversion = otp_cc_dec_conversion;
206   else
207     conversion = otp_hex_conversion;
208
209   /* Step 3, 4. */
210   otp_keyblock2keystring(l_response, output, conversion);
211   (void) memcpy(response, l_response, 8);
212   response[8] = '\0';
213
214   /* Step 5. */
215   if (user_info->featuremask & OTP_CF_R7)
216     (void) memmove(&response[3], &response[4], 5);
217
218   return 0;
219 }
220
221
222 /*
223  * Update rd (there is no csd for cryptocard).
224  * Returns 0 if succesful, -1 otherwise.
225  */
226 static int
227 cryptocard_updatecsd(
228 #ifdef __GNUC__
229 __attribute__ ((unused))
230 #endif
231                      const otp_user_info_t *user_info,
232                      otp_user_state_t *user_state,
233 #ifdef __GNUC__
234 __attribute__ ((unused))
235 #endif
236                      const char challenge[OTP_MAX_CHALLENGE_LEN + 1],
237 #ifdef __GNUC__
238 __attribute__ ((unused))
239 #endif
240                      int twin,
241                      int ewin,
242 #ifdef __GNUC__
243 __attribute__ ((unused))
244 #endif
245                      time_t when,
246                      int auth_rc,
247 #ifdef __GNUC__
248 __attribute__ ((unused))
249 #endif
250                      const char *log_prefix)
251 {
252   if (auth_rc == OTP_RC_OK)
253     user_state->rd[0] = '\0';                           /* reset */
254   else
255     (void) sprintf(user_state->rd, "%" PRIx32,
256                    (int32_t) ewin);                     /* rwindow candidate */
257
258   return 0;
259 }
260
261
262 /* events can only go forward, so an auth can never be too early */
263 static int
264 cryptocard_isearly(
265 #ifdef __GNUC__
266 __attribute__ ((unused))
267 #endif
268                    const otp_user_state_t *user_state,
269 #ifdef __GNUC__
270 __attribute__ ((unused))
271 #endif
272                    const char challenge[OTP_MAX_CHALLENGE_LEN + 1],
273 #ifdef __GNUC__
274 __attribute__ ((unused))
275 #endif
276                    const char *log_prefix)
277 {
278   return 0;
279 }
280
281
282 /*
283  * Determine if a window position if consecutive relative to a saved
284  * (rwindow candidate) window position, for rwindow override.
285  * user_state contains the previous auth position, twin and ewin the current.
286  * Returns 1 on success (consecutive), 0 otherwise.
287  */
288 static int
289 cryptocard_isconsecutive(
290                          const otp_user_info_t *user_info,
291                          const otp_user_state_t *user_state,
292 #ifdef __GNUC__
293 __attribute__ ((unused))
294 #endif
295                          int twin,
296                          int ewin,
297                          const char *log_prefix)
298 {
299   int rcanewin;         /* saved rwindow candidate window position */
300
301   /* extract the saved rwindow candidate position */
302   if (sscanf(user_state->rd, "%" SCNx32, &rcanewin) != 1) {
303     otp_log(OTP_LOG_ERR, "%s: %s: invalid rwindow data for [%s]", log_prefix,
304             __func__, user_info->username);
305     return 0;
306   }
307
308   /* Is this the next passcode? */
309   if (ewin == rcanewin + 1)
310     return 1;   /* yes */
311   else
312     return 0;   /* no */
313 }
314
315
316 /* no twin so just return 0 */
317 static int
318 cryptocard_maxtwin(
319 #ifdef __GNUC__
320 __attribute__ ((unused))
321 #endif
322                     const otp_user_info_t *user_info,
323 #ifdef __GNUC__
324 __attribute__ ((unused))
325 #endif
326                     const char csd[OTP_MAX_CSD_LEN + 1])
327 {
328   return 0;
329 }
330
331
332 /* cardops instance */
333 static cardops_t cryptocard_cardops = {
334   .prefix               = "cryptocard",
335   .prefix_len           = 10, /* strlen("cryptocard") */
336
337   .name2fm              = cryptocard_name2fm,
338   .keystring2keyblock   = cryptocard_keystring2keyblock,
339   .nullstate            = cryptocard_nullstate,
340   .challenge            = cryptocard_challenge,
341   .response             = cryptocard_response,
342   .updatecsd            = cryptocard_updatecsd,
343   .isearly              = cryptocard_isearly,
344   .isconsecutive        = cryptocard_isconsecutive,
345   .maxtwin              = cryptocard_maxtwin,
346 };
347
348
349 /* constructor */
350 void
351 cryptocard_init(void)
352 {
353   if (otp_num_cardops == OTP_MAX_VENDORS) {
354     otp_log(OTP_LOG_ERR, "cryptocard_init: module limit exceeded");
355     return;
356   }
357
358   otp_cardops[otp_num_cardops++] = cryptocard_cardops;
359   otp_log(OTP_LOG_DEBUG, "cryptocard_init: loaded");
360 }