1 /* $OpenBSD: token.c,v 1.10 2005/05/26 23:04:09 avsm 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/param.h> 44 #include <sys/time.h> 45 #include <sys/resource.h> 46 47 #include <ctype.h> 48 #include <stdio.h> 49 #include <syslog.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <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 unsigned long 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 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 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 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 state = strtok(challenge, "\""); 178 state = strtok(NULL, "\""); 179 tmp.ul[0] = strtoul(state, NULL, 10); 180 snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8lu",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 memset(&tokenrec.secret, 0, 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 memset(user_seed.ct, 0, sizeof(user_seed.ct)); 205 des_ecb_encrypt(&tokennumber.cb, &cipher_text.cb, key_schedule, 206 DES_ENCRYPT); 207 memset(key_schedule, 0, 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.8lx", 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 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.ct, 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 memset(&secret.ct, 0, 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 memset(&secret.ct, 0, sizeof(secret)); 318 memset(&nulls, 0, sizeof(nulls)); 319 des_ecb_encrypt(&nulls.cb, &checksum.cb, key_schedule, DES_ENCRYPT); 320 memset(key_schedule, 0, sizeof(key_schedule)); 321 HTONL(checksum.ul[0]); 322 snprintf(checktxt.ct, sizeof(checktxt.ct), "%8.8lx", 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 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 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 370 cb2h(TOKEN_CBlock cb, char* hp) 371 { 372 char scratch[17]; 373 374 snprintf(scratch, 9, "%8.8lx", cb.ul[0]); 375 snprintf(scratch+8, 9, "%8.8lx", cb.ul[1]); 376 memcpy(hp, scratch, 16); 377 } 378 379 /* 380 * Lowercase possible hex response 381 */ 382 383 static void 384 lcase(char *cp) 385 { 386 while (*cp) { 387 if (isupper(*cp)) 388 *cp = tolower(*cp); 389 cp++; 390 } 391 } 392