Skip to content

Commit

Permalink
Make libcrypt thread-safe. Add crypt_r(3).
Browse files Browse the repository at this point in the history
glibc has a pretty nice function called crypt_r(3), which is nothing
more than crypt(3), but thread-safe. It accomplishes this by introducing
a 'struct crypt_data' structure that contains a buffer that is large
enough to hold the resulting string.

Let's go ahead and also add this function. It would be a shame if a
useful function like this wouldn't be usable in multithreaded apps.
Refactor crypt.c and all of the backends to no longer declare static
arrays, but write their output in a provided buffer.

There is no need to do any buffer length computation here, as we'll just
need to ensure that 'struct crypt_data' is large enough, which it is.
_PASSWORD_LEN is defined to 128 bytes, but in this case I'm picking 256,
as this is going to be part of the actual ABI.

Differential Revision:	https://reviews.freebsd.org/D7306
  • Loading branch information
Ed Schouten authored and Ed Schouten committed Aug 10, 2016
1 parent 3a77833 commit 5f521d7
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 276 deletions.
7 changes: 7 additions & 0 deletions include/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,11 +484,18 @@ pid_t vfork(void) __returns_twice;

#if __BSD_VISIBLE
struct timeval; /* select(2) */

struct crypt_data {
int initialized; /* For compatibility with glibc. */
char __buf[256]; /* Buffer returned by crypt_r(). */
};

int acct(const char *);
int async_daemon(void);
int check_utility_compat(const char *);
const char *
crypt_get_format(void);
char *crypt_r(const char *, const char *, struct crypt_data *);
int crypt_set_format(const char *);
int des_cipher(const char *, char *, long, int);
int des_setkey(const char *key);
Expand Down
3 changes: 2 additions & 1 deletion lib/libcrypt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ SRCS= crypt.c misc.c \
crypt-sha256.c sha256c.c \
crypt-sha512.c sha512c.c
MAN= crypt.3
MLINKS= crypt.3 crypt_get_format.3 crypt.3 crypt_set_format.3
MLINKS= crypt.3 crypt_get_format.3 crypt.3 crypt_r.3 \
crypt.3 crypt_set_format.3
CFLAGS+= -I${.CURDIR}/../libmd -I${.CURDIR}/../libutil \
-I${.CURDIR}/../../sys/crypto/sha2

Expand Down
50 changes: 22 additions & 28 deletions lib/libcrypt/crypt-md5.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,27 @@ __FBSDID("$FreeBSD$");
* UNIX password
*/

char *
crypt_md5(const char *pw, const char *salt)
int
crypt_md5(const char *pw, const char *salt, char *buffer)
{
MD5_CTX ctx,ctx1;
unsigned long l;
int sl, pl;
u_int i;
u_char final[MD5_SIZE];
static const char *sp, *ep;
static char passwd[120], *p;
const char *ep;
static const char *magic = "$1$";

/* Refine the Salt first */
sp = salt;

/* If it starts with the magic string, then skip that */
if(!strncmp(sp, magic, strlen(magic)))
sp += strlen(magic);
/* If the salt starts with the magic string, skip that. */
if (!strncmp(salt, magic, strlen(magic)))
salt += strlen(magic);

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

/* get the length of the true salt */
sl = ep - sp;
sl = ep - salt;

MD5Init(&ctx);

Expand All @@ -76,12 +72,12 @@ crypt_md5(const char *pw, const char *salt)
MD5Update(&ctx, (const u_char *)magic, strlen(magic));

/* Then the raw salt */
MD5Update(&ctx, (const u_char *)sp, (u_int)sl);
MD5Update(&ctx, (const u_char *)salt, (u_int)sl);

/* Then just as many characters of the MD5(pw,salt,pw) */
MD5Init(&ctx1);
MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
MD5Update(&ctx1, (const u_char *)sp, (u_int)sl);
MD5Update(&ctx1, (const u_char *)salt, (u_int)sl);
MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
MD5Final(final, &ctx1);
for(pl = (int)strlen(pw); pl > 0; pl -= MD5_SIZE)
Expand All @@ -99,9 +95,9 @@ crypt_md5(const char *pw, const char *salt)
MD5Update(&ctx, (const u_char *)pw, 1);

/* Now make the output string */
strcpy(passwd, magic);
strncat(passwd, sp, (u_int)sl);
strcat(passwd, "$");
buffer = stpcpy(buffer, magic);
buffer = stpncpy(buffer, salt, (u_int)sl);
*buffer++ = '$';

MD5Final(final, &ctx);

Expand All @@ -118,7 +114,7 @@ crypt_md5(const char *pw, const char *salt)
MD5Update(&ctx1, (const u_char *)final, MD5_SIZE);

if(i % 3)
MD5Update(&ctx1, (const u_char *)sp, (u_int)sl);
MD5Update(&ctx1, (const u_char *)salt, (u_int)sl);

if(i % 7)
MD5Update(&ctx1, (const u_char *)pw, strlen(pw));
Expand All @@ -130,24 +126,22 @@ crypt_md5(const char *pw, const char *salt)
MD5Final(final, &ctx1);
}

p = passwd + strlen(passwd);

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

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

return (passwd);
return (0);
}
31 changes: 12 additions & 19 deletions lib/libcrypt/crypt-nthash.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,14 @@ __FBSDID("$FreeBSD$");
*/

