1 /* $OpenBSD: token.c,v 1.19 2015/10/05 17:31:17 millert Exp $ */
2
3 /*-
4 * Copyright (c) 1995 Migration Associates Corp. All Rights Reserved
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Berkeley Software Design,
17 * Inc.
18 * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
19 * or promote products derived from this software without specific prior
20 * written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * BSDI $From: token.c,v 1.2 1996/08/28 22:07:55 prb Exp $
35 */
36
37 /*
38 * DES functions for one-way encrypting Authentication Tokens.
39 * All knowledge of DES is confined to this file.
40 */
41
42 #include <sys/types.h>
43 #include <sys/time.h>
44 #include <sys/resource.h>
45
46 #include <ctype.h>
47 #include <stdio.h>
48 #include <syslog.h>
49 #include <limits.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <openssl/des.h>
54
55 #include "token.h"
56 #include "tokendb.h"
57
58 /*
59 * Define a union of various types of arguments to DES functions.
60 * All native DES types are modulo 8 bytes in length. Cipher text
61 * needs a trailing null byte.
62 */
63
64 typedef union {
65 DES_cblock cb;
66 char ct[9];
67 uint32_t ul[2];
68 } TOKEN_CBlock;
69
70 /*
71 * Static definition of random number challenge for token.
72 * Challenge length is 8 bytes, left-justified with trailing null byte.
73 */
74
75 static TOKEN_CBlock tokennumber;
76
77 /*
78 * Static function prototypes
79 */
80
81 static void tokenseed(TOKEN_CBlock *);
82 static void lcase(char *);
83 static void h2d(char *);
84 static void h2cb(char *, TOKEN_CBlock *);
85 static void cb2h(TOKEN_CBlock, char *);
86
87 /*
88 * Generate random DES cipherblock seed. Feedback key into
89 * new_random_key to strengthen.
90 */
91
92 static void
tokenseed(TOKEN_CBlock * cb)93 tokenseed(TOKEN_CBlock *cb)
94 {
95 cb->ul[0] = arc4random();
96 cb->ul[1] = arc4random();
97 }
98
99 /*
100 * Send a random challenge string to the token. The challenge
101 * is always base 10 as there are no alpha keys on the keyboard.
102 */
103
104 void
tokenchallenge(char * user,char * challenge,int size,char * card_type)105 tokenchallenge(char *user, char *challenge, int size, char *card_type)
106 {
107 TOKENDB_Rec tr;
108 TOKEN_CBlock cb;
109 DES_key_schedule ks;
110 int r, c;
111
112 r = 1; /* no reduced input mode by default! */
113
114 if ((tt->modes & TOKEN_RIM) &&
115 tokendb_getrec(user, &tr) == 0 &&
116 (tr.mode & TOKEN_RIM)) {
117 c = 0;
118 while ((r = tokendb_lockrec(user, &tr, TOKEN_LOCKED)) == 1) {
119 if (c++ >= 60)
120 break;
121 sleep(1);
122 }
123 tr.flags &= ~TOKEN_LOCKED;
124 if (r == 0 && tr.rim[0]) {
125 h2cb(tr.secret, &cb);
126 DES_fixup_key_parity(&cb.cb);
127 DES_key_sched(&cb.cb, &ks);
128 DES_ecb_encrypt(&tr.rim, &cb.cb, &ks, DES_ENCRYPT);
129 memcpy(tr.rim, cb.cb, 8);
130 for (r = 0; r < 8; ++r) {
131 if ((tr.rim[r] &= 0xf) > 9)
132 tr.rim[r] -= 10;
133 tr.rim[r] |= 0x30;
134 }
135 r = 0; /* reset it back */
136 memcpy(tokennumber.ct, tr.rim, 8);
137 tokennumber.ct[8] = 0;
138 tokendb_putrec(user, &tr);
139 }
140 }
141 if (r != 0 || tr.rim[0] == '\0') {
142 memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
143 snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",
144 arc4random());
145 if (r == 0) {
146 memcpy(tr.rim, tokennumber.ct, 8);
147 tokendb_putrec(user, &tr);
148 }
149 }
150
151 snprintf(challenge, size, "%s Challenge \"%s\"\r\n%s Response: ",
152 card_type, tokennumber.ct, card_type);
153 }
154
155 /*
156 * Verify response from user against token's predicted cipher
157 * of the random number challenge.
158 */
159
160 int
tokenverify(char * username,char * challenge,char * response)161 tokenverify(char *username, char *challenge, char *response)
162 {
163 char *state;
164 TOKENDB_Rec tokenrec;
165 TOKEN_CBlock tmp;
166 TOKEN_CBlock cmp_text;
167 TOKEN_CBlock user_seed;
168 TOKEN_CBlock cipher_text;
169 DES_key_schedule key_schedule;
170
171
172 memset(cmp_text.ct, 0, sizeof(cmp_text.ct));
173 memset(user_seed.ct, 0, sizeof(user_seed.ct));
174 memset(cipher_text.ct, 0, sizeof(cipher_text.ct));
175 memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
176
177 (void)strtok(challenge, "\"");
178 state = strtok(NULL, "\"");
179 tmp.ul[0] = strtoul(state, NULL, 10);
180 snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",tmp.ul[0]);
181
182 /*
183 * Retrieve the db record for the user. Nuke it as soon as
184 * we have translated out the user's shared secret just in
185 * case we (somehow) get core dumped...
186 */
187
188 if (tokendb_getrec(username, &tokenrec))
189 return (-1);
190
191 h2cb(tokenrec.secret, &user_seed);
192 explicit_bzero(&tokenrec.secret, sizeof(tokenrec.secret));
193
194 if (!(tokenrec.flags & TOKEN_ENABLED))
195 return (-1);
196
197 /*
198 * Compute the anticipated response in hex. Nuke the user's
199 * shared secret asap.
200 */
201
202 DES_fixup_key_parity(&user_seed.cb);
203 DES_key_sched(&user_seed.cb, &key_schedule);
204 explicit_bzero(user_seed.ct, sizeof(user_seed.ct));
205 DES_ecb_encrypt(&tokennumber.cb, &cipher_text.cb, &key_schedule,
206 DES_ENCRYPT);
207 explicit_bzero(&key_schedule, sizeof(key_schedule));
208
209 /*
210 * The token thinks it's descended from VAXen. Deal with i386
211 * endian-ness of binary cipher prior to generating ascii from first
212 * 32 bits.
213 */
214
215 HTONL(cipher_text.ul[0]);
216 snprintf(cmp_text.ct, sizeof(cmp_text.ct), "%8.8x", cipher_text.ul[0]);
217
218 if (tokenrec.mode & TOKEN_PHONEMODE) {
219 /*
220 * If we are a CRYPTOCard, we need to see if we are in
221 * "telephone number mode". If so, transmogrify the fourth
222 * digit of the cipher. Lower case response just in case
223 * it's * hex. Compare hex cipher with anticipated response
224 * from token.
225 */
226
227 lcase(response);
228
229 if (response[3] == '-')
230 cmp_text.ct[3] = '-';
231 }
232
233 if ((tokenrec.mode & TOKEN_HEXMODE) && !strcmp(response, cmp_text.ct))
234 return (0);
235
236 /*
237 * No match against the computed hex cipher. The token could be
238 * in decimal mode. Pervert the string to magic decimal equivalent.
239 */
240
241 h2d(cmp_text.ct);
242
243 if ((tokenrec.mode & TOKEN_DECMODE) && !strcmp(response, cmp_text.ct))
244 return (0);
245
246 return (-1);
247 }
248
249 /*
250 * Initialize a new user record in the token database.
251 */
252
253 int
tokenuserinit(int flags,char * username,unsigned char * usecret,unsigned mode)254 tokenuserinit(int flags, char *username, unsigned char *usecret, unsigned mode)
255 {
256 TOKENDB_Rec tokenrec;
257 TOKEN_CBlock secret;
258 TOKEN_CBlock nulls;
259 TOKEN_CBlock checksum;
260 TOKEN_CBlock checktxt;
261 DES_key_schedule key_schedule;
262
263 memset(&secret, 0, sizeof(secret));
264
265 /*
266 * If no user secret passed in, create one
267 */
268
269 if ( (flags & TOKEN_GENSECRET) )
270 tokenseed(&secret);
271 else
272 memcpy(&secret, usecret, sizeof(DES_cblock));
273
274 DES_fixup_key_parity(&secret.cb);
275
276 /*
277 * Check if the db record already exists. If there's no
278 * force-init flag and it exists, go away. Else,
279 * create the user's db record and put to the db.
280 */
281
282
283 if (!(flags & TOKEN_FORCEINIT) &&
284 tokendb_getrec(username, &tokenrec) == 0)
285 return (1);
286
287 memset(&tokenrec, 0, sizeof(tokenrec));
288 strlcpy(tokenrec.uname, username, sizeof(tokenrec.uname));
289 cb2h(secret, tokenrec.secret);
290 tokenrec.mode = 0;
291 tokenrec.flags = TOKEN_ENABLED | TOKEN_USEMODES;
292 tokenrec.mode = mode;
293 memset(tokenrec.reserved_char1, 0, sizeof(tokenrec.reserved_char1));
294 memset(tokenrec.reserved_char2, 0, sizeof(tokenrec.reserved_char2));
295
296 if (tokendb_putrec(username, &tokenrec))
297 return (-1);
298
299 /*
300 * Check if the shared secret was generated here. If so, we
301 * need to inform the user about it in order that it can be
302 * programmed into the token. See tokenverify() (above) for
303 * discussion of cipher generation.
304 */
305
306 if (!(flags & TOKEN_GENSECRET)) {
307 explicit_bzero(&secret, sizeof(secret));
308 return (0);
309 }
310
311 printf("Shared secret for %s\'s token: "
312 "%03o %03o %03o %03o %03o %03o %03o %03o\n",
313 username, secret.cb[0], secret.cb[1], secret.cb[2], secret.cb[3],
314 secret.cb[4], secret.cb[5], secret.cb[6], secret.cb[7]);
315
316 DES_key_sched(&secret.cb, &key_schedule);
317 explicit_bzero(&secret, sizeof(secret));
318 memset(&nulls, 0, sizeof(nulls));
319 DES_ecb_encrypt(&nulls.cb, &checksum.cb, &key_schedule, DES_ENCRYPT);
320 explicit_bzero(&key_schedule, sizeof(key_schedule));
321 HTONL(checksum.ul[0]);
322 snprintf(checktxt.ct, sizeof(checktxt.ct), "%8.8x", checksum.ul[0]);
323 printf("Hex Checksum: \"%s\"", checktxt.ct);
324
325 h2d(checktxt.ct);
326 printf("\tDecimal Checksum: \"%s\"\n", checktxt.ct);
327
328 return (0);
329 }
330
331 /*
332 * Magically transform a hex character string into a decimal character
333 * string as defined by the token card vendor. The string should have
334 * been lowercased by now.
335 */
336
337 static void
h2d(char * cp)338 h2d(char *cp)
339 {
340 int i;
341
342 for (i=0; i<sizeof(DES_cblock); i++, cp++) {
343 if (*cp >= 'a' && *cp <= 'f')
344 *cp = tt->map[*cp - 'a'];
345 }
346 }
347
348 /*
349 * Translate an hex 16 byte ascii representation of an unsigned
350 * integer to a DES_cblock.
351 */
352
353 static void
h2cb(char * hp,TOKEN_CBlock * cb)354 h2cb(char *hp, TOKEN_CBlock *cb)
355 {
356 char scratch[9];
357
358 strlcpy(scratch, hp, sizeof(scratch));
359 cb->ul[0] = strtoul(scratch, NULL, 16);
360
361 strlcpy(scratch, hp + 8, sizeof(scratch));
362 cb->ul[1] = strtoul(scratch, NULL, 16);
363 }
364
365 /*
366 * Translate a DES_cblock to an 16 byte ascii hex representation.
367 */
368
369 static void
cb2h(TOKEN_CBlock cb,char * hp)370 cb2h(TOKEN_CBlock cb, char* hp)
371 {
372 char scratch[17];
373
374 snprintf(scratch, 9, "%8.8x", cb.ul[0]);
375 snprintf(scratch+8, 9, "%8.8x", cb.ul[1]);
376 memcpy(hp, scratch, 16);
377 }
378
379 /*
380 * Lowercase possible hex response
381 */
382
383 static void
lcase(char * cp)384 lcase(char *cp)
385 {
386 while (*cp) {
387 if (isupper((unsigned char)*cp))
388 *cp = tolower((unsigned char)*cp);
389 cp++;
390 }
391 }
392