pass csd to cardops response() method and update on successful auth
[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 <string.h>
23
24 #include "../otp.h"
25 #include "../otp_cardops.h"
26 #include "cryptocard.h"
27
28 /* Card name to feature mask mappings */
29 static struct {
30   const char *name;
31   uint32_t fm;
32 } card[] = {
33   { "cryptocard-h8-rc", CRYPTOCARD_H8_RC },
34   { "cryptocard-d8-rc", CRYPTOCARD_D8_RC },
35   { "cryptocard-h7-rc", CRYPTOCARD_H7_RC },
36   { "cryptocard-d7-rc", CRYPTOCARD_D7_RC },
37   { "cryptocard-h8-es", CRYPTOCARD_H8_ES },
38   { "cryptocard-d8-es", CRYPTOCARD_D8_ES },
39   { "cryptocard-h7-es", CRYPTOCARD_H7_ES },
40   { "cryptocard-d7-es", CRYPTOCARD_D7_ES },
41   { "cryptocard-h8-rs", CRYPTOCARD_H8_RS },
42   { "cryptocard-d8-rs", CRYPTOCARD_D8_RS },
43   { "cryptocard-h7-rs", CRYPTOCARD_H7_RS },
44   { "cryptocard-d7-rs", CRYPTOCARD_D7_RS },
45
46   { NULL, 0 }                                   /* end of list */
47 };
48
49
50 /*
51  * Convert card name to feature mask.
52  * Returns 0 on success, non-zero otherwise.
53  */
54 static int
55 cryptocard_name2fm(const char *name, uint32_t *featuremask)
56 {
57   int i;
58
59   for (i = 0; card[i].name; ++i) {
60     if (!strcasecmp(name, card[i].name)) {
61       *featuremask = card[i].fm;
62       return 0;
63     }
64   }
65   return 1;
66 }
67
68
69 /*
70  * Convert an ASCII keystring to a keyblock.
71  * Returns 0 on success, non-zero otherwise.
72  */
73 static int
74 cryptocard_keystring2keyblock(const char *keystring, unsigned char keyblock[])
75 {
76   /* 64-bit DES key with optional line ending */
77   if ((strlen(keystring) & ~1) != 16)
78     return 1;
79
80   return otp_keystring2keyblock(keystring, keyblock);
81 }
82
83
84 /*
85  * Set nullstate.
86  * We don't currently support nullstate for CRYPTOCard, so return -1.
87  */
88 static int
89 cryptocard_nullstate(
90 #ifdef __GNUC__
91 __attribute__ ((unused))
92 #endif
93                       const otp_user_info_t *user_info,
94 #ifdef __GNUC__
95 __attribute__ ((unused))
96 #endif
97                       otp_user_state_t *user_state, 
98                       const char *log_prefix)
99 {
100   otp_log(OTP_LOG_ERR, "%s: null state not supported for CRYPTOCard",
101           log_prefix);
102   return -1;
103 }
104
105
106 /*
107  * Return a synchronous challenge.
108  * Returns 0 on success, non-zero otherwise.
109  */
110 static int
111 cryptocard_challenge(const otp_user_info_t *user_info, unsigned int ewin,
112 #ifdef __GNUC__
113 __attribute__ ((unused))
114 #endif
115                      int twin,
116                      char challenge[OTP_MAX_CHALLENGE_LEN + 1],
117                      const char *log_prefix)
118 {
119   unsigned char output[8];
120   int i, rc = 0;
121
122   /* CRYPTOCard sync challenges are always 8 bytes. */
123   if (strlen(challenge) != 8)
124     return -1;
125
126   /* iterate ewin times on the challenge */
127   while (ewin--) {
128     if ((rc = otp_x99_mac(challenge, 8, output, user_info->keyblock,
129                           log_prefix)) == 0) {
130       /* convert the mac into the next challenge */
131       for (i = 0; i < 8; ++i) {
132         output[i] &= 0x0f;
133         if (output[i] > 9)
134           output[i] -= 10;
135         output[i] |= 0x30;
136       }
137       (void) memcpy(challenge, output, 8);
138       challenge[8] = '\0';
139     } else {
140       /* something went wrong */
141       break;
142     }
143   }
144
145   return rc;
146 }
147
148
149 /*
150  * Return the expected card response for a given challenge.
151  * Returns 0 on success, non-zero otherwise.
152  *
153  * The X9.9 MAC is used by CRYPTOcard in the following manner:
154  *
155  * 1. Convert the challenge to ASCII (eg "12345" -> 0x3132333435).
156  *    We don't actually do a conversion, the challenge is already ASCII.
157  *    Note that Secure Computing SafeWord Gold/Platinum tokens can use
158  *    "raw" challenge bytes.
159  * 2. Use the challenge as the plaintext input to the X9.9 MAC algorithm.
160  *
161  * 3. Convert the 32 bit MAC to ASCII (eg 0x1234567f -> "1234567f").
162  *    Note that SafeWord Gold/Platinum tokens can display a 64 bit MAC.
163  * 4. Possibly apply transformations on chars "a" thru "f".
164  * 5. Truncate the response for 7 digit display modes.
165  */
166 static int
167 cryptocard_response(otp_user_info_t *user_info,
168 #ifdef __GNUC__
169 __attribute__ ((unused))
170 #endif
171                     char *csd,
172                     const char *challenge,
173                     char response[OTP_MAX_RESPONSE_LEN + 1],
174                     const char *log_prefix)
175 {
176   unsigned char output[8];
177   char l_response[17];
178   const char *conversion;
179
180   /* Step 1, 2. */
181   if (otp_x99_mac(challenge, strlen(challenge), output,
182                   user_info->keyblock, log_prefix) !=0)
183     return 1;
184
185   /* Setup for step 4. */
186   if (user_info->featuremask & OTP_CF_DD)
187     conversion = otp_cc_dec_conversion;
188   else
189     conversion = otp_hex_conversion;
190
191   /* Step 3, 4. */
192   otp_keyblock2keystring(l_response, output, conversion);
193   (void) memcpy(response, l_response, 8);
194   response[8] = '\0';
195
196   /* Step 5. */
197   if (user_info->featuremask & OTP_CF_R7)
198     (void) memmove(&response[3], &response[4], 5);
199
200   return 0;
201 }
202
203
204 /* cardops instance */
205 static cardops_t cryptocard_cardops = {
206   .prefix               = "cryptocard",
207   .prefix_len           = 10, /* strlen("cryptocard") */
208
209   .name2fm              = cryptocard_name2fm,
210   .keystring2keyblock   = cryptocard_keystring2keyblock,
211   .nullstate            = cryptocard_nullstate,
212   .challenge            = cryptocard_challenge,
213   .response             = cryptocard_response
214 };
215
216 /* constructor */
217 void
218 cryptocard_init(void)
219 {
220   if (otp_num_cardops == OTP_MAX_VENDORS) {
221     otp_log(OTP_LOG_ERR, "cryptocard_init: module limit exceeded");
222     return;
223   }
224
225   otp_cardops[otp_num_cardops++] = cryptocard_cardops;
226   otp_log(OTP_LOG_DEBUG, "cryptocard_init: loaded");
227 }