1*1b7fb171Smillert /* $OpenBSD: token.c,v 1.19 2015/10/05 17:31:17 millert Exp $ */
227b8cbb6Smillert
327b8cbb6Smillert /*-
4fafad76dSmillert * Copyright (c) 1995 Migration Associates Corp. All Rights Reserved
527b8cbb6Smillert *
627b8cbb6Smillert * Redistribution and use in source and binary forms, with or without
727b8cbb6Smillert * modification, are permitted provided that the following conditions
827b8cbb6Smillert * are met:
927b8cbb6Smillert * 1. Redistributions of source code must retain the above copyright
1027b8cbb6Smillert * notice, this list of conditions and the following disclaimer.
1127b8cbb6Smillert * 2. Redistributions in binary form must reproduce the above copyright
1227b8cbb6Smillert * notice, this list of conditions and the following disclaimer in the
1327b8cbb6Smillert * documentation and/or other materials provided with the distribution.
1427b8cbb6Smillert * 3. All advertising materials mentioning features or use of this software
1527b8cbb6Smillert * must display the following acknowledgement:
1627b8cbb6Smillert * This product includes software developed by Berkeley Software Design,
1727b8cbb6Smillert * Inc.
1827b8cbb6Smillert * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
1927b8cbb6Smillert * or promote products derived from this software without specific prior
2027b8cbb6Smillert * written permission.
2127b8cbb6Smillert *
2227b8cbb6Smillert * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
2327b8cbb6Smillert * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2427b8cbb6Smillert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2527b8cbb6Smillert * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
2627b8cbb6Smillert * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2727b8cbb6Smillert * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2827b8cbb6Smillert * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2927b8cbb6Smillert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3027b8cbb6Smillert * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3127b8cbb6Smillert * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3227b8cbb6Smillert * SUCH DAMAGE.
3327b8cbb6Smillert *
3427b8cbb6Smillert * BSDI $From: token.c,v 1.2 1996/08/28 22:07:55 prb Exp $
3527b8cbb6Smillert */
3627b8cbb6Smillert
3727b8cbb6Smillert /*
3827b8cbb6Smillert * DES functions for one-way encrypting Authentication Tokens.
3927b8cbb6Smillert * All knowledge of DES is confined to this file.
4027b8cbb6Smillert */
4127b8cbb6Smillert
4227b8cbb6Smillert #include <sys/types.h>
4327b8cbb6Smillert #include <sys/time.h>
4427b8cbb6Smillert #include <sys/resource.h>
4527b8cbb6Smillert
4627b8cbb6Smillert #include <ctype.h>
4727b8cbb6Smillert #include <stdio.h>
4827b8cbb6Smillert #include <syslog.h>
49d05a8106Sderaadt #include <limits.h>
5027b8cbb6Smillert #include <stdlib.h>
5127b8cbb6Smillert #include <string.h>
5227b8cbb6Smillert #include <unistd.h>
530456bce5Sjsg #include <openssl/des.h>
5427b8cbb6Smillert
5527b8cbb6Smillert #include "token.h"
5627b8cbb6Smillert #include "tokendb.h"
5727b8cbb6Smillert
5827b8cbb6Smillert /*
5927b8cbb6Smillert * Define a union of various types of arguments to DES functions.
6027b8cbb6Smillert * All native DES types are modulo 8 bytes in length. Cipher text
6127b8cbb6Smillert * needs a trailing null byte.
6227b8cbb6Smillert */
6327b8cbb6Smillert
6427b8cbb6Smillert typedef union {
650456bce5Sjsg DES_cblock cb;
6627b8cbb6Smillert char ct[9];
6764ff528aStedu uint32_t ul[2];
6827b8cbb6Smillert } TOKEN_CBlock;
6927b8cbb6Smillert
7027b8cbb6Smillert /*
7127b8cbb6Smillert * Static definition of random number challenge for token.
7227b8cbb6Smillert * Challenge length is 8 bytes, left-justified with trailing null byte.
7327b8cbb6Smillert */
7427b8cbb6Smillert
7527b8cbb6Smillert static TOKEN_CBlock tokennumber;
7627b8cbb6Smillert
7727b8cbb6Smillert /*
7827b8cbb6Smillert * Static function prototypes
7927b8cbb6Smillert */
8027b8cbb6Smillert
81b4975d76Smarkus static void tokenseed(TOKEN_CBlock *);
8227b8cbb6Smillert static void lcase(char *);
8327b8cbb6Smillert static void h2d(char *);
8427b8cbb6Smillert static void h2cb(char *, TOKEN_CBlock *);
8527b8cbb6Smillert static void cb2h(TOKEN_CBlock, char *);
8627b8cbb6Smillert
8727b8cbb6Smillert /*
8827b8cbb6Smillert * Generate random DES cipherblock seed. Feedback key into
8927b8cbb6Smillert * new_random_key to strengthen.
9027b8cbb6Smillert */
9127b8cbb6Smillert
9227b8cbb6Smillert static void
tokenseed(TOKEN_CBlock * cb)93b4975d76Smarkus tokenseed(TOKEN_CBlock *cb)
9427b8cbb6Smillert {
95b4975d76Smarkus cb->ul[0] = arc4random();
96b4975d76Smarkus cb->ul[1] = arc4random();
9727b8cbb6Smillert }
9827b8cbb6Smillert
9927b8cbb6Smillert /*
10027b8cbb6Smillert * Send a random challenge string to the token. The challenge
10127b8cbb6Smillert * is always base 10 as there are no alpha keys on the keyboard.
10227b8cbb6Smillert */
10327b8cbb6Smillert
10427b8cbb6Smillert void
tokenchallenge(char * user,char * challenge,int size,char * card_type)10527b8cbb6Smillert tokenchallenge(char *user, char *challenge, int size, char *card_type)
10627b8cbb6Smillert {
10727b8cbb6Smillert TOKENDB_Rec tr;
10827b8cbb6Smillert TOKEN_CBlock cb;
1090456bce5Sjsg DES_key_schedule ks;
11027b8cbb6Smillert int r, c;
11127b8cbb6Smillert
11227b8cbb6Smillert r = 1; /* no reduced input mode by default! */
11327b8cbb6Smillert
11427b8cbb6Smillert if ((tt->modes & TOKEN_RIM) &&
11527b8cbb6Smillert tokendb_getrec(user, &tr) == 0 &&
11627b8cbb6Smillert (tr.mode & TOKEN_RIM)) {
11727b8cbb6Smillert c = 0;
11827b8cbb6Smillert while ((r = tokendb_lockrec(user, &tr, TOKEN_LOCKED)) == 1) {
11927b8cbb6Smillert if (c++ >= 60)
12027b8cbb6Smillert break;
12127b8cbb6Smillert sleep(1);
12227b8cbb6Smillert }
12327b8cbb6Smillert tr.flags &= ~TOKEN_LOCKED;
12427b8cbb6Smillert if (r == 0 && tr.rim[0]) {
12527b8cbb6Smillert h2cb(tr.secret, &cb);
1260456bce5Sjsg DES_fixup_key_parity(&cb.cb);
1270456bce5Sjsg DES_key_sched(&cb.cb, &ks);
1280456bce5Sjsg DES_ecb_encrypt(&tr.rim, &cb.cb, &ks, DES_ENCRYPT);
12927b8cbb6Smillert memcpy(tr.rim, cb.cb, 8);
13027b8cbb6Smillert for (r = 0; r < 8; ++r) {
13127b8cbb6Smillert if ((tr.rim[r] &= 0xf) > 9)
13227b8cbb6Smillert tr.rim[r] -= 10;
13327b8cbb6Smillert tr.rim[r] |= 0x30;
13427b8cbb6Smillert }
13527b8cbb6Smillert r = 0; /* reset it back */
13627b8cbb6Smillert memcpy(tokennumber.ct, tr.rim, 8);
13727b8cbb6Smillert tokennumber.ct[8] = 0;
13827b8cbb6Smillert tokendb_putrec(user, &tr);
13927b8cbb6Smillert }
14027b8cbb6Smillert }
14127b8cbb6Smillert if (r != 0 || tr.rim[0] == '\0') {
142b4975d76Smarkus memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
14394f29c57Smillert snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",
14494f29c57Smillert arc4random());
14527b8cbb6Smillert if (r == 0) {
14627b8cbb6Smillert memcpy(tr.rim, tokennumber.ct, 8);
14727b8cbb6Smillert tokendb_putrec(user, &tr);
14827b8cbb6Smillert }
14927b8cbb6Smillert }
15027b8cbb6Smillert
15127b8cbb6Smillert snprintf(challenge, size, "%s Challenge \"%s\"\r\n%s Response: ",
15227b8cbb6Smillert card_type, tokennumber.ct, card_type);
15327b8cbb6Smillert }
15427b8cbb6Smillert
15527b8cbb6Smillert /*
15627b8cbb6Smillert * Verify response from user against token's predicted cipher
15727b8cbb6Smillert * of the random number challenge.
15827b8cbb6Smillert */
15927b8cbb6Smillert
16027b8cbb6Smillert int
tokenverify(char * username,char * challenge,char * response)16127b8cbb6Smillert tokenverify(char *username, char *challenge, char *response)
16227b8cbb6Smillert {
16327b8cbb6Smillert char *state;
16427b8cbb6Smillert TOKENDB_Rec tokenrec;
16527b8cbb6Smillert TOKEN_CBlock tmp;
16627b8cbb6Smillert TOKEN_CBlock cmp_text;
16727b8cbb6Smillert TOKEN_CBlock user_seed;
16827b8cbb6Smillert TOKEN_CBlock cipher_text;
1690456bce5Sjsg DES_key_schedule key_schedule;
17027b8cbb6Smillert
17127b8cbb6Smillert
172b4975d76Smarkus memset(cmp_text.ct, 0, sizeof(cmp_text.ct));
173b4975d76Smarkus memset(user_seed.ct, 0, sizeof(user_seed.ct));
174b4975d76Smarkus memset(cipher_text.ct, 0, sizeof(cipher_text.ct));
175b4975d76Smarkus memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
17627b8cbb6Smillert
177a1ac9dbdSfgsch (void)strtok(challenge, "\"");
17827b8cbb6Smillert state = strtok(NULL, "\"");
17927b8cbb6Smillert tmp.ul[0] = strtoul(state, NULL, 10);
18082ff3630Sguenther snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",tmp.ul[0]);
18127b8cbb6Smillert
18227b8cbb6Smillert /*
18327b8cbb6Smillert * Retrieve the db record for the user. Nuke it as soon as
18427b8cbb6Smillert * we have translated out the user's shared secret just in
18527b8cbb6Smillert * case we (somehow) get core dumped...
18627b8cbb6Smillert */
18727b8cbb6Smillert
18827b8cbb6Smillert if (tokendb_getrec(username, &tokenrec))
18927b8cbb6Smillert return (-1);
19027b8cbb6Smillert
19127b8cbb6Smillert h2cb(tokenrec.secret, &user_seed);
192*1b7fb171Smillert explicit_bzero(&tokenrec.secret, sizeof(tokenrec.secret));
19327b8cbb6Smillert
19427b8cbb6Smillert if (!(tokenrec.flags & TOKEN_ENABLED))
19527b8cbb6Smillert return (-1);
19627b8cbb6Smillert
19727b8cbb6Smillert /*
19827b8cbb6Smillert * Compute the anticipated response in hex. Nuke the user's
19927b8cbb6Smillert * shared secret asap.
20027b8cbb6Smillert */
20127b8cbb6Smillert
2020456bce5Sjsg DES_fixup_key_parity(&user_seed.cb);
2030456bce5Sjsg DES_key_sched(&user_seed.cb, &key_schedule);
204*1b7fb171Smillert explicit_bzero(user_seed.ct, sizeof(user_seed.ct));
2050456bce5Sjsg DES_ecb_encrypt(&tokennumber.cb, &cipher_text.cb, &key_schedule,
20627b8cbb6Smillert DES_ENCRYPT);
207*1b7fb171Smillert explicit_bzero(&key_schedule, sizeof(key_schedule));
20827b8cbb6Smillert
20927b8cbb6Smillert /*
21027b8cbb6Smillert * The token thinks it's descended from VAXen. Deal with i386
21127b8cbb6Smillert * endian-ness of binary cipher prior to generating ascii from first
21227b8cbb6Smillert * 32 bits.
21327b8cbb6Smillert */
21427b8cbb6Smillert
21527b8cbb6Smillert HTONL(cipher_text.ul[0]);
21682ff3630Sguenther snprintf(cmp_text.ct, sizeof(cmp_text.ct), "%8.8x", cipher_text.ul[0]);
21727b8cbb6Smillert
21827b8cbb6Smillert if (tokenrec.mode & TOKEN_PHONEMODE) {
21927b8cbb6Smillert /*
22027b8cbb6Smillert * If we are a CRYPTOCard, we need to see if we are in
22127b8cbb6Smillert * "telephone number mode". If so, transmogrify the fourth
22227b8cbb6Smillert * digit of the cipher. Lower case response just in case
22327b8cbb6Smillert * it's * hex. Compare hex cipher with anticipated response
22427b8cbb6Smillert * from token.
22527b8cbb6Smillert */
22627b8cbb6Smillert
22727b8cbb6Smillert lcase(response);
22827b8cbb6Smillert
22927b8cbb6Smillert if (response[3] == '-')
23027b8cbb6Smillert cmp_text.ct[3] = '-';
23127b8cbb6Smillert }
23227b8cbb6Smillert
23327b8cbb6Smillert if ((tokenrec.mode & TOKEN_HEXMODE) && !strcmp(response, cmp_text.ct))
23427b8cbb6Smillert return (0);
23527b8cbb6Smillert
23627b8cbb6Smillert /*
23727b8cbb6Smillert * No match against the computed hex cipher. The token could be
23827b8cbb6Smillert * in decimal mode. Pervert the string to magic decimal equivalent.
23927b8cbb6Smillert */
24027b8cbb6Smillert
24127b8cbb6Smillert h2d(cmp_text.ct);
24227b8cbb6Smillert
24327b8cbb6Smillert if ((tokenrec.mode & TOKEN_DECMODE) && !strcmp(response, cmp_text.ct))
24427b8cbb6Smillert return (0);
24527b8cbb6Smillert
24627b8cbb6Smillert return (-1);
24727b8cbb6Smillert }
24827b8cbb6Smillert
24927b8cbb6Smillert /*
25027b8cbb6Smillert * Initialize a new user record in the token database.
25127b8cbb6Smillert */
25227b8cbb6Smillert
25327b8cbb6Smillert int
tokenuserinit(int flags,char * username,unsigned char * usecret,unsigned mode)25427b8cbb6Smillert tokenuserinit(int flags, char *username, unsigned char *usecret, unsigned mode)
25527b8cbb6Smillert {
25627b8cbb6Smillert TOKENDB_Rec tokenrec;
25727b8cbb6Smillert TOKEN_CBlock secret;
25827b8cbb6Smillert TOKEN_CBlock nulls;
25927b8cbb6Smillert TOKEN_CBlock checksum;
26027b8cbb6Smillert TOKEN_CBlock checktxt;
2610456bce5Sjsg DES_key_schedule key_schedule;
26227b8cbb6Smillert
263a1ac9dbdSfgsch memset(&secret, 0, sizeof(secret));
264b4975d76Smarkus
26527b8cbb6Smillert /*
26627b8cbb6Smillert * If no user secret passed in, create one
26727b8cbb6Smillert */
26827b8cbb6Smillert
26927b8cbb6Smillert if ( (flags & TOKEN_GENSECRET) )
270b4975d76Smarkus tokenseed(&secret);
271b4975d76Smarkus else
2720456bce5Sjsg memcpy(&secret, usecret, sizeof(DES_cblock));
273b4975d76Smarkus
2740456bce5Sjsg DES_fixup_key_parity(&secret.cb);
27527b8cbb6Smillert
27627b8cbb6Smillert /*
277284c262eSjufi * Check if the db record already exists. If there's no
27827b8cbb6Smillert * force-init flag and it exists, go away. Else,
27927b8cbb6Smillert * create the user's db record and put to the db.
28027b8cbb6Smillert */
28127b8cbb6Smillert
28227b8cbb6Smillert
28327b8cbb6Smillert if (!(flags & TOKEN_FORCEINIT) &&
28427b8cbb6Smillert tokendb_getrec(username, &tokenrec) == 0)
28527b8cbb6Smillert return (1);
28627b8cbb6Smillert
28727b8cbb6Smillert memset(&tokenrec, 0, sizeof(tokenrec));
288a79f1fdfSderaadt strlcpy(tokenrec.uname, username, sizeof(tokenrec.uname));
28927b8cbb6Smillert cb2h(secret, tokenrec.secret);
29027b8cbb6Smillert tokenrec.mode = 0;
29127b8cbb6Smillert tokenrec.flags = TOKEN_ENABLED | TOKEN_USEMODES;
29227b8cbb6Smillert tokenrec.mode = mode;
293686fccfdSavsm memset(tokenrec.reserved_char1, 0, sizeof(tokenrec.reserved_char1));
294686fccfdSavsm memset(tokenrec.reserved_char2, 0, sizeof(tokenrec.reserved_char2));
29527b8cbb6Smillert
29627b8cbb6Smillert if (tokendb_putrec(username, &tokenrec))
29727b8cbb6Smillert return (-1);
29827b8cbb6Smillert
29927b8cbb6Smillert /*
30027b8cbb6Smillert * Check if the shared secret was generated here. If so, we
30127b8cbb6Smillert * need to inform the user about it in order that it can be
30227b8cbb6Smillert * programmed into the token. See tokenverify() (above) for
30327b8cbb6Smillert * discussion of cipher generation.
30427b8cbb6Smillert */
30527b8cbb6Smillert
306b4975d76Smarkus if (!(flags & TOKEN_GENSECRET)) {
307*1b7fb171Smillert explicit_bzero(&secret, sizeof(secret));
30827b8cbb6Smillert return (0);
309b4975d76Smarkus }
31027b8cbb6Smillert
31127b8cbb6Smillert printf("Shared secret for %s\'s token: "
31227b8cbb6Smillert "%03o %03o %03o %03o %03o %03o %03o %03o\n",
31327b8cbb6Smillert username, secret.cb[0], secret.cb[1], secret.cb[2], secret.cb[3],
31427b8cbb6Smillert secret.cb[4], secret.cb[5], secret.cb[6], secret.cb[7]);
31527b8cbb6Smillert
3160456bce5Sjsg DES_key_sched(&secret.cb, &key_schedule);
317*1b7fb171Smillert explicit_bzero(&secret, sizeof(secret));
31827b8cbb6Smillert memset(&nulls, 0, sizeof(nulls));
3190456bce5Sjsg DES_ecb_encrypt(&nulls.cb, &checksum.cb, &key_schedule, DES_ENCRYPT);
320*1b7fb171Smillert explicit_bzero(&key_schedule, sizeof(key_schedule));
32127b8cbb6Smillert HTONL(checksum.ul[0]);
32282ff3630Sguenther snprintf(checktxt.ct, sizeof(checktxt.ct), "%8.8x", checksum.ul[0]);
32327b8cbb6Smillert printf("Hex Checksum: \"%s\"", checktxt.ct);
32427b8cbb6Smillert
32527b8cbb6Smillert h2d(checktxt.ct);
32627b8cbb6Smillert printf("\tDecimal Checksum: \"%s\"\n", checktxt.ct);
32727b8cbb6Smillert
32827b8cbb6Smillert return (0);
32927b8cbb6Smillert }
33027b8cbb6Smillert
33127b8cbb6Smillert /*
33227b8cbb6Smillert * Magically transform a hex character string into a decimal character
33327b8cbb6Smillert * string as defined by the token card vendor. The string should have
33427b8cbb6Smillert * been lowercased by now.
33527b8cbb6Smillert */
33627b8cbb6Smillert
33727b8cbb6Smillert static void
h2d(char * cp)33827b8cbb6Smillert h2d(char *cp)
33927b8cbb6Smillert {
34027b8cbb6Smillert int i;
34127b8cbb6Smillert
3420456bce5Sjsg for (i=0; i<sizeof(DES_cblock); i++, cp++) {
34327b8cbb6Smillert if (*cp >= 'a' && *cp <= 'f')
34427b8cbb6Smillert *cp = tt->map[*cp - 'a'];
34527b8cbb6Smillert }
34627b8cbb6Smillert }
34727b8cbb6Smillert
34827b8cbb6Smillert /*
34927b8cbb6Smillert * Translate an hex 16 byte ascii representation of an unsigned
3500456bce5Sjsg * integer to a DES_cblock.
35127b8cbb6Smillert */
35227b8cbb6Smillert
35327b8cbb6Smillert static void
h2cb(char * hp,TOKEN_CBlock * cb)35427b8cbb6Smillert h2cb(char *hp, TOKEN_CBlock *cb)
35527b8cbb6Smillert {
35627b8cbb6Smillert char scratch[9];
35727b8cbb6Smillert
35827b8cbb6Smillert strlcpy(scratch, hp, sizeof(scratch));
35927b8cbb6Smillert cb->ul[0] = strtoul(scratch, NULL, 16);
36027b8cbb6Smillert
36127b8cbb6Smillert strlcpy(scratch, hp + 8, sizeof(scratch));
36227b8cbb6Smillert cb->ul[1] = strtoul(scratch, NULL, 16);
36327b8cbb6Smillert }
36427b8cbb6Smillert
36527b8cbb6Smillert /*
3660456bce5Sjsg * Translate a DES_cblock to an 16 byte ascii hex representation.
36727b8cbb6Smillert */
36827b8cbb6Smillert
36927b8cbb6Smillert static void
cb2h(TOKEN_CBlock cb,char * hp)37027b8cbb6Smillert cb2h(TOKEN_CBlock cb, char* hp)
37127b8cbb6Smillert {
37227b8cbb6Smillert char scratch[17];
37327b8cbb6Smillert
37482ff3630Sguenther snprintf(scratch, 9, "%8.8x", cb.ul[0]);
37582ff3630Sguenther snprintf(scratch+8, 9, "%8.8x", cb.ul[1]);
37627b8cbb6Smillert memcpy(hp, scratch, 16);
37727b8cbb6Smillert }
37827b8cbb6Smillert
37927b8cbb6Smillert /*
38027b8cbb6Smillert * Lowercase possible hex response
38127b8cbb6Smillert */
38227b8cbb6Smillert
38327b8cbb6Smillert static void
lcase(char * cp)38427b8cbb6Smillert lcase(char *cp)
38527b8cbb6Smillert {
38627b8cbb6Smillert while (*cp) {
3874207a9b6Sderaadt if (isupper((unsigned char)*cp))
3884207a9b6Sderaadt *cp = tolower((unsigned char)*cp);
38927b8cbb6Smillert cp++;
39027b8cbb6Smillert }
39127b8cbb6Smillert }
392