#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <pwd.h>
#include <errno.h>
#include <stdlib.h>
#include <syslog.h>
#include <crypt.h>
#include <md5.h>
#define CRYPT_ALGNAME "md5"
#define BASIC_ROUND_COUNT 4096
#define DIGEST_LEN 16
#define ROUND_BUFFER_LEN 64
static const char constant_phrase[] =
"To be, or not to be,--that is the question:--\n"
"Whether 'tis nobler in the mind to suffer\n"
"The slings and arrows of outrageous fortune\n"
"Or to take arms against a sea of troubles,\n"
"And by opposing end them?--To die,--to sleep,--\n"
"No more; and by a sleep to say we end\n"
"The heartache, and the thousand natural shocks\n"
"That flesh is heir to,--'tis a consummation\n"
"Devoutly to be wish'd. To die,--to sleep;--\n"
"To sleep! perchance to dream:--ay, there's the rub;\n"
"For in that sleep of death what dreams may come,\n"
"When we have shuffled off this mortal coil,\n"
"Must give us pause: there's the respect\n"
"That makes calamity of so long life;\n"
"For who would bear the whips and scorns of time,\n"
"The oppressor's wrong, the proud man's contumely,\n"
"The pangs of despis'd love, the law's delay,\n"
"The insolence of office, and the spurns\n"
"That patient merit of the unworthy takes,\n"
"When he himself might his quietus make\n"
"With a bare bodkin? who would these fardels bear,\n"
"To grunt and sweat under a weary life,\n"
"But that the dread of something after death,--\n"
"The undiscover'd country, from whose bourn\n"
"No traveller returns,--puzzles the will,\n"
"And makes us rather bear those ills we have\n"
"Than fly to others that we know not of?\n"
"Thus conscience does make cowards of us all;\n"
"And thus the native hue of resolution\n"
"Is sicklied o'er with the pale cast of thought;\n"
"And enterprises of great pith and moment,\n"
"With this regard, their currents turn awry,\n"
"And lose the name of action.--Soft you now!\n"
"The fair Ophelia!--Nymph, in thy orisons\n"
"Be all my sins remember'd.\n";
static int
md5bit(uint8_t *digest, int bit_num)
{
int byte_off;
int bit_off;
bit_num %= 128;
byte_off = bit_num / 8;
bit_off = bit_num % 8;
return ((digest[byte_off] & (0x01 << bit_off)) ? 1 : 0);
}
static uchar_t itoa64[] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static void
to64(char *s, uint64_t v, int n)
{
while (--n >= 0) {
*s++ = itoa64[v & 0x3f];
v >>= 6;
}
}
#define ROUNDS "rounds="
#define ROUNDSLEN (sizeof (ROUNDS) - 1)
static uint32_t
getrounds(const char *s)
{
char *r, *p, *e;
long val;
if (s == NULL)
return (0);
if ((r = strstr(s, ROUNDS)) == NULL) {
return (0);
}
if (strncmp(r, ROUNDS, ROUNDSLEN) != 0) {
return (0);
}
p = r + ROUNDSLEN;
errno = 0;
val = strtol(p, &e, 10);
if (errno != 0 || val < 0 ||
!(*e == '\0' || *e == ',' || *e == '$')) {
syslog(LOG_WARNING,
"crypt_sunmd5: invalid rounds specification \"%s\"", s);
return (0);
}
return ((uint32_t)val);
}
char *
crypt_gensalt_impl(char *gsbuffer,
size_t gsbufflen,
const char *oldsalt,
const struct passwd *userinfo,
const char **params)
{
uint32_t confrounds = 0;
uint32_t saltrounds;
int i;
int fd;
ssize_t got;
uint64_t rndval;
char rndstr[sizeof (rndval) + 1];
for (i = 0; params != NULL && params[i] != NULL; i++) {
if (strncmp(params[i], ROUNDS, ROUNDSLEN) == 0) {
confrounds = getrounds(params[i]);
} else {
syslog(LOG_WARNING,
"crypt_sunmd5: invalid parameter %s", params[i]);
errno = EINVAL;
return (NULL);
}
}
saltrounds = getrounds(oldsalt);
if (confrounds > saltrounds) {
saltrounds = confrounds;
}
if ((fd = open("/dev/random", O_RDONLY)) == -1) {
goto fail;
}
got = read(fd, &rndval, sizeof (rndval));
if (got < sizeof (rndval)) {
int err = errno;
(void) close(fd);
errno = err;
goto fail;
}
(void) close(fd);
to64((char *)&rndstr, rndval, sizeof (rndval));
rndstr[sizeof (rndstr) - 1] = '\0';
if (saltrounds > 0) {
if (snprintf(gsbuffer, gsbufflen,
"$" CRYPT_ALGNAME "," ROUNDS "%d$",
saltrounds) >= gsbufflen)
goto fail;
} else {
if (snprintf(gsbuffer, gsbufflen,
"$" CRYPT_ALGNAME "$") >= gsbufflen)
goto fail;
}
if (strlcat(gsbuffer, rndstr, gsbufflen) >= gsbufflen)
goto fail;
if (strlcat(gsbuffer, "$", gsbufflen) >= gsbufflen)
goto fail;
return (gsbuffer);
fail:
bzero(gsbuffer, gsbufflen);
return (NULL);
}
char *
crypt_genhash_impl(char *ctbuffer,
size_t ctbufflen,
const char *plaintext,
const char *salt,
const char **params)
{
int i;
int round;
int maxrounds = BASIC_ROUND_COUNT;
uint32_t l;
char *puresalt;
char *saltend;
char *p;
struct {
MD5_CTX context;
uint8_t digest[DIGEST_LEN];
int indirect_4[16];
int shift_4[16];
int s7shift;
int indirect_7[16];
int shift_7[16];
int indirect_a;
int shift_a;
int indirect_b;
int shift_b;
int bit_a;
int bit_b;
char roundascii[ROUND_BUFFER_LEN];
} data;
saltend = strrchr(salt, '$');
if (saltend == NULL || saltend == salt) {
return (NULL);
}
if (saltend[1] != '\0') {
size_t len = saltend - salt + 1;
if ((puresalt = malloc(len)) == NULL) {
return (NULL);
}
(void) strlcpy(puresalt, salt, len);
} else {
puresalt = strdup(salt);
if (puresalt == NULL) {
return (NULL);
}
}
maxrounds += getrounds(salt);
MD5Init(&data.context);
MD5Update(&data.context, (uchar_t *)plaintext, strlen(plaintext));
MD5Update(&data.context, (uchar_t *)puresalt, strlen(puresalt));
MD5Final(data.digest, &data.context);
for (round = 0; round < maxrounds; round++) {
MD5Init(&data.context);
MD5Update(&data.context, data.digest, sizeof (data.digest));
for (i = 0; i < 16; i++) {
int j;
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);
for (i = 0; i < 16; i++) {
data.indirect_4[i] =
(data.digest[i] >> data.shift_4[i]) & 0x0f;
}
for (i = 0; i < 16; i++) {
data.indirect_7[i] = (data.digest[data.indirect_4[i]]
>> data.shift_7[i]) & 0x7f;
}
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);
}
data.indirect_a = (data.indirect_a >> data.shift_a) & 0x7f;
data.indirect_b = (data.indirect_b >> data.shift_b) & 0x7f;
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
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
}
(void) snprintf(data.roundascii, ROUND_BUFFER_LEN, "%d", round);
MD5Update(&data.context,
(unsigned char *) data.roundascii, strlen(data.roundascii));
MD5Final(data.digest, &data.context);
}
#if ALGDEBUG
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';
bzero(&data, sizeof (data));
return (ctbuffer);
}