Edit

IABSD.fr/src/libexec/login_token/token.c

Branch :

  • Show log

    Commit

  • Author : millert
    Date : 2015-10-05 17:31:17
    Hash : 1b7fb171
    Message : Use explicit_bzero() instead of memset() for zeroing out secrets. OK deraadt@

  • libexec/login_token/token.c
  • /*	$OpenBSD: token.c,v 1.19 2015/10/05 17:31:17 millert Exp $	*/
    
    /*-
     * Copyright (c) 1995 Migration Associates Corp. All Rights Reserved
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. All advertising materials mentioning features or use of this software
     *    must display the following acknowledgement:
     *      This product includes software developed by Berkeley Software Design,
     *      Inc.
     * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
     *    or promote products derived from this software without specific prior
     *    written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     *
     *	BSDI $From: token.c,v 1.2 1996/08/28 22:07:55 prb Exp $
     */
    
    /*
     * DES functions for one-way encrypting Authentication Tokens.
     * All knowledge of DES is confined to this file.
     */
    
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    
    #include <ctype.h>
    #include <stdio.h>
    #include <syslog.h>
    #include <limits.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <openssl/des.h>
    
    #include "token.h"
    #include "tokendb.h"
    
    /*
     * Define a union of various types of arguments to DES functions.
     * All native DES types are modulo 8 bytes in length. Cipher text
     * needs a trailing null byte.
     */
    
    typedef	union {
    	DES_cblock	cb;
    	char		ct[9];
    	uint32_t	ul[2];
    } TOKEN_CBlock;
    
    /*
     * Static definition of random number challenge for token.
     * Challenge length is 8 bytes, left-justified with trailing null byte.
     */
    
    static	TOKEN_CBlock tokennumber;
    
    /*
     * Static function prototypes
     */
    
    static	void	tokenseed(TOKEN_CBlock *);
    static	void	lcase(char *);
    static	void	h2d(char *);
    static	void	h2cb(char *, TOKEN_CBlock *);
    static	void	cb2h(TOKEN_CBlock, char *);
    
    /*
     * Generate random DES cipherblock seed. Feedback key into
     * new_random_key to strengthen.
     */
    
    static void
    tokenseed(TOKEN_CBlock *cb)
    {
    	cb->ul[0] = arc4random();
    	cb->ul[1] = arc4random();
    }
    
    /*
     * Send a random challenge string to the token. The challenge
     * is always base 10 as there are no alpha keys on the keyboard.
     */
    
    void
    tokenchallenge(char *user, char *challenge, int size, char *card_type)
    {
    	TOKENDB_Rec tr;
    	TOKEN_CBlock cb;
    	DES_key_schedule ks;
    	int r, c;
    
    	r = 1;	/* no reduced input mode by default! */
    
    	if ((tt->modes & TOKEN_RIM) &&
    	    tokendb_getrec(user, &tr) == 0 &&
    	    (tr.mode & TOKEN_RIM)) {
    		c = 0;
    		while ((r = tokendb_lockrec(user, &tr, TOKEN_LOCKED)) == 1) {
    			if (c++ >= 60)
    				break;
    			sleep(1);
    		}
    		tr.flags &= ~TOKEN_LOCKED;
    		if (r == 0 && tr.rim[0]) {
    			h2cb(tr.secret, &cb);
    			DES_fixup_key_parity(&cb.cb);
    			DES_key_sched(&cb.cb, &ks);
    			DES_ecb_encrypt(&tr.rim, &cb.cb, &ks, DES_ENCRYPT);
    			memcpy(tr.rim, cb.cb, 8);
    			for (r = 0; r < 8; ++r) {
    				if ((tr.rim[r] &= 0xf) > 9)
    					tr.rim[r] -= 10;
    				tr.rim[r] |= 0x30;
    			}
    			r = 0;		/* reset it back */
    			memcpy(tokennumber.ct, tr.rim, 8);
    			tokennumber.ct[8] = 0;
    			tokendb_putrec(user, &tr);
    		}
    	}
    	if (r != 0 || tr.rim[0] == '\0') {
    		memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
    		snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",
    		    arc4random());
    		if (r == 0) {
    			memcpy(tr.rim, tokennumber.ct, 8);
    			tokendb_putrec(user, &tr);
    		}
    	}
    
    	snprintf(challenge, size, "%s Challenge \"%s\"\r\n%s Response: ",
    	    card_type, tokennumber.ct, card_type);
    }
    
    /*
     * Verify response from user against token's predicted cipher
     * of the random number challenge.
     */
    
    int
    tokenverify(char *username, char *challenge, char *response)
    {
    	char	*state;
    	TOKENDB_Rec tokenrec;
    	TOKEN_CBlock tmp;
    	TOKEN_CBlock cmp_text;
    	TOKEN_CBlock user_seed;
    	TOKEN_CBlock cipher_text;
    	DES_key_schedule key_schedule;
    
    
    	memset(cmp_text.ct, 0, sizeof(cmp_text.ct));
    	memset(user_seed.ct, 0, sizeof(user_seed.ct));
    	memset(cipher_text.ct, 0, sizeof(cipher_text.ct));
    	memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
    
    	(void)strtok(challenge, "\"");
    	state = strtok(NULL, "\"");
    	tmp.ul[0] = strtoul(state, NULL, 10);
    	snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",tmp.ul[0]);
    
    	/*
    	 * Retrieve the db record for the user. Nuke it as soon as
    	 * we have translated out the user's shared secret just in
    	 * case we (somehow) get core dumped...
    	 */
    
    	if (tokendb_getrec(username, &tokenrec))
    		return (-1);
    
    	h2cb(tokenrec.secret, &user_seed);
    	explicit_bzero(&tokenrec.secret, sizeof(tokenrec.secret));
    
    	if (!(tokenrec.flags & TOKEN_ENABLED))
    		return (-1);
    
    	/*
    	 * Compute the anticipated response in hex. Nuke the user's
    	 * shared secret asap.
    	 */
    
    	DES_fixup_key_parity(&user_seed.cb);
    	DES_key_sched(&user_seed.cb, &key_schedule);
    	explicit_bzero(user_seed.ct, sizeof(user_seed.ct));
    	DES_ecb_encrypt(&tokennumber.cb, &cipher_text.cb, &key_schedule,
    	    DES_ENCRYPT);
    	explicit_bzero(&key_schedule, sizeof(key_schedule));
    
    	/*
    	 * The token thinks it's descended from VAXen.  Deal with i386
    	 * endian-ness of binary cipher prior to generating ascii from first
    	 * 32 bits.
    	 */
    
    	HTONL(cipher_text.ul[0]);
    	snprintf(cmp_text.ct, sizeof(cmp_text.ct), "%8.8x", cipher_text.ul[0]);
    
    	if (tokenrec.mode & TOKEN_PHONEMODE) {
    		/*
    		 * If we are a CRYPTOCard, we need to see if we are in
    		 * "telephone number mode".  If so, transmogrify the fourth
    		 * digit of the cipher.  Lower case response just in case
    		 * it's * hex.  Compare hex cipher with anticipated response
    		 * from token.
    		 */
    
    		lcase(response);
    
    		if (response[3] == '-')
    			cmp_text.ct[3] = '-';
    	}
    
    	if ((tokenrec.mode & TOKEN_HEXMODE) && !strcmp(response, cmp_text.ct))
    		return (0);
    
    	/*
    	 * No match against the computed hex cipher.  The token could be
    	 * in decimal mode.  Pervert the string to magic decimal equivalent.
    	 */
    
    	h2d(cmp_text.ct);
    
    	if ((tokenrec.mode & TOKEN_DECMODE) && !strcmp(response, cmp_text.ct))
    		return (0);
    
    	return (-1);
    }
    
    /*
     * Initialize a new user record in the token database.
     */
    
    int
    tokenuserinit(int flags, char *username, unsigned char *usecret, unsigned mode)
    {
    	TOKENDB_Rec tokenrec;
    	TOKEN_CBlock secret;
    	TOKEN_CBlock nulls;
    	TOKEN_CBlock checksum;
    	TOKEN_CBlock checktxt;
    	DES_key_schedule key_schedule;
    
    	memset(&secret, 0, sizeof(secret));
    
    	/*
    	 * If no user secret passed in, create one
    	 */
    
    	if ( (flags & TOKEN_GENSECRET) )
    		tokenseed(&secret);
    	else
    		memcpy(&secret, usecret, sizeof(DES_cblock));
    
    	DES_fixup_key_parity(&secret.cb);
    
    	/*
    	 * Check if the db record already exists.  If there's no
    	 * force-init flag and it exists, go away. Else,
    	 * create the user's db record and put to the db.
    	 */
    
    
    	if (!(flags & TOKEN_FORCEINIT) &&
    	    tokendb_getrec(username, &tokenrec) == 0)
    		return (1);
    
    	memset(&tokenrec, 0, sizeof(tokenrec));
    	strlcpy(tokenrec.uname, username, sizeof(tokenrec.uname));
    	cb2h(secret, tokenrec.secret);
    	tokenrec.mode = 0;
    	tokenrec.flags = TOKEN_ENABLED | TOKEN_USEMODES;
    	tokenrec.mode = mode;
    	memset(tokenrec.reserved_char1, 0, sizeof(tokenrec.reserved_char1));
    	memset(tokenrec.reserved_char2, 0, sizeof(tokenrec.reserved_char2));
    
    	if (tokendb_putrec(username, &tokenrec))
    		return (-1);
    
    	/*
    	 * Check if the shared secret was generated here. If so, we
    	 * need to inform the user about it in order that it can be
    	 * programmed into the token. See tokenverify() (above) for
    	 * discussion of cipher generation.
    	 */
    
    	if (!(flags & TOKEN_GENSECRET)) {
    		explicit_bzero(&secret, sizeof(secret));
    		return (0);
    	}
    
    	printf("Shared secret for %s\'s token: "
    	    "%03o %03o %03o %03o %03o %03o %03o %03o\n",
    	    username, secret.cb[0], secret.cb[1], secret.cb[2], secret.cb[3],
    	    secret.cb[4], secret.cb[5], secret.cb[6], secret.cb[7]);
    
    	DES_key_sched(&secret.cb, &key_schedule);
    	explicit_bzero(&secret, sizeof(secret));
    	memset(&nulls, 0, sizeof(nulls));
    	DES_ecb_encrypt(&nulls.cb, &checksum.cb, &key_schedule, DES_ENCRYPT);
    	explicit_bzero(&key_schedule, sizeof(key_schedule));
    	HTONL(checksum.ul[0]);
    	snprintf(checktxt.ct, sizeof(checktxt.ct), "%8.8x", checksum.ul[0]);
    	printf("Hex Checksum: \"%s\"", checktxt.ct);
    
    	h2d(checktxt.ct);
    	printf("\tDecimal Checksum: \"%s\"\n", checktxt.ct);
    
    	return (0);
    }
    
    /*
     * Magically transform a hex character string into a decimal character
     * string as defined by the token card vendor. The string should have
     * been lowercased by now.
     */
    
    static	void
    h2d(char *cp)
    {
    	int	i;
    
    	for (i=0; i<sizeof(DES_cblock); i++, cp++) {
    		if (*cp >= 'a' && *cp <= 'f')
    			*cp = tt->map[*cp - 'a'];
    	}
    }
    
    /*
     * Translate an hex 16 byte ascii representation of an unsigned
     * integer to a DES_cblock.
     */
    
    static	void
    h2cb(char *hp, TOKEN_CBlock *cb)
    {
    	char	scratch[9];
    
    	strlcpy(scratch, hp, sizeof(scratch));
    	cb->ul[0] = strtoul(scratch, NULL, 16);
    
    	strlcpy(scratch, hp + 8, sizeof(scratch));
    	cb->ul[1] = strtoul(scratch, NULL, 16);
    }
    
    /*
     * Translate a DES_cblock to an 16 byte ascii hex representation.
     */
    
    static	void
    cb2h(TOKEN_CBlock cb, char* hp)
    {
    	char	scratch[17];
    
    	snprintf(scratch,   9, "%8.8x", cb.ul[0]);
    	snprintf(scratch+8, 9, "%8.8x", cb.ul[1]);
    	memcpy(hp, scratch, 16);
    }
    
    /*
     * Lowercase possible hex response
     */
    
    static	void
    lcase(char *cp)
    {
    	while (*cp) {
    		if (isupper((unsigned char)*cp))
    			*cp = tolower((unsigned char)*cp);
    		cp++;
    	}
    }