1N/A/*
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License (the "License").
1N/A * You may not use this file except in compliance with the License.
1N/A *
1N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * When distributing Covered Code, include this CDDL HEADER in each
1N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A * If applicable, add the following below this CDDL HEADER, with the
1N/A * fields enclosed by brackets "[]" replaced with your own identifying
1N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1N/A *
1N/A * CDDL HEADER END
1N/A */
1N/A/*
1N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A#include <sys/types.h>
1N/A#include <sys/stat.h>
1N/A#include <fcntl.h>
1N/A#include <unistd.h>
1N/A#include <strings.h>
1N/A#include <pwd.h>
1N/A#include <errno.h>
1N/A#include <stdlib.h>
1N/A#include <syslog.h>
1N/A
1N/A#include <crypt.h>
1N/A#include <md5.h>
1N/A
1N/A#define CRYPT_ALGNAME "md5"
1N/A
1N/A
1N/A/* minimum number of rounds we do, not including the per-user ones */
1N/A
1N/A#define BASIC_ROUND_COUNT 4096 /* enough to make things interesting */
1N/A#define DIGEST_LEN 16
1N/A#define ROUND_BUFFER_LEN 64
1N/A
1N/A/*
1N/A * Public domain quotation courtesy of Project Gutenberg.
1N/A * ftp://metalab.unc.edu/pub/docs/books/gutenberg/etext98/2ws2610.txt
1N/A * Hamlet III.ii - 1517 bytes, including trailing NUL
1N/A * ANSI-C string constant concatenation is a requirement here.
1N/A */
1N/A
1N/Astatic const char constant_phrase[] =
1N/A "To be, or not to be,--that is the question:--\n"
1N/A "Whether 'tis nobler in the mind to suffer\n"
1N/A "The slings and arrows of outrageous fortune\n"
1N/A "Or to take arms against a sea of troubles,\n"
1N/A "And by opposing end them?--To die,--to sleep,--\n"
1N/A "No more; and by a sleep to say we end\n"
1N/A "The heartache, and the thousand natural shocks\n"
1N/A "That flesh is heir to,--'tis a consummation\n"
1N/A "Devoutly to be wish'd. To die,--to sleep;--\n"
1N/A "To sleep! perchance to dream:--ay, there's the rub;\n"
1N/A "For in that sleep of death what dreams may come,\n"
1N/A "When we have shuffled off this mortal coil,\n"
1N/A "Must give us pause: there's the respect\n"
1N/A "That makes calamity of so long life;\n"
1N/A "For who would bear the whips and scorns of time,\n"
1N/A "The oppressor's wrong, the proud man's contumely,\n"
1N/A "The pangs of despis'd love, the law's delay,\n"
1N/A "The insolence of office, and the spurns\n"
1N/A "That patient merit of the unworthy takes,\n"
1N/A "When he himself might his quietus make\n"
1N/A "With a bare bodkin? who would these fardels bear,\n"
1N/A "To grunt and sweat under a weary life,\n"
1N/A "But that the dread of something after death,--\n"
1N/A "The undiscover'd country, from whose bourn\n"
1N/A "No traveller returns,--puzzles the will,\n"
1N/A "And makes us rather bear those ills we have\n"
1N/A "Than fly to others that we know not of?\n"
1N/A "Thus conscience does make cowards of us all;\n"
1N/A "And thus the native hue of resolution\n"
1N/A "Is sicklied o'er with the pale cast of thought;\n"
1N/A "And enterprises of great pith and moment,\n"
1N/A "With this regard, their currents turn awry,\n"
1N/A "And lose the name of action.--Soft you now!\n"
1N/A "The fair Ophelia!--Nymph, in thy orisons\n"
1N/A "Be all my sins remember'd.\n";
1N/A
1N/A/* ------------------------------------------------------------------ */
1N/A
1N/Astatic int
1N/Amd5bit(uint8_t *digest, int bit_num)
1N/A{
1N/A int byte_off;
1N/A int bit_off;
1N/A
1N/A bit_num %= 128; /* keep this bounded for convenience */
1N/A byte_off = bit_num / 8;
1N/A bit_off = bit_num % 8;
1N/A
1N/A /* return the value of bit N from the digest */
1N/A return ((digest[byte_off] & (0x01 << bit_off)) ? 1 : 0);
1N/A}
1N/A
1N/Astatic uchar_t itoa64[] = /* 0 ... 63 => ascii - 64 */
1N/A "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1N/A
1N/Astatic void
1N/Ato64(char *s, uint64_t v, int n)
1N/A{
1N/A while (--n >= 0) {
1N/A *s++ = itoa64[v & 0x3f];
1N/A v >>= 6;
1N/A }
1N/A}
1N/A
1N/A#define ROUNDS "rounds="
1N/A#define ROUNDSLEN (sizeof (ROUNDS) - 1)
1N/A
1N/A/*
1N/A * get the integer value after rounds= where ever it occurs in the string.
1N/A * if the last char after the int is a , or $ that is fine anything else is an
1N/A * error.
1N/A */
1N/Astatic uint32_t
1N/Agetrounds(const char *s)
1N/A{
1N/A char *r, *p, *e;
1N/A long val;
1N/A
1N/A if (s == NULL)
1N/A return (0);
1N/A
1N/A if ((r = strstr(s, ROUNDS)) == NULL) {
1N/A return (0);
1N/A }
1N/A
1N/A if (strncmp(r, ROUNDS, ROUNDSLEN) != 0) {
1N/A return (0);
1N/A }
1N/A
1N/A p = r + ROUNDSLEN;
1N/A errno = 0;
1N/A val = strtol(p, &e, 10);
1N/A /*
1N/A * An error occurred or there is non-numeric stuff at the end
1N/A * which isn't one of the crypt(3c) special chars ',' or '$'
1N/A */
1N/A if (errno != 0 || val < 0 ||
1N/A !(*e == '\0' || *e == ',' || *e == '$')) {
1N/A syslog(LOG_WARNING,
1N/A "crypt_sunmd5: invalid rounds specification \"%s\"", s);
1N/A return (0);
1N/A }
1N/A
1N/A return ((uint32_t)val);
1N/A}
1N/A
1N/A/* ARGSUSED3 */
1N/Achar *
1N/Acrypt_gensalt_impl(char *gsbuffer,
1N/A size_t gsbufflen,
1N/A const char *oldsalt,
1N/A const struct passwd *userinfo,
1N/A const char **params)
1N/A{
1N/A uint32_t confrounds = 0;
1N/A uint32_t saltrounds;
1N/A int i;
1N/A int fd;
1N/A ssize_t got;
1N/A uint64_t rndval;
1N/A char rndstr[sizeof (rndval) + 1]; /* rndval as a base64 string */
1N/A
1N/A for (i = 0; params != NULL && params[i] != NULL; i++) {
1N/A if (strncmp(params[i], ROUNDS, ROUNDSLEN) == 0) {
1N/A confrounds = getrounds(params[i]);
1N/A } else {
1N/A syslog(LOG_WARNING,
1N/A "crypt_sunmd5: invalid parameter %s", params[i]);
1N/A errno = EINVAL;
1N/A return (NULL);
1N/A }
1N/A }
1N/A
1N/A /*
1N/A * If the config file has a higher value for rounds= than what
1N/A * was in the old salt use that, otherwise keep what was in the
1N/A * old salt.
1N/A */
1N/A saltrounds = getrounds(oldsalt);
1N/A if (confrounds > saltrounds) {
1N/A saltrounds = confrounds;
1N/A }
1N/A
1N/A if ((fd = open("/dev/random", O_RDONLY)) == -1) {
1N/A goto fail;
1N/A }
1N/A
1N/A got = read(fd, &rndval, sizeof (rndval));
1N/A if (got < sizeof (rndval)) {
1N/A int err = errno;
1N/A
1N/A (void) close(fd);
1N/A errno = err;
1N/A goto fail;
1N/A }
1N/A (void) close(fd);
1N/A
1N/A to64((char *)&rndstr, rndval, sizeof (rndval));
1N/A rndstr[sizeof (rndstr) - 1] = '\0';
1N/A
1N/A if (saltrounds > 0) {
1N/A if (snprintf(gsbuffer, gsbufflen,
1N/A "$" CRYPT_ALGNAME "," ROUNDS "%d$",
1N/A saltrounds) >= gsbufflen)
1N/A goto fail;
1N/A } else {
1N/A if (snprintf(gsbuffer, gsbufflen,
1N/A "$" CRYPT_ALGNAME "$") >= gsbufflen)
1N/A goto fail;
1N/A }
1N/A
1N/A if (strlcat(gsbuffer, rndstr, gsbufflen) >= gsbufflen)
1N/A goto fail;
1N/A if (strlcat(gsbuffer, "$", gsbufflen) >= gsbufflen)
1N/A goto fail;
1N/A
1N/A return (gsbuffer);
1N/A
1N/Afail:
1N/A bzero(gsbuffer, gsbufflen);
1N/A return (NULL);
1N/A}
1N/A
1N/A
1N/A/*ARGSUSED4*/
1N/Achar *
1N/Acrypt_genhash_impl(char *ctbuffer,
1N/A size_t ctbufflen,
1N/A const char *plaintext,
1N/A const char *salt,
1N/A const char **params)
1N/A{
1N/A int i;
1N/A int round;
1N/A int maxrounds = BASIC_ROUND_COUNT;
1N/A uint32_t l;
1N/A char *puresalt;
1N/A char *saltend;
1N/A char *p;
1N/A
1N/A /* put all the sensitive data in a struct */
1N/A struct {
1N/A MD5_CTX context; /* working buffer for MD5 algorithm */
1N/A uint8_t digest[DIGEST_LEN]; /* where the MD5 digest is stored */
1N/A
1N/A int indirect_4[16]; /* extracted array of 4bit values */
1N/A int shift_4[16]; /* shift schedule, vals 0..4 */
1N/A
1N/A int s7shift; /* shift for shift_7 creation, vals 0..7 */
1N/A int indirect_7[16]; /* extracted array of 7bit values */
1N/A int shift_7[16]; /* shift schedule, vals 0..1 */
1N/A
1N/A int indirect_a; /* 7bit index into digest */
1N/A int shift_a; /* shift schedule, vals 0..1 */
1N/A
1N/A int indirect_b; /* 7bit index into digest */
1N/A int shift_b; /* shift schedule, vals 0..1 */
1N/A
1N/A int bit_a; /* single bit for cointoss */
1N/A int bit_b; /* single bit for cointoss */
1N/A
1N/A char roundascii[ROUND_BUFFER_LEN]; /* ascii rep of roundcount */
1N/A } data;
1N/A
1N/A
1N/A /*
1N/A * Extract the puresalt (if it exists) from the existing salt string
1N/A * $md5[,rounds=%d]$<puresalt>$<optional existing encoding>
1N/A */
1N/A saltend = strrchr(salt, '$');
1N/A if (saltend == NULL || saltend == salt) {
1N/A return (NULL);
1N/A }
1N/A if (saltend[1] != '\0') {
1N/A size_t len = saltend - salt + 1;
1N/A if ((puresalt = malloc(len)) == NULL) {
1N/A return (NULL);
1N/A }
1N/A (void) strlcpy(puresalt, salt, len);
1N/A } else {
1N/A puresalt = strdup(salt);
1N/A if (puresalt == NULL) {
1N/A return (NULL);
1N/A }
1N/A }
1N/A
1N/A maxrounds += getrounds(salt);
1N/A
1N/A /* initialise the context */
1N/A
1N/A MD5Init(&data.context);
1N/A
1N/A /* update with the (hopefully entropic) plaintext */
1N/A
1N/A MD5Update(&data.context, (uchar_t *)plaintext, strlen(plaintext));
1N/A
1N/A /* update with the (publically known) salt */
1N/A
1N/A MD5Update(&data.context, (uchar_t *)puresalt, strlen(puresalt));
1N/A
1N/A
1N/A /* compute the digest */
1N/A
1N/A MD5Final(data.digest, &data.context);
1N/A
1N/A /*
1N/A * now to delay high-speed md5 implementations that have stuff
1N/A * like code inlining, loops unrolled and table lookup
*/
for (round = 0; round < maxrounds; round++) {
/* re-initialise the context */
MD5Init(&data.context);
/* update with the previous digest */
MD5Update(&data.context, data.digest, sizeof (data.digest));
/* populate the shift schedules for use later */
for (i = 0; i < 16; i++) {
int j;
/* offset 3 -> occasionally span more than 1 int32 fetch */
j = (i + 3) % 16;
data.s7shift = data.digest[i] % 8;
data.shift_4[i] = data.digest[j] % 5;
data.shift_7[i] = (data.digest[j] >> data.s7shift)
& 0x01;
}
data.shift_a = md5bit(data.digest, round);
data.shift_b = md5bit(data.digest, round + 64);
/* populate indirect_4 with 4bit values extracted from digest */
for (i = 0; i < 16; i++) {
/* shift the digest byte and extract four bits */
data.indirect_4[i] =
(data.digest[i] >> data.shift_4[i]) & 0x0f;
}
/*
* populate indirect_7 with 7bit values from digest
* indexed via indirect_4
*/
for (i = 0; i < 16; i++) {
/* shift the digest byte and extract seven bits */
data.indirect_7[i] = (data.digest[data.indirect_4[i]]
>> data.shift_7[i]) & 0x7f;
}
/*
* use the 7bit values to indirect into digest,
* and create two 8bit values from the results.
*/
data.indirect_a = data.indirect_b = 0;
for (i = 0; i < 8; i++) {
data.indirect_a |= (md5bit(data.digest,
data.indirect_7[i]) << i);
data.indirect_b |= (md5bit(data.digest,
data.indirect_7[i + 8]) << i);
}
/* shall we utilise the top or bottom 7 bits? */
data.indirect_a = (data.indirect_a >> data.shift_a) & 0x7f;
data.indirect_b = (data.indirect_b >> data.shift_b) & 0x7f;
/* extract two data.digest bits */
data.bit_a = md5bit(data.digest, data.indirect_a);
data.bit_b = md5bit(data.digest, data.indirect_b);
#if ALGDEBUG
for (i = 0; i < 15; i++) {
(void) printf("%1x-", data.indirect_4[i]);
}
(void) printf("%1x ", data.indirect_4[15]);
for (i = 0; i < 15; i++) {
(void) printf("%02x-", data.indirect_7[i]);
}
(void) printf("%02x ", data.indirect_7[15]);
(void) printf("%02x/%02x ", data.indirect_a, data.indirect_b);
(void) printf("%d^%d\n", data.bit_a, data.bit_b);
#endif
/* xor a coin-toss; if true, mix-in the constant phrase */
if (data.bit_a ^ data.bit_b) {
MD5Update(&data.context,
(unsigned char *) constant_phrase,
sizeof (constant_phrase));
#if ALGDEBUG
(void) printf("mixing constant_phrase\n");
#endif
}
/* digest a decimal sprintf of the current roundcount */
(void) snprintf(data.roundascii, ROUND_BUFFER_LEN, "%d", round);
MD5Update(&data.context,
(unsigned char *) data.roundascii, strlen(data.roundascii));
/* compute/flush the digest, and loop */
MD5Final(data.digest, &data.context);
}
#if ALGDEBUG
/* print the digest */
for (i = 0; i < 16; i++) {
(void) printf("%02x", data.digest[i]);
}
(void) printf("\n");
#endif
(void) snprintf(ctbuffer, ctbufflen, "%s$", puresalt);
p = ctbuffer + strlen(ctbuffer);
l = (data.digest[ 0]<<16) | (data.digest[ 6]<<8) | data.digest[12];
to64(p, l, 4); p += 4;
l = (data.digest[ 1]<<16) | (data.digest[ 7]<<8) | data.digest[13];
to64(p, l, 4); p += 4;
l = (data.digest[ 2]<<16) | (data.digest[ 8]<<8) | data.digest[14];
to64(p, l, 4); p += 4;
l = (data.digest[ 3]<<16) | (data.digest[ 9]<<8) | data.digest[15];
to64(p, l, 4); p += 4;
l = (data.digest[ 4]<<16) | (data.digest[10]<<8) | data.digest[ 5];
to64(p, l, 4); p += 4;
l = data.digest[11]; to64(p, l, 2); p += 2;
*p = '\0';
/* tidy up after ourselves */
bzero(&data, sizeof (data));
return (ctbuffer);
}