/* ARGSUSED */
char *
crypt_nthash(const char *pw, const char *salt __unused)
int
crypt_nthash(const char *pw, const char *salt __unused, char *buffer)
{
size_t unipwLen;
int i, j;
static char hexconvtab[] = "0123456789abcdef";
int i;
static const char hexconvtab[] = "0123456789abcdef";
static const char *magic = "$3$";
static char passwd[120];
u_int16_t unipw[128];
char final[MD4_SIZE*2 + 1];
u_char hash[MD4_SIZE];
const char *s;
MD4_CTX ctx;
Expand All @@ -70,19 +68,14 @@ crypt_nthash(const char *pw, const char *salt __unused)
MD4Init(&ctx);
MD4Update(&ctx, (u_char *)unipw, unipwLen*sizeof(u_int16_t));
MD4Final(hash, &ctx);

for (i = j = 0; i < MD4_SIZE; i++) {
final[j++] = hexconvtab[hash[i] >> 4];
final[j++] = hexconvtab[hash[i] & 15];
}
final[j] = '\0';

strcpy(passwd, magic);
strcat(passwd, "$");
strncat(passwd, final, MD4_SIZE*2);

/* Don't leave anything around in vm they could use. */
memset(final, 0, sizeof(final));
buffer = stpcpy(buffer, magic);
*buffer++ = '$';
for (i = 0; i < MD4_SIZE; i++) {
*buffer++ = hexconvtab[hash[i] >> 4];
*buffer++ = hexconvtab[hash[i] & 15];
}
*buffer = '\0';

return (passwd);
return (0);
}
86 changes: 20 additions & 66 deletions lib/libcrypt/crypt-sha256.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,10 @@ static const char sha256_rounds_prefix[] = "rounds=";
/* Maximum number of rounds. */
#define ROUNDS_MAX 999999999

static char *
crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen)
int
crypt_sha256(const char *key, const char *salt, char *buffer)
{
u_long srounds;
int n;
uint8_t alt_result[32], temp_result[32];
SHA256_CTX ctx, alt_ctx;
size_t salt_len, key_len, cnt, rounds;
Expand Down Expand Up @@ -210,42 +209,27 @@ crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen)

/* Now we can construct the result string. It consists of three
* parts. */
cp = stpncpy(buffer, sha256_salt_prefix, MAX(0, buflen));
buflen -= sizeof(sha256_salt_prefix) - 1;
cp = stpcpy(buffer, sha256_salt_prefix);

if (rounds_custom) {
n = snprintf(cp, MAX(0, buflen), "%s%zu$",
sha256_rounds_prefix, rounds);
if (rounds_custom)
cp += sprintf(cp, "%s%zu$", sha256_rounds_prefix, rounds);

cp += n;
buflen -= n;
}
cp = stpncpy(cp, salt, salt_len);

cp = stpncpy(cp, salt, MIN((size_t)MAX(0, buflen), salt_len));
buflen -= MIN((size_t)MAX(0, buflen), salt_len);
*cp++ = '$';

if (buflen > 0) {
*cp++ = '$';
--buflen;
}

b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4, &buflen, &cp);
b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4, &buflen, &cp);
b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4, &buflen, &cp);
b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4, &buflen, &cp);
b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4, &buflen, &cp);
b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4, &buflen, &cp);
b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4, &buflen, &cp);
b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4, &buflen, &cp);
b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4, &buflen, &cp);
b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4, &buflen, &cp);
b64_from_24bit(0, alt_result[31], alt_result[30], 3, &buflen, &cp);
if (buflen <= 0) {
errno = ERANGE;
buffer = NULL;
}
else
*cp = '\0'; /* Terminate the string. */
b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4, &cp);
b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4, &cp);
b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4, &cp);
b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4, &cp);
b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4, &cp);
b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4, &cp);
b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4, &cp);
b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4, &cp);
b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4, &cp);
b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4, &cp);
b64_from_24bit(0, alt_result[31], alt_result[30], 3, &cp);
*cp = '\0'; /* Terminate the string. */

/* Clear the buffer for the intermediate result so that people
* attaching to processes or reading core dumps cannot get any
Expand All @@ -263,37 +247,7 @@ crypt_sha256_r(const char *key, const char *salt, char *buffer, int buflen)
if (copied_salt != NULL)
memset(copied_salt, '\0', salt_len);

return buffer;
}

/* This entry point is equivalent to crypt(3). */
char *
crypt_sha256(const char *key, const char *salt)
{
/* We don't want to have an arbitrary limit in the size of the
* password. We can compute an upper bound for the size of the
* result in advance and so we can prepare the buffer we pass to
* `crypt_sha256_r'. */
static char *buffer;
static int buflen;
int needed;
char *new_buffer;

needed = (sizeof(sha256_salt_prefix) - 1
+ sizeof(sha256_rounds_prefix) + 9 + 1
+ strlen(salt) + 1 + 43 + 1);

if (buflen < needed) {
new_buffer = (char *)realloc(buffer, needed);

if (new_buffer == NULL)
return NULL;

buffer = new_buffer;
buflen = needed;
}

return crypt_sha256_r(key, salt, buffer, buflen);
return (0);
}

#ifdef TEST
Expand Down

0 comments on commit 5f521d7

Please sign in to comment.