OpenSolaris_b135/lib/crypt_modules/bsdmd5/bsdmd5.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Portions of this code:
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
 * ----------------------------------------------------------------------------
 *
 * $FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp $
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <errno.h>

#include <md5.h>
#include <crypt.h>

static const char crypt_alg_magic[] = "$1$";

#define	SALT_LEN	8

static uchar_t itoa64[] =		/* 0 ... 63 => ascii - 64 */
	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

static void
to64(char *s, uint64_t v, int n)
{
	while (--n >= 0) {
		*s++ = itoa64[v & 0x3f];
		v >>= 6;
	}
}


/* ARGSUSED4 */
char *
crypt_genhash_impl(char *ctbuffer,
	    size_t ctbufflen,
	    const char *plaintext,
	    const char *switchsalt,
	    const char **params)
{
	char *p;
	int sl, l, pl, i;
	uchar_t *sp, *ep;
	uchar_t final[16]; /* XXX: 16 is some number from the orig source */
	MD5_CTX ctx, ctx1;
	const int crypt_alg_magic_len = strlen(crypt_alg_magic);

	/* Refine the salt */
	sp = (uchar_t *)switchsalt;

	/* skip our magic string */
	if (strncmp((char *)sp, crypt_alg_magic, crypt_alg_magic_len) == 0) {
		sp += crypt_alg_magic_len;
	}

	/* Salt stops at the first $, max SALT_LEN chars */
	for (ep = sp; *ep && *ep != '$' && ep < (sp + SALT_LEN); ep++)
		continue;

	sl = ep - sp;

	MD5Init(&ctx);

	/* The password first, since that is what is most unknown */
	MD5Update(&ctx, (uchar_t *)plaintext, strlen(plaintext));

	/* Then our magic string */
	MD5Update(&ctx, (uchar_t *)crypt_alg_magic, strlen(crypt_alg_magic));

	/* Then the raw salt */
	MD5Update(&ctx, (uchar_t *)sp, sl);

	/* Then just as many characters of the MD5(plaintext,salt,plaintext) */
	MD5Init(&ctx1);
	MD5Update(&ctx1, (uchar_t *)plaintext, strlen(plaintext));
	MD5Update(&ctx1, sp, sl);
	MD5Update(&ctx1, (uchar_t *)plaintext, strlen(plaintext));
	MD5Final(final, &ctx1);
	for (pl = strlen(plaintext); pl > 0; pl -= 16)
		MD5Update(&ctx, final, pl > 16 ? 16 : pl);

	/* Don't leave anything around in vm they could use. */
	(void) memset(final, 0, sizeof (final));

	/* Then something really weird... */
	for (i = strlen(plaintext); i; i >>= 1) {
		if (i & 1) {
			MD5Update(&ctx, final, 1);
		} else {
			MD5Update(&ctx, (uchar_t *)plaintext, 1);
		}
	}

	/* Now make the output string */
	(void) strlcpy(ctbuffer, crypt_alg_magic, ctbufflen);
	(void) strncat(ctbuffer, (const char *)sp, sl);
	(void) strlcat(ctbuffer, "$", ctbufflen);

	MD5Final(final, &ctx);

	/*
	 * and now, just to make sure things don't run too fast
	 * On a 60 Mhz Pentium this takes 34 msec, so you would
	 * need 30 seconds to build a 1000 entry dictionary...
	 */
	for (i = 0; i < 1000; i++) {
		MD5Init(&ctx1);
		if (i & 1)
			MD5Update(&ctx1, (uchar_t *)plaintext,
			    strlen(plaintext));
		else
			MD5Update(&ctx1, final, 16);

		if (i % 3)
			MD5Update(&ctx1, sp, sl);

		if (i % 7)
			MD5Update(&ctx1, (uchar_t *)plaintext,
			    strlen(plaintext));

		if (i & 1)
			MD5Update(&ctx1, final, 16);
		else
			MD5Update(&ctx1, (uchar_t *)plaintext,
			    strlen(plaintext));
		MD5Final(final, &ctx1);
	}

	p = ctbuffer + strlen(ctbuffer);

	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
	l = final[11]; to64(p, l, 2); p += 2;
	*p = '\0';

	/* Don't leave anything around in vm they could use. */
	(void) memset(final, 0, sizeof (final));

	return (ctbuffer);
}


/* ARGSUSED2 */
char *
crypt_gensalt_impl(char *gsbuffer,
	    size_t gsbufflen,
	    const char *oldsalt,
	    const struct passwd *userinfo,
	    const char **params)
{
	int fd;
	int err;
	ssize_t got;
	uint64_t rndval;

	if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
		return (NULL);
	}

	(void) strlcpy(gsbuffer, crypt_alg_magic, gsbufflen);

	got = read(fd, &rndval, sizeof (rndval));
	if (got < sizeof (rndval)) {
		err = errno;
		(void) close(fd);
		errno = err;
		return (NULL);
	}
	to64(&gsbuffer[strlen(crypt_alg_magic)], rndval, sizeof (rndval));

	(void) close(fd);

	return (gsbuffer);
}