/*
* 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);
}