diff options
author | Zack Weinberg <zackw@panix.com> | 2017-10-02 09:22:10 -0400 |
---|---|---|
committer | Zack Weinberg <zackw@panix.com> | 2017-10-02 09:22:10 -0400 |
commit | 4856729bd75c78e4e1e4e35ab5afd7cef6c66afc (patch) | |
tree | c1d010716a13c7614331439c518b3d0f7f8a01b5 | |
parent | 185e42ea6d33b84a22ec11f1fa4755c8bd72ca16 (diff) | |
download | libxcrypt-4856729bd75c78e4e1e4e35ab5afd7cef6c66afc.tar.gz libxcrypt-4856729bd75c78e4e1e4e35ab5afd7cef6c66afc.tar.bz2 libxcrypt-4856729bd75c78e4e1e4e35ab5afd7cef6c66afc.zip |
Shrink crypt_data and increase CRYPT_(GENSALT_)OUTPUT_SIZE.
struct crypt_data reserved enough space for UFC-DES - a little more
than 128kB. We are no longer using UFC-DES, and none of the
currently-supported algorithms require nearly that much space. (Some
algorithms that we may want to support in the future, such as scrypt
and Argon2, require *enormously* more memory - gigabytes - but that
will require a new interface, so there is no point worrying about it
now. We reserve generous space for future expansion;
CRYPT_OUTPUT_SIZE and CRYPT_GENSALT_OUTPUT_SIZE are increased to 384
and 192 bytes respectively, and the size of crypt_data only drops to
32kB, of which the first 2kB is a documented interface, the next 8kB
is supplied to algorithm modules as scratch space (no algorithm
currently requires more than about half of this), and the remaining
22kB will be used by the patch coming up next.
This is the first of two planned API breaks. After this change, it is
definitely true that programs compiled against this libcrypt will not
work correctly with glibc's libcrypt, because their crypt_data will be
too small. However, programs compiled with glibc's libcrypt will
still work fine, because their crypt_data is larger than required and
this library doesn't actually pay any attention to its contents. (The
"initialized" field is still documented, because that requirement
might come back in the future, but we don't look at it now.) The
symbol versioning is already consistent with this -- existing binaries
use GLIBC_x.y symbols which we provide, new ones use XCRYPT_x.y
symbols which glibc doesn't provide.
There are a number of internal cleanups in this patch as well, since
we had to touch every algorithm module anyway: the algorithm modules
are no longer responsible for aligning their scratch space or for
erasing it after they are done with it, internal terminology has been
made consistent throughout ("phrase" or "passphrase" instead of
"password" or "key"; "setting" for the full setting string, "salt"
only for the actual salt component of the setting string; "rbytes" and
"nrbytes" instead of "input" and "size" for the random-byte string fed
to crypt_gensalt), and we take a few more steps in the direction of
consistently using unsigned char for internal processing.
* crypt-base.h (CRYPT_OUTPUT_SIZE): Increase to 384.
(CRYPT_GENSALT_OUTPUT_SIZE): Increase to 192.
(CRYPT_MAX_PASSPHRASE_SIZE): New macro (value 512).
(struct crypt_data): Contents completely changed. Now only 32kB.
(crypt, crypt_r, crypt_ra, crypt_rn)
(crypt_gensalt, crypt_gensalt_rn, crypt_gensalt_rn):
Improve documentation and formal parameter names.
* crypt.c: Improve formal parameter names throughout.
(struct crypt_internal, crypt_fn, gensalt_fn): New types.
(get_internal, call_crypt_fn): New functions.
(make_failure_token): If there is not enough space for the
full failure token, write as much of it as possible.
(do_crypt_rn, do_crypt_ra): Use call_crypt_fn.
(crypt_gensalt_rn): Make input/output buffer size checks more
thorough. Use make_failure_token.
* crypt-private.h: Don't include stddef.h.
(ALG_SPECIFIC_SIZE): New macro.
(all function prototypes): Update.
* crypt-bcrypt.c: Use unsigned char more. Add static_asserts
that CRYPT_OUTPUT_SIZE and ALG_SPECIFIC_SIZE are big enough.
Remove the 'output' field from struct BF_buffer.
(BF_get_data, BF_wipe_intermediate_data): Delete.
(crypt_bcrypt_rn, BF_gensalt): Update to match internal API change.
* crypt-des.c, crypt-md5.c, crypt-sha256.c, crypt-sha512.c:
Similarly.
* m4/zw_alignment.m4 (zw_C_MAX_ALIGN_T): New macro.
* configure.ac: Call it.
* crypt-symbols.h: Add fallback definition of max_align_t.
Include stddef.h and stdint.h here. Provide definitions of
MIN and MAX here.
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | crypt-base.h | 98 | ||||
-rw-r--r-- | crypt-bcrypt.c | 172 | ||||
-rw-r--r-- | crypt-des.c | 160 | ||||
-rw-r--r-- | crypt-gensalt.c | 179 | ||||
-rw-r--r-- | crypt-md5.c | 93 | ||||
-rw-r--r-- | crypt-private.h | 90 | ||||
-rw-r--r-- | crypt-sha256.c | 100 | ||||
-rw-r--r-- | crypt-sha512.c | 100 | ||||
-rw-r--r-- | crypt-symbols.h | 24 | ||||
-rw-r--r-- | crypt.c | 173 | ||||
-rw-r--r-- | m4/zw_alignment.m4 | 21 |
12 files changed, 656 insertions, 555 deletions
diff --git a/configure.ac b/configure.ac index 1bcee0d..d9f6bc4 100644 --- a/configure.ac +++ b/configure.ac @@ -110,6 +110,7 @@ fi # Checks for typedefs, structures, and compiler characteristics. zw_C_ALIGNAS zw_C_ALIGNOF +zw_C_MAX_ALIGN_T zw_C_STATIC_ASSERT # Checks for library functions. diff --git a/crypt-base.h b/crypt-base.h index 8377965..87b94e2 100644 --- a/crypt-base.h +++ b/crypt-base.h @@ -23,15 +23,24 @@ /*HEADER*/ /* The strings returned by crypt, crypt_r, crypt_rn, and crypt_ra will - be no longer than this. This is NOT the appropriate size to use in - allocating the buffer supplied to crypt_rn -- see below. */ -#define CRYPT_OUTPUT_SIZE (7 + 22 + 31 + 1) + be no longer than this, counting the terminating NUL. (Existing + algorithms all produce much shorter strings, but we have reserved + generous space for future expansion.) This is NOT the appropriate + size to use in allocating the buffer supplied to crypt_rn; use + sizeof (struct crypt_data) instead. */ +#define CRYPT_OUTPUT_SIZE 384 + +/* Passphrases longer than this (counting the terminating NUL) are not + supported. Note that some hash algorithms have lower limits. */ +#define CRYPT_MAX_PASSPHRASE_SIZE 512 /* The strings returned by crypt_gensalt, crypt_gensalt_rn, and crypt_gensalt_ra will be no longer than this. This IS the appropriate size to use when allocating the buffer supplied to - crypt_gensalt_rn. */ -#define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1) + crypt_gensalt_rn. (Again, existing algorithms all produce + much shorter strings, but we have reserved generous space for + future expansion.) */ +#define CRYPT_GENSALT_OUTPUT_SIZE 192 /* One-way hash the passphrase PHRASE as specified by SETTING, and return a string suitable for storage in a Unix-style "passwd" file. @@ -53,22 +62,51 @@ extern char *crypt (const char *__phrase, const char *__setting) __THROW __nonnull ((1, 2)); -/* Memory area used by crypt_r. - - Older versions of this library expected applications to set the - 'initialized' field of this structure to 0 before calling crypt_r - for the first time; this is no longer necessary, but the field is - preserved for compatibility's sake. */ +/* Memory area used by crypt_r. */ struct crypt_data { - char opaque[16 * 8 + 32768 * 4 + 14 + 2 + sizeof(long int) + sizeof(int)]; - int initialized; + /* crypt_r writes the hashed password to this field of its 'data' + argument. crypt_rn and crypt_ra do the same, treating the + untyped data area they are supplied with as this struct. */ + char output[CRYPT_OUTPUT_SIZE]; + + /* Applications are encouraged, but not required, to use this field + to store the "setting" string that must be passed to crypt_*. + Future extensions to the API may make this more ergonomic. + + A valid "setting" is either previously hashed password or the + string produced by one of the crypt_gensalt functions; see the + crypt_gensalt documentation for further details. */ + char setting[CRYPT_OUTPUT_SIZE]; + + /* Applications are encouraged, but not required, to use this field + to store the unhashed passphrase they will pass to crypt_*. + Future extensions to the API may make this more ergonomic. */ + char input[CRYPT_MAX_PASSPHRASE_SIZE]; + + /* This field should be set to 0 before calling crypt_r, crypt_rn, + or crypt_ra for the first time with a just-allocated + 'struct crypt_data'. This is not required if crypt_ra is allowed + to do the allocation itself (i.e. if the *DATA argument is a null + pointer). Future extensions to the API may make this more ergonomic. */ + char initialized; + + /* Reserved for future application-visible fields. For maximum + forward compatibility, applications should set this field to all + bytes zero before calling crypt_r, crypt_rn, or crypt_ra for the + first time with a just-allocated 'struct crypt_data'. Future + extensions to the API may make this more ergonomic. */ + char reserved[767]; + + /* Scratch space used internally. Applications should not read or + write this field. All data written to this area is erased before + returning from the library. */ + char internal[30720]; }; /* Thread-safe version of crypt. Instead of writing to a static - storage area, the string returned by this function will be - somewhere within the crypt_data object supplied as an argument. - Otherwise, behaves exactly the same as crypt. */ + storage area, the string returned by this function will be within + DATA->output. Otherwise, behaves exactly the same as crypt. */ extern char *crypt_r (const char *__phrase, const char *__setting, struct crypt_data *restrict __data) __THROW __nonnull ((1, 2, 3)); @@ -103,36 +141,40 @@ extern char *crypt_ra (const char *__phrase, const char *__setting, /* Generate a string suitable for use as the setting when hashing a new passphrase. PREFIX controls which hash function will be used, COUNT controls the computational cost of the hash (for functions - where this is tunable), and INPUT should point to SIZE bytes of + where this is tunable), and RBYTES should point to NRBYTES bytes of random data. - The string returned is stored in a statically-allocated buffer, - and will be overwritten if the function is called again. It is not + The string returned is stored in a statically-allocated buffer, and + will be overwritten if the function is called again. It is not safe to call this function from multiple threads concurrently. - However, it is safe to pass the string to crypt without copying it - first; the two functions use separate buffers. + However, within a single thread, it is safe to pass the string as + the SETTING argument to crypt without copying it first; the two + functions use separate buffers. If an error occurs (e.g. a prefix that does not correspond to a supported hash function, or an inadequate amount of random data), this function returns a null pointer. */ extern char *crypt_gensalt (const char *__prefix, unsigned long __count, - const char *__input, int __size) + const char *__rbytes, int __nrbytes) __THROW __nonnull ((1, 3)); /* Thread-safe version of crypt_gensalt; instead of a statically-allocated buffer, the generated setting string is written to OUTPUT, which is OUTPUT_SIZE bytes long. OUTPUT_SIZE - must be at least CRYPT_GENSALT_OUTPUT_SIZE (see above). */ + must be at least CRYPT_GENSALT_OUTPUT_SIZE (see above). + + If an error occurs, this function returns a null pointer and writes + a string that does not correspond to any valid setting into OUTPUT. */ extern char *crypt_gensalt_rn (const char *__prefix, unsigned long __count, - const char *__input, int __size, + const char *__rbytes, int __nrbytes, char *__output, int __output_size) __THROW __nonnull ((1, 5)); -/* Another thread-safe version of crypt_gensalt; the string returned - is in storage allocated by malloc, and should be deallocated with - free when it is no longer needed. */ +/* Another thread-safe version of crypt_gensalt; the generated setting + string is in storage allocated by malloc, and should be deallocated + with free when it is no longer needed. */ extern char *crypt_gensalt_ra (const char *__prefix, unsigned long __count, - const char *__input, int __size) + const char *__rbytes, int __nrbytes) __THROW __nonnull ((1)); /*TRAILER*/ diff --git a/crypt-bcrypt.c b/crypt-bcrypt.c index 5d16214..be94f59 100644 --- a/crypt-bcrypt.c +++ b/crypt-bcrypt.c @@ -412,11 +412,11 @@ BF_decode (BF_word * dst, const char *src, int size) } static void -BF_encode (char *dst, const BF_word * src, int size) +BF_encode (unsigned char *dst, const BF_word * src, int size) { const unsigned char *sptr = (const unsigned char *) src; const unsigned char *end = sptr + size; - unsigned char *dptr = (unsigned char *) dst; + unsigned char *dptr = dst; unsigned int c1, c2; do @@ -671,6 +671,9 @@ static const unsigned char flags_by_subtype[26] = { terminator: 1 char */ #define BF_HASH_LENGTH (BF_SETTING_LENGTH + 31 + 1) +static_assert (BF_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for bcrypt"); + /* BF_data holds all of the sensitive intermediate data used by BF_crypt. */ struct BF_data @@ -684,47 +687,30 @@ struct BF_data } binary; }; -/* A BF_buffer holds the output plus all of the sensitive intermediate - data. It may have been allocated by application code, so it may - not be properly aligned, and besides which BF_HASH_LENGTH may be - odd, so we pad 'databuf' enough to find a properly-aligned BF_data - object within. The extra two characters' space in tmp_output are - used by the self-test -- see below. */ +/* A BF_buffer holds a BF_data plus two extra output buffers used by + the self-test logic. One of them is slightly overlength so the + self-test can verify that BF_crypt emits exactly BF_HASH_LENGTH + bytes and no more. */ struct BF_buffer { - char output[BF_HASH_LENGTH]; - char tmp_output[BF_HASH_LENGTH + 2]; - uint8_t databuf[sizeof (struct BF_data) + alignof (struct BF_data)]; + struct BF_data data; + unsigned char re_output[BF_HASH_LENGTH]; + unsigned char st_output[BF_HASH_LENGTH + 2]; }; -static inline struct BF_data * -BF_get_data (struct BF_buffer *buf) -{ - uintptr_t datp = (uintptr_t) &buf->databuf; - uintptr_t align = alignof (struct BF_data); - datp = (datp + align - 1) & ~align; - return (struct BF_data *)datp; -} +static_assert (sizeof (struct BF_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for bcrypt"); -static inline void -BF_wipe_intermediate_data (struct BF_buffer *buf) -{ - memset (((char *)buf) + offsetof (struct BF_buffer, tmp_output), - 0, - sizeof (struct BF_buffer) - offsetof (struct BF_buffer, tmp_output)); -} -static char * -BF_crypt (const char *key, const char *setting, - struct BF_buffer *buffer, BF_word min) +static unsigned char * +BF_crypt (const char *key, const char *setting, unsigned char *output, + struct BF_data *data, BF_word min) { - struct BF_data *data = BF_get_data (buffer); BF_word L, R; BF_word tmp1, tmp2, tmp3, tmp4; BF_word *ptr; BF_word count; int i; - char *output = buffer->tmp_output; if (setting[0] != '$' || setting[1] != '2' || @@ -835,9 +821,9 @@ BF_crypt (const char *key, const char *setting, memcpy (output, setting, BF_SETTING_LENGTH - 1); output[BF_SETTING_LENGTH - 1] = - (char)BF_itoa64[(int) - BF_atoi64[(int) setting[BF_SETTING_LENGTH - 1] - - 0x20] & 0x30]; + BF_itoa64[(int) + BF_atoi64[(int) setting[BF_SETTING_LENGTH - 1] - + 0x20] & 0x30]; /* This has to be bug-compatible with the original implementation, so * only encode 23 of the 24 bytes. :-) */ @@ -868,28 +854,25 @@ BF_crypt (const char *key, const char *setting, * The performance cost of this quick self-test is around 0.6% at the "$2a$08" * setting. */ -char * +uint8_t * crypt_bcrypt_rn (const char *key, const char *setting, - char *data, size_t size) + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for a BF_buffer in DATA. */ - if (size < sizeof (struct BF_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < BF_HASH_LENGTH || s_size < sizeof (struct BF_buffer)) { errno = ERANGE; - return NULL; + return 0; } - struct BF_buffer *buffer = (struct BF_buffer *)data; + struct BF_buffer *buffer = scratch; /* Hash the supplied password */ - char *rv = BF_crypt (key, setting, buffer, 16); + uint8_t *rv = BF_crypt (key, setting, buffer->re_output, &buffer->data, 16); if (!rv) - { - BF_wipe_intermediate_data (buffer); - return 0; - } + return 0; /* errno has already been set */ - /* Preserve the output and the current value of errno. */ - memcpy (buffer->output, buffer->tmp_output, BF_HASH_LENGTH); + /* Save and restore the current value of errno around the self-test. */ int save_errno = errno; /* Do a quick self-test. It is important that we make both calls to @@ -908,20 +891,20 @@ crypt_bcrypt_rn (const char *key, const char *setting, char test_setting[BF_SETTING_LENGTH]; unsigned int flags = flags_by_subtype[(unsigned int) (unsigned char) setting[2] - 'a']; - const char *p; + uint8_t *p; int ok; memcpy (test_setting, test_setting_init, BF_SETTING_LENGTH + 1); test_hash = test_hashes[flags & 1]; test_setting[2] = setting[2]; - memset (buffer->tmp_output, 0x55, sizeof buffer->tmp_output); - p = BF_crypt (test_key, test_setting, buffer, 1); + memset (buffer->st_output, 0x55, sizeof buffer->st_output); + p = BF_crypt (test_key, test_setting, buffer->st_output, &buffer->data, 1); - ok = (p == buffer->tmp_output && + ok = (p == buffer->st_output && !memcmp (p, test_setting, BF_SETTING_LENGTH) && !memcmp (p + BF_SETTING_LENGTH, test_hash, - sizeof buffer->tmp_output - (BF_SETTING_LENGTH + 1))); + sizeof buffer->st_output - (BF_SETTING_LENGTH + 1))); /* Do a second self-test of the key-expansion "safety" logic. */ { @@ -934,92 +917,85 @@ crypt_bcrypt_rn (const char *key, const char *setting, !memcmp (ae, ye, sizeof (ae)) && !memcmp (ai, yi, sizeof (ai)); } - if (ok) - { - errno = save_errno; - rv = buffer->output; - } - else + if (!ok) { - /* self-test failed; pretend we don't support this hash type */ + /* Self-test failed; pretend we don't support this hash type. */ errno = EINVAL; rv = 0; } - BF_wipe_intermediate_data (buffer); - return rv; + /* Self-test succeeded; copy the true output into the true output + buffer and return. We already know there is enough space. */ + memcpy (output, buffer->re_output, BF_HASH_LENGTH); + errno = save_errno; + return output; } -static char * +static uint8_t * BF_gensalt (char subtype, unsigned long count, - const char *input, int size, char *output, int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size) { - BF_word aligned_input[16 / sizeof(BF_word)]; - memcpy(aligned_input, input, 16); - - if (output_size < 7 + 22 + 1) - { - errno = ERANGE; - if (output_size > 0) - output[0] = '\0'; - return NULL; - } - if (!count) count = 5; - - if (size < 16 || + if (nrbytes < 16 || count < 4 || count > 31 || (subtype != 'a' && subtype != 'b' && subtype != 'x' && subtype != 'y')) { errno = EINVAL; - if (output_size > 0) - output[0] = '\0'; return NULL; } + if (o_size < 7 + 22 + 1) + { + errno = ERANGE; + return NULL; + } + + BF_word aligned_rbytes[16 / sizeof(BF_word)]; + memcpy(aligned_rbytes, rbytes, 16); output[0] = '$'; output[1] = '2'; - output[2] = subtype; + output[2] = (uint8_t)subtype; output[3] = '$'; - output[4] = (char)('0' + count / 10); - output[5] = (char)('0' + count % 10); + output[4] = (uint8_t)('0' + count / 10); + output[5] = (uint8_t)('0' + count % 10); output[6] = '$'; - BF_encode (&output[7], aligned_input, 16); + BF_encode (&output[7], aligned_rbytes, 16); output[7 + 22] = '\0'; return output; } -char * +uint8_t * gensalt_bcrypt_a_rn (unsigned long count, - const char *input, int input_size, - char *output, int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size) { - return BF_gensalt ('a', count, input, input_size, output, output_size); + return BF_gensalt ('a', count, rbytes, nrbytes, output, o_size); } -char * +uint8_t * gensalt_bcrypt_b_rn (unsigned long count, - const char *input, int input_size, - char *output, int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size) { - return BF_gensalt ('b', count, input, input_size, output, output_size); + return BF_gensalt ('b', count, rbytes, nrbytes, output, o_size); } -char * +uint8_t * gensalt_bcrypt_x_rn (unsigned long count, - const char *input, int input_size, - char *output, int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size) { - return BF_gensalt ('x', count, input, input_size, output, output_size); + return BF_gensalt ('x', count, rbytes, nrbytes, output, o_size); } -char * +uint8_t * gensalt_bcrypt_y_rn (unsigned long count, - const char *input, int input_size, - char *output, int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size) { - return BF_gensalt ('y', count, input, input_size, output, output_size); + return BF_gensalt ('y', count, rbytes, nrbytes, output, o_size); } diff --git a/crypt-des.c b/crypt-des.c index d6a56ea..4708a0f 100644 --- a/crypt-des.c +++ b/crypt-des.c @@ -54,40 +54,25 @@ #define DES_EXT_OUTPUT_LEN 21 /* _CCCCSSSShhhhhhhhhhh0 */ #define DES_BIG_OUTPUT_LEN ((16*11) + 2 + 1) /* SS (hhhhhhhhhhh){1,16} 0 */ -#define MAX(x,y) ((x)>(y)?(x):(y)) - #define DES_MAX_OUTPUT_LEN \ MAX (DES_TRD_OUTPUT_LEN, MAX (DES_EXT_OUTPUT_LEN, DES_BIG_OUTPUT_LEN)) -/* A des_buffer holds the output plus all of the sensitive intermediate - data. It may have been allocated by application code, so it may not - be properly aligned, and besides which DES_MAX_OUTPUT_LEN may be odd, - so we pad 'ctxbuf' enough to find a properly-aligned des_ctx within. */ +static_assert (DES_MAX_OUTPUT_LEN <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for DES"); + +/* A des_buffer holds all of the sensitive intermediate data used by + crypt_des_*. */ struct des_buffer { - char output[DES_MAX_OUTPUT_LEN]; + struct des_ctx ctx; uint8_t keybuf[8]; uint8_t pkbuf[8]; - uint8_t ctxbuf[sizeof (struct des_ctx) + alignof (struct des_ctx)]; }; -static inline struct des_ctx * -des_get_ctx (struct des_buffer *buf) -{ - uintptr_t ctxp = (uintptr_t) &buf->ctxbuf; - uintptr_t align = alignof (struct des_ctx); - ctxp = (ctxp + align - 1) & ~align; - return (struct des_ctx *)ctxp; -} +static_assert (sizeof (struct des_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for DES"); -static inline void -des_wipe_intermediate_data (struct des_buffer *buf) -{ - memset (((char *)buf) + offsetof (struct des_buffer, keybuf), - 0, - sizeof (struct des_buffer) - offsetof (struct des_buffer, keybuf)); -} static const uint8_t ascii64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -117,7 +102,7 @@ ascii_to_bin(char ch) set. The plaintext is 64 bits of zeroes, and the raw ciphertext is written to cbuf[]. */ static void -des_gen_hash (struct des_ctx *ctx, uint32_t count, unsigned char *output, +des_gen_hash (struct des_ctx *ctx, uint32_t count, uint8_t *output, uint8_t cbuf[8]) { uint8_t plaintext[8]; @@ -125,8 +110,8 @@ des_gen_hash (struct des_ctx *ctx, uint32_t count, unsigned char *output, des_crypt_block (ctx, cbuf, plaintext, count, false); /* Now encode the result. */ - const unsigned char *sptr = cbuf; - const unsigned char *end = sptr + 8; + const uint8_t *sptr = cbuf; + const uint8_t *end = sptr + 8; unsigned int c1, c2; do @@ -160,25 +145,26 @@ des_gen_hash (struct des_ctx *ctx, uint32_t count, unsigned char *output, } /* The original UNIX DES-based password hash, no extensions. */ -static char * -crypt_des_trd_rn (const char *key, const char *setting, - char *data, size_t size) +static uint8_t * +crypt_des_trd_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for a des_buffer in DATA. */ - if (size < sizeof (struct des_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < DES_TRD_OUTPUT_LEN || s_size < sizeof (struct des_buffer)) { errno = ERANGE; return 0; } - struct des_buffer *buf = (struct des_buffer *)data; - struct des_ctx *ctx = des_get_ctx (buf); + struct des_buffer *buf = scratch; + struct des_ctx *ctx = &buf->ctx; uint32_t salt = 0; uint8_t *keybuf = buf->keybuf, *pkbuf = buf->pkbuf; - unsigned char *output = (unsigned char *)buf->output; + uint8_t *cp = output; int i; - /* "old"-style: setting - 2 bytes of salt, key - up to 8 characters. + /* "old"-style: setting - 2 bytes of salt, phrase - up to 8 characters. Note: ascii_to_bin maps all byte values outside the ascii64 alphabet to zero. Do not read past the end of the string. */ salt = ascii_to_bin (setting[0]); @@ -190,23 +176,22 @@ crypt_des_trd_rn (const char *key, const char *setting, might be catastrophically malformed (e.g. a 0- or 1-byte string; this could plausibly happen if e.g. login(8) doesn't special-case "*" or "!" in the password database). */ - *output++ = ascii64[salt & 0x3f]; - *output++ = ascii64[(salt >> 6) & 0x3f]; + *cp++ = ascii64[salt & 0x3f]; + *cp++ = ascii64[(salt >> 6) & 0x3f]; /* Copy the first 8 characters of the password into keybuf, shifting each character up by 1 bit and padding on the right with zeroes. */ for (i = 0; i < 8; i++) { - keybuf[i] = (uint8_t)(*key << 1); - if (*key) - key++; + keybuf[i] = (uint8_t)(*phrase << 1); + if (*phrase) + phrase++; } des_set_key (ctx, keybuf); des_set_salt (ctx, salt); - des_gen_hash (ctx, 25, output, pkbuf); - des_wipe_intermediate_data (buf); - return buf->output; + des_gen_hash (ctx, 25, cp, pkbuf); + return output; } /* This algorithm is algorithm 0 (default) shipped with the C2 secure @@ -225,22 +210,23 @@ crypt_des_trd_rn (const char *key, const char *setting, (that is, the password can be no more than 128 characters long). Andy Phillips <atp@mssl.ucl.ac.uk> */ -static char * -crypt_des_big_rn (const char *key, const char *setting, - char *data, size_t size) +static uint8_t * +crypt_des_big_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for a des_buffer in DATA. */ - if (size < sizeof (struct des_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < DES_BIG_OUTPUT_LEN || s_size < sizeof (struct des_buffer)) { errno = ERANGE; return 0; } - struct des_buffer *buf = (struct des_buffer *)data; - struct des_ctx *ctx = des_get_ctx (buf); + struct des_buffer *buf = scratch; + struct des_ctx *ctx = &buf->ctx; uint32_t salt = 0; uint8_t *keybuf = buf->keybuf, *pkbuf = buf->pkbuf; - unsigned char *output = (unsigned char *)buf->output; + uint8_t *cp = output; int i, seg; /* The setting string is exactly the same as for a traditional DES @@ -249,59 +235,58 @@ crypt_des_big_rn (const char *key, const char *setting, if (setting[0]) salt |= ascii_to_bin (setting[1]) << 6; - *output++ = ascii64[salt & 0x3f]; - *output++ = ascii64[(salt >> 6) & 0x3f]; + *cp++ = ascii64[salt & 0x3f]; + *cp++ = ascii64[(salt >> 6) & 0x3f]; for (seg = 0; seg < 16; seg++) { /* Copy and shift each block as for the traditional DES. */ for (i = 0; i < 8; i++) { - keybuf[i] = (uint8_t)(*key << 1); - if (*key) - key++; + keybuf[i] = (uint8_t)(*phrase << 1); + if (*phrase) + phrase++; } des_set_key (ctx, keybuf); des_set_salt (ctx, salt); - des_gen_hash (ctx, 25, output, pkbuf); + des_gen_hash (ctx, 25, cp, pkbuf); - if (*key == 0) + if (*phrase == 0) break; /* change the salt (1st 2 chars of previous block) - this was found by dowsing */ - salt = ascii_to_bin ((char)output[0]); - salt |= ascii_to_bin ((char)output[1]) << 6; - output += 11; + salt = ascii_to_bin ((char)cp[0]); + salt |= ascii_to_bin ((char)cp[1]) << 6; + cp += 11; } - des_wipe_intermediate_data (buf); - return buf->output; + return output; } /* crypt_rn() entry point for both the original UNIX password hash, with its 8-character length limit, and the Digital UNIX "bigcrypt" extension to permit longer passwords. */ -char * -crypt_des_trd_or_big_rn (const char *key, const char *salt, - char *data, size_t size) +uint8_t * +crypt_des_trd_or_big_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - if (strlen (salt) > 13) - return crypt_des_big_rn (key, salt, data, size); - else - return crypt_des_trd_rn (key, salt, data, size); + return (strlen (setting) > 13 ? crypt_des_big_rn : crypt_des_trd_rn) + (phrase, setting, output, o_size, scratch, s_size); } /* crypt_rn() entry point for BSD-style extended DES hashes. These permit long passwords and have more salt and a controllable iteration count, but are still unacceptably weak by modern standards. */ -char * -crypt_des_xbsd_rn (const char *key, const char *setting, - char *data, size_t size) +uint8_t * +crypt_des_xbsd_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for a des_buffer in DATA. */ - if (size < sizeof (struct des_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < DES_EXT_OUTPUT_LEN || s_size < sizeof (struct des_buffer)) { errno = ERANGE; return 0; @@ -314,16 +299,16 @@ crypt_des_xbsd_rn (const char *key, const char *setting, return 0; } - struct des_buffer *buf = (struct des_buffer *)data; - struct des_ctx *ctx = des_get_ctx (buf); + struct des_buffer *buf = scratch; + struct des_ctx *ctx = &buf->ctx; uint32_t count = 0, salt = 0; uint8_t *keybuf = buf->keybuf, *pkbuf = buf->pkbuf; - unsigned char *output = (unsigned char *)buf->output; + uint8_t *cp = output; int i; /* "new"-style DES hash: setting - underscore, 4 bytes of count, 4 bytes of salt - key - unlimited characters + phrase - unlimited characters */ for (i = 1; i < 5; i++) count |= ascii_to_bin(setting[i]) << ((i - 1) * 6); @@ -331,8 +316,8 @@ crypt_des_xbsd_rn (const char *key, const char *setting, for (i = 5; i < 9; i++) salt |= ascii_to_bin(setting[i]) << ((i - 5) * 6); - memcpy (output, setting, 9); - output += 9; + memcpy (cp, setting, 9); + cp += 9; /* Fold passwords longer than 8 bytes into a single DES key using a procedure similar to a Merkle-Dåmgard hash construction. Each @@ -346,19 +331,18 @@ crypt_des_xbsd_rn (const char *key, const char *setting, { for (i = 0; i < 8; i++) { - keybuf[i] = (uint8_t)(pkbuf[i] ^ (*key << 1)); - if (*key) - key++; + keybuf[i] = (uint8_t)(pkbuf[i] ^ (*phrase << 1)); + if (*phrase) + phrase++; } des_set_key (ctx, keybuf); - if (*key == 0) + if (*phrase == 0) break; des_crypt_block (ctx, pkbuf, keybuf, 1, false); } /* Proceed as for the traditional DES hash. */ des_set_salt (ctx, salt); - des_gen_hash (ctx, count, output, pkbuf); - des_wipe_intermediate_data (buf); - return buf->output; + des_gen_hash (ctx, count, cp, pkbuf); + return output; } diff --git a/crypt-gensalt.c b/crypt-gensalt.c index 0abdaaf..18c47fd 100644 --- a/crypt-gensalt.c +++ b/crypt-gensalt.c @@ -14,100 +14,115 @@ #include "crypt-private.h" -static const char _xcrypt_itoa64[64 + 1] = +static const unsigned char _xcrypt_itoa64[64 + 1] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; #if ENABLE_WEAK_HASHES -char * +uint8_t * gensalt_des_trd_rn (unsigned long count, - const char *input, int size, char *output, - int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size) { - if (size < 2 || output_size < 2 + 1 || (count && count != 25)) + if (output_size < 3) { - if (output_size > 0) - output[0] = '\0'; - errno = ((output_size < 2 + 1) ? ERANGE : EINVAL); + errno = ERANGE; + return NULL; + } + + if (nrbytes < 2 || (count != 0 && count != 25)) + { + errno = EINVAL; return NULL; } - output[0] = _xcrypt_itoa64[(unsigned int) input[0] & 0x3f]; - output[1] = _xcrypt_itoa64[(unsigned int) input[1] & 0x3f]; + output[0] = _xcrypt_itoa64[(unsigned int) rbytes[0] & 0x3f]; + output[1] = _xcrypt_itoa64[(unsigned int) rbytes[1] & 0x3f]; output[2] = '\0'; return output; } -char * +uint8_t * gensalt_des_xbsd_rn (unsigned long count, - const char *input, int size, char *output, - int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size) { - unsigned long value; - -/* Even iteration counts make it easier to detect weak DES keys from a look - * at the hash, so they should be avoided */ - if (size < 3 || output_size < 1 + 4 + 4 + 1 || - (count && (count > 0xffffff || !(count & 1)))) + if (output_size < 1 + 4 + 4 + 1) { - if (output_size > 0) - output[0] = '\0'; - errno = ((output_size < 1 + 4 + 4 + 1) ? ERANGE : EINVAL); + errno = ERANGE; return NULL; } - if (!count) + if (count == 0) count = 725; + /* Even iteration counts make it easier to detect weak DES keys from a look + at the hash, so they should be avoided. */ + if (nrbytes < 3 || count > 0xffffff || count % 2 == 0) + { + errno = EINVAL; + return NULL; + } + + unsigned long value = + ((unsigned long) (unsigned char) rbytes[0] << 0) | + ((unsigned long) (unsigned char) rbytes[1] << 8) | + ((unsigned long) (unsigned char) rbytes[2] << 16); + output[0] = '_'; - output[1] = _xcrypt_itoa64[count & 0x3f]; - output[2] = _xcrypt_itoa64[(count >> 6) & 0x3f]; + + output[1] = _xcrypt_itoa64[(count >> 0) & 0x3f]; + output[2] = _xcrypt_itoa64[(count >> 6) & 0x3f]; output[3] = _xcrypt_itoa64[(count >> 12) & 0x3f]; output[4] = _xcrypt_itoa64[(count >> 18) & 0x3f]; - value = (unsigned long) (unsigned char) input[0] | - ((unsigned long) (unsigned char) input[1] << 8) | - ((unsigned long) (unsigned char) input[2] << 16); - output[5] = _xcrypt_itoa64[value & 0x3f]; - output[6] = _xcrypt_itoa64[(value >> 6) & 0x3f]; + + output[5] = _xcrypt_itoa64[(value >> 0) & 0x3f]; + output[6] = _xcrypt_itoa64[(value >> 6) & 0x3f]; output[7] = _xcrypt_itoa64[(value >> 12) & 0x3f]; output[8] = _xcrypt_itoa64[(value >> 18) & 0x3f]; + output[9] = '\0'; return output; } -char * -gensalt_md5_rn (unsigned long ARG_UNUSED (count), - const char *input, int size, - char *output, int output_size) +uint8_t * +gensalt_md5_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size) { unsigned long value; - if (size < 3 || output_size < 3 + 4 + 1) + if (output_size < 3 + 4 + 1) { - if (output_size > 0) - output[0] = '\0'; errno = ERANGE; return NULL; } + if (nrbytes < 3 || (count != 0 && count != 1000)) + { + errno = EINVAL; + return NULL; + } + output[0] = '$'; output[1] = '1'; output[2] = '$'; - value = (unsigned long) (unsigned char) input[0] | - ((unsigned long) (unsigned char) input[1] << 8) | - ((unsigned long) (unsigned char) input[2] << 16); + + value = (unsigned long) (unsigned char) rbytes[0] | + ((unsigned long) (unsigned char) rbytes[1] << 8) | + ((unsigned long) (unsigned char) rbytes[2] << 16); output[3] = _xcrypt_itoa64[value & 0x3f]; output[4] = _xcrypt_itoa64[(value >> 6) & 0x3f]; output[5] = _xcrypt_itoa64[(value >> 12) & 0x3f]; output[6] = _xcrypt_itoa64[(value >> 18) & 0x3f]; output[7] = '\0'; - if (size >= 6 && output_size >= 3 + 4 + 4 + 1) + if (nrbytes >= 6 && output_size >= 3 + 4 + 4 + 1) { - value = (unsigned long) (unsigned char) input[3] | - ((unsigned long) (unsigned char) input[4] << 8) | - ((unsigned long) (unsigned char) input[5] << 16); + value = (unsigned long) (unsigned char) rbytes[3] | + ((unsigned long) (unsigned char) rbytes[4] << 8) | + ((unsigned long) (unsigned char) rbytes[5] << 16); output[7] = _xcrypt_itoa64[value & 0x3f]; output[8] = _xcrypt_itoa64[(value >> 6) & 0x3f]; output[9] = _xcrypt_itoa64[(value >> 12) & 0x3f]; @@ -119,42 +134,41 @@ gensalt_md5_rn (unsigned long ARG_UNUSED (count), } #endif -static char * +static uint8_t * gensalt_sha_rn (char tag, unsigned long count, - const char *input, int size, - char *output, int output_size) + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size) { unsigned long value; - char raw_salt[9]; + unsigned char raw_salt[9]; int written; - if (output_size < 1) + if (output_size < 3 + 4 + 1) { errno = ERANGE; return NULL; } - if (output_size < 7) + + if (nrbytes < 3 || count > 999999999) { - output[0] = '\0'; - errno = ERANGE; + errno = EINVAL; return NULL; } - /* at this point, output_size is known to be positive */ - value = (unsigned long) (unsigned char) input[0] | - ((unsigned long) (unsigned char) input[1] << 8) | - ((unsigned long) (unsigned char) input[2] << 16); + value = (unsigned long) (unsigned char) rbytes[0] | + ((unsigned long) (unsigned char) rbytes[1] << 8) | + ((unsigned long) (unsigned char) rbytes[2] << 16); raw_salt[0] = _xcrypt_itoa64[value & 0x3f]; raw_salt[1] = _xcrypt_itoa64[(value >> 6) & 0x3f]; raw_salt[2] = _xcrypt_itoa64[(value >> 12) & 0x3f]; raw_salt[3] = _xcrypt_itoa64[(value >> 18) & 0x3f]; raw_salt[4] = '\0'; - if (size >= 6) + if (nrbytes >= 6) { - value = (unsigned long) (unsigned char) input[3] | - ((unsigned long) (unsigned char) input[4] << 8) | - ((unsigned long) (unsigned char) input[5] << 16); + value = (unsigned long) (unsigned char) rbytes[3] | + ((unsigned long) (unsigned char) rbytes[4] << 8) | + ((unsigned long) (unsigned char) rbytes[5] << 16); raw_salt[4] = _xcrypt_itoa64[value & 0x3f]; raw_salt[5] = _xcrypt_itoa64[(value >> 6) & 0x3f]; raw_salt[6] = _xcrypt_itoa64[(value >> 12) & 0x3f]; @@ -164,9 +178,10 @@ gensalt_sha_rn (char tag, unsigned long count, if (count > 0) { - written = snprintf (output, (size_t)output_size, "$%c$rounds=%lu$%s", + written = snprintf ((char *)output, (size_t)output_size, + "$%c$rounds=%lu$%s", tag, count, raw_salt); - if (written > 0 && written <= output_size) + if (written > 0 && (size_t)written <= output_size) return output; if (raw_salt[4] != '\0') @@ -174,17 +189,18 @@ gensalt_sha_rn (char tag, unsigned long count, /* The output didn't fit. Try truncating the salt to four characters. */ raw_salt[4] = '\0'; - written = snprintf (output, (size_t)output_size, "$%c$rounds=%lu$%s", + written = snprintf ((char *)output, (size_t)output_size, + "$%c$rounds=%lu$%s", tag, count, raw_salt); - if (written > 0 && written <= output_size) + if (written > 0 && (size_t)written <= output_size) return output; } } else { - written = snprintf (output, (size_t)output_size, "$%c$%s", - tag, raw_salt); - if (written > 0 && written <= output_size) + written = snprintf ((char *)output, (size_t)output_size, + "$%c$%s", tag, raw_salt); + if (written > 0 && (size_t)written <= output_size) return output; if (raw_salt[4] != '\0') @@ -192,28 +208,33 @@ gensalt_sha_rn (char tag, unsigned long count, /* The output didn't fit. Try truncating the salt to four characters. */ raw_salt[4] = '\0'; - written = snprintf (output, (size_t)output_size, "$%c$%s", - tag, raw_salt); - if (written > 0 && written <= output_size) + written = snprintf ((char *)output, (size_t)output_size, + "$%c$%s", tag, raw_salt); + if (written > 0 && (size_t)written <= output_size) return output; } } - output[0] = '\0'; + /* we know we do have enough space for this */ + output[0] = '*'; + output[1] = '0'; + output[2] = '\0'; errno = ERANGE; return NULL; } -char * -gensalt_sha256_rn (unsigned long count, const char *input, int size, - char *output, int output_size) +uint8_t * +gensalt_sha256_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size) { - return gensalt_sha_rn ('5', count, input, size, output, output_size); + return gensalt_sha_rn ('5', count, rbytes, nrbytes, output, output_size); } -char * -gensalt_sha512_rn (unsigned long count, const char *input, int size, - char *output, int output_size) +uint8_t * +gensalt_sha512_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size) { - return gensalt_sha_rn ('6', count, input, size, output, output_size); + return gensalt_sha_rn ('6', count, rbytes, nrbytes, output, output_size); } diff --git a/crypt-md5.c b/crypt-md5.c index 106ddb9..166a331 100644 --- a/crypt-md5.c +++ b/crypt-md5.c @@ -46,53 +46,42 @@ static const char b64t[64] = #define MD5_HASH_LENGTH \ (sizeof (md5_salt_prefix) + SALT_LEN_MAX + 1 + 22) -/* An md5_buffer holds the output plus all of the sensitive intermediate - data. It may have been allocated by application code, so it may not - be properly aligned, and besides which MD5_HASH_LENGTH may be odd, so - we pad 'ctxbuf' enough to find a properly-aligned md5_ctx within. */ +static_assert (MD5_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for MD5"); + +/* An md5_buffer holds all of the sensitive intermediate data. */ struct md5_buffer { - char output[MD5_HASH_LENGTH]; - uint8_t ctxbuf[sizeof (struct md5_ctx) + alignof (struct md5_ctx)]; + struct md5_ctx ctx; uint8_t result[16]; }; -static inline struct md5_ctx * -md5_get_ctx (struct md5_buffer *buf) -{ - uintptr_t ctxp = (uintptr_t) &buf->ctxbuf; - uintptr_t align = alignof (struct md5_ctx); - ctxp = (ctxp + align - 1) & ~align; - return (struct md5_ctx *)ctxp; -} +static_assert (sizeof (struct md5_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for MD5"); -static inline void -md5_wipe_intermediate_data (struct md5_buffer *buf) -{ - memset (((char *)buf) + offsetof (struct md5_buffer, ctxbuf), - 0, - sizeof (struct md5_buffer) - offsetof (struct md5_buffer, ctxbuf)); -} /* This entry point is equivalent to the `crypt' function in Unix libcs. */ -char * -crypt_md5_rn (const char *key, const char *salt, - char *data, size_t size) +uint8_t * +crypt_md5_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for an md5_buffer in DATA. */ - if (size < sizeof (struct md5_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < MD5_HASH_LENGTH || s_size < sizeof (struct md5_buffer)) { errno = ERANGE; return 0; } - struct md5_buffer *buf = (struct md5_buffer *)data; - struct md5_ctx *ctx = md5_get_ctx (buf); + struct md5_buffer *buf = scratch; + struct md5_ctx *ctx = &buf->ctx; uint8_t *result = buf->result; - char *cp = buf->output; + char *cp = (char *)output; + const char *salt = setting; + size_t salt_len; - size_t key_len; + size_t phrase_len; size_t cnt; /* Find beginning of salt string. The prefix should normally always @@ -104,20 +93,20 @@ crypt_md5_rn (const char *key, const char *salt, salt_len = strcspn (salt, "$"); if (salt_len > SALT_LEN_MAX) salt_len = SALT_LEN_MAX; - key_len = strlen (key); + phrase_len = strlen (phrase); - /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The + /* Compute alternate MD5 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ md5_init_ctx (ctx); - /* Add key. */ - md5_process_bytes (key, key_len, ctx); + /* Add phrase. */ + md5_process_bytes (phrase, phrase_len, ctx); /* Add salt. */ md5_process_bytes (salt, salt_len, ctx); - /* Add key again. */ - md5_process_bytes (key, key_len, ctx); + /* Add phrase again. */ + md5_process_bytes (phrase, phrase_len, ctx); /* Now get result of this (16 bytes). */ md5_finish_ctx (ctx, result); @@ -125,8 +114,8 @@ crypt_md5_rn (const char *key, const char *salt, /* Prepare for the real work. */ md5_init_ctx (ctx); - /* Add the key string. */ - md5_process_bytes (key, key_len, ctx); + /* Add the phrase string. */ + md5_process_bytes (phrase, phrase_len, ctx); /* Because the SALT argument need not always have the salt prefix we add it separately. */ @@ -138,8 +127,8 @@ crypt_md5_rn (const char *key, const char *salt, md5_process_bytes (salt, salt_len, ctx); - /* Add for any character in the key one byte of the alternate sum. */ - for (cnt = key_len; cnt > 16; cnt -= 16) + /* Add for any character in the phrase one byte of the alternate sum. */ + for (cnt = phrase_len; cnt > 16; cnt -= 16) md5_process_bytes (result, 16, ctx); md5_process_bytes (result, cnt, ctx); @@ -147,11 +136,11 @@ crypt_md5_rn (const char *key, const char *salt, *result = '\0'; /* The original implementation now does something weird: for every 1 - bit in the key the first 0 is added to the buffer, for every 0 - bit the first character of the key. This does not seem to be + bit in the phrase the first 0 is added to the buffer, for every 0 + bit the first character of the phrase. This does not seem to be what was intended but we have to follow this to be compatible. */ - for (cnt = key_len; cnt > 0; cnt >>= 1) - md5_process_bytes ((cnt & 1) != 0 ? (const char *) result : key, 1, + for (cnt = phrase_len; cnt > 0; cnt >>= 1) + md5_process_bytes ((cnt & 1) != 0 ? (const char *) result : phrase, 1, ctx); /* Create intermediate result. */ @@ -165,9 +154,9 @@ crypt_md5_rn (const char *key, const char *salt, /* New context. */ md5_init_ctx (ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - md5_process_bytes (key, key_len, ctx); + md5_process_bytes (phrase, phrase_len, ctx); else md5_process_bytes (result, 16, ctx); @@ -175,15 +164,15 @@ crypt_md5_rn (const char *key, const char *salt, if (cnt % 3 != 0) md5_process_bytes (salt, salt_len, ctx); - /* Add key for numbers not divisible by 7. */ + /* Add phrase for numbers not divisible by 7. */ if (cnt % 7 != 0) - md5_process_bytes (key, key_len, ctx); + md5_process_bytes (phrase, phrase_len, ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) md5_process_bytes (result, 16, ctx); else - md5_process_bytes (key, key_len, ctx); + md5_process_bytes (phrase, phrase_len, ctx); /* Create intermediate result. */ md5_finish_ctx (ctx, result); @@ -220,7 +209,5 @@ crypt_md5_rn (const char *key, const char *salt, b64_from_24bit (0, 0, result[11], 2); *cp = '\0'; - - md5_wipe_intermediate_data (buf); - return buf->output; + return output; } diff --git a/crypt-private.h b/crypt-private.h index 4300d40..1d47d9b 100644 --- a/crypt-private.h +++ b/crypt-private.h @@ -22,57 +22,65 @@ #include "crypt-base.h" -#include <stddef.h> +/* The "scratch" area passed to each of the individual hash functions is + this big. */ +#define ALG_SPECIFIC_SIZE 8192 /* Individual hash functions */ #if ENABLE_WEAK_HASHES -extern char *crypt_des_trd_or_big_rn (const char *key, const char *salt, - char *data, size_t size); -extern char *crypt_des_xbsd_rn (const char *key, const char *salt, - char *data, size_t size); -extern char *crypt_md5_rn (const char *key, const char *salt, - char *data, size_t size); +extern uint8_t *crypt_des_trd_or_big_rn (const char *phrase, + const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); +extern uint8_t *crypt_des_xbsd_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); +extern uint8_t *crypt_md5_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); #endif -extern char *crypt_sha256_rn (const char *key, const char *salt, - char *data, size_t size); -extern char *crypt_sha512_rn (const char *key, const char *salt, - char *data, size_t size); -extern char *crypt_bcrypt_rn (const char *key, const char *salt, - char *data, size_t size); +extern uint8_t *crypt_sha256_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); +extern uint8_t *crypt_sha512_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); +extern uint8_t *crypt_bcrypt_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); #if ENABLE_WEAK_HASHES -extern char *gensalt_des_trd_rn (unsigned long count, - const char *input, int size, - char *output, int output_size); -extern char *gensalt_des_xbsd_rn (unsigned long count, - const char *input, int size, - char *output, int output_size); -extern char *gensalt_md5_rn (unsigned long count, const char *input, - int size, char *output, int output_size); +extern uint8_t *gensalt_des_trd_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); +extern uint8_t *gensalt_des_xbsd_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); +extern uint8_t *gensalt_md5_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); #endif -extern char *gensalt_sha256_rn (unsigned long count, - const char *input, - int size, char *output, - int output_size); -extern char *gensalt_sha512_rn (unsigned long count, - const char *input, - int size, char *output, - int output_size); +extern uint8_t *gensalt_sha256_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); +extern uint8_t *gensalt_sha512_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); -extern char *gensalt_bcrypt_a_rn (unsigned long count, - const char *input, int size, - char *output, int output_size); -extern char *gensalt_bcrypt_b_rn (unsigned long count, - const char *input, int size, - char *output, int output_size); -extern char *gensalt_bcrypt_x_rn (unsigned long count, - const char *input, int size, - char *output, int output_size); -extern char *gensalt_bcrypt_y_rn (unsigned long count, - const char *input, int size, - char *output, int output_size); +extern uint8_t *gensalt_bcrypt_a_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); +extern uint8_t *gensalt_bcrypt_b_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); +extern uint8_t *gensalt_bcrypt_x_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); +extern uint8_t *gensalt_bcrypt_y_rn (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t o_size); #endif /* crypt-private.h */ diff --git a/crypt-sha256.c b/crypt-sha256.c index 134941b..22cc88c 100644 --- a/crypt-sha256.c +++ b/crypt-sha256.c @@ -28,9 +28,6 @@ #include <string.h> -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - /* Define our magic string to mark salt for SHA256 "encryption" replacement. */ static const char sha256_salt_prefix[] = "$5$"; @@ -58,37 +55,20 @@ static const char sha256_rounds_prefix[] = "rounds="; (sizeof (sha256_salt_prefix) + sizeof (sha256_rounds_prefix) + \ LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 43) +static_assert (SHA256_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for SHA256"); -/* A sha256_buffer holds the output plus all of the sensitive intermediate - data. It may have been allocated by application code, so it may not - be properly aligned, and besides which SHA256_HASH_LENGTH may be odd, so - we pad 'ctxbuf' enough to find a properly-aligned sha256_ctx within. */ +/* A sha256_buffer holds all of the sensitive intermediate data. */ struct sha256_buffer { - char output[SHA256_HASH_LENGTH]; - uint8_t ctxbuf[sizeof (struct sha256_ctx) + alignof (struct sha256_ctx)]; + struct sha256_ctx ctx; uint8_t result[32]; uint8_t p_bytes[32]; uint8_t s_bytes[32]; }; -static inline struct sha256_ctx * -sha256_get_ctx (struct sha256_buffer *buf) -{ - uintptr_t ctxp = (uintptr_t) &buf->ctxbuf; - uintptr_t align = alignof (struct sha256_ctx); - ctxp = (ctxp + align - 1) & ~align; - return (struct sha256_ctx *)ctxp; -} - -static inline void -sha256_wipe_intermediate_data (struct sha256_buffer *buf) -{ - memset (((char *)buf) + offsetof (struct sha256_buffer, ctxbuf), - 0, - sizeof (struct sha256_buffer) - - offsetof (struct sha256_buffer, ctxbuf)); -} +static_assert (sizeof (struct sha256_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for SHA256"); /* Table with characters for base64 transformation. */ @@ -108,26 +88,28 @@ sha256_process_recycled_bytes (unsigned char block[32], size_t len, sha256_process_bytes (block, cnt, ctx); } -char * -crypt_sha256_rn (const char *key, const char *salt, - char *data, size_t size) +uint8_t * +crypt_sha256_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for a sha256_buffer in DATA. */ - if (size < sizeof (struct sha256_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < SHA256_HASH_LENGTH || s_size < sizeof (struct sha256_buffer)) { errno = ERANGE; return 0; } - struct sha256_buffer *buf = (struct sha256_buffer *)data; - struct sha256_ctx *ctx = sha256_get_ctx (buf); + struct sha256_buffer *buf = scratch; + struct sha256_ctx *ctx = &buf->ctx; uint8_t *result = buf->result; uint8_t *p_bytes = buf->p_bytes; uint8_t *s_bytes = buf->s_bytes; - char *cp = buf->output; + char *cp = (char *)output; + const char *salt = setting; size_t salt_len; - size_t key_len; + size_t phrase_len; size_t cnt; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; @@ -154,20 +136,20 @@ crypt_sha256_rn (const char *key, const char *salt, } salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); - key_len = strlen (key); + phrase_len = strlen (phrase); - /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The + /* Compute alternate SHA256 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ sha256_init_ctx (ctx); - /* Add key. */ - sha256_process_bytes (key, key_len, ctx); + /* Add phrase. */ + sha256_process_bytes (phrase, phrase_len, ctx); /* Add salt. */ sha256_process_bytes (salt, salt_len, ctx); - /* Add key again. */ - sha256_process_bytes (key, key_len, ctx); + /* Add phrase again. */ + sha256_process_bytes (phrase, phrase_len, ctx); /* Now get result of this (32 bytes). */ sha256_finish_ctx (ctx, result); @@ -175,26 +157,26 @@ crypt_sha256_rn (const char *key, const char *salt, /* Prepare for the real work. */ sha256_init_ctx (ctx); - /* Add the key string. */ - sha256_process_bytes (key, key_len, ctx); + /* Add the phrase string. */ + sha256_process_bytes (phrase, phrase_len, ctx); /* The last part is the salt string. This must be at most 8 characters and it ends at the first `$' character (for compatibility with existing implementations). */ sha256_process_bytes (salt, salt_len, ctx); - /* Add for any character in the key one byte of the alternate sum. */ - for (cnt = key_len; cnt > 32; cnt -= 32) + /* Add for any character in the phrase one byte of the alternate sum. */ + for (cnt = phrase_len; cnt > 32; cnt -= 32) sha256_process_bytes (result, 32, ctx); sha256_process_bytes (result, cnt, ctx); - /* Take the binary representation of the length of the key and for every - 1 add the alternate sum, for every 0 the key. */ - for (cnt = key_len; cnt > 0; cnt >>= 1) + /* Take the binary representation of the length of the phrase and for every + 1 add the alternate sum, for every 0 the phrase. */ + for (cnt = phrase_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) sha256_process_bytes (result, 32, ctx); else - sha256_process_bytes (key, key_len, ctx); + sha256_process_bytes (phrase, phrase_len, ctx); /* Create intermediate result. */ sha256_finish_ctx (ctx, result); @@ -203,8 +185,8 @@ crypt_sha256_rn (const char *key, const char *salt, sha256_init_ctx (ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < key_len; ++cnt) - sha256_process_bytes (key, key_len, ctx); + for (cnt = 0; cnt < phrase_len; ++cnt) + sha256_process_bytes (phrase, phrase_len, ctx); /* Finish the digest. */ sha256_finish_ctx (ctx, p_bytes); @@ -226,9 +208,9 @@ crypt_sha256_rn (const char *key, const char *salt, /* New context. */ sha256_init_ctx (ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - sha256_process_recycled_bytes (p_bytes, key_len, ctx); + sha256_process_recycled_bytes (p_bytes, phrase_len, ctx); else sha256_process_bytes (result, 32, ctx); @@ -236,15 +218,15 @@ crypt_sha256_rn (const char *key, const char *salt, if (cnt % 3 != 0) sha256_process_recycled_bytes (s_bytes, salt_len, ctx); - /* Add key for numbers not divisible by 7. */ + /* Add phrase for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha256_process_recycled_bytes (p_bytes, key_len, ctx); + sha256_process_recycled_bytes (p_bytes, phrase_len, ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) sha256_process_bytes (result, 32, ctx); else - sha256_process_recycled_bytes (p_bytes, key_len, ctx); + sha256_process_recycled_bytes (p_bytes, phrase_len, ctx); /* Create intermediate result. */ sha256_finish_ctx (ctx, result); @@ -294,7 +276,5 @@ crypt_sha256_rn (const char *key, const char *salt, b64_from_24bit (0, result[31], result[30], 3); *cp = '\0'; - - sha256_wipe_intermediate_data (buf); - return buf->output; + return output; } diff --git a/crypt-sha512.c b/crypt-sha512.c index 8107b24..7b60101 100644 --- a/crypt-sha512.c +++ b/crypt-sha512.c @@ -28,9 +28,6 @@ #include <string.h> -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - /* Define our magic string to mark salt for SHA512 "encryption" replacement. */ static const char sha512_salt_prefix[] = "$6$"; @@ -58,37 +55,20 @@ static const char sha512_rounds_prefix[] = "rounds="; (sizeof (sha512_salt_prefix) + sizeof (sha512_rounds_prefix) + \ LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 86) +static_assert (SHA512_HASH_LENGTH <= CRYPT_OUTPUT_SIZE, + "CRYPT_OUTPUT_SIZE is too small for SHA512"); -/* A sha512_buffer holds the output plus all of the sensitive intermediate - data. It may have been allocated by application code, so it may not - be properly aligned, and besides which SHA512_HASH_LENGTH may be odd, so - we pad 'ctxbuf' enough to find a properly-aligned sha512_ctx within. */ +/* A sha512_buffer holds all of the sensitive intermediate data. */ struct sha512_buffer { - char output[SHA512_HASH_LENGTH]; - uint8_t ctxbuf[sizeof (struct sha512_ctx) + alignof (struct sha512_ctx)]; + struct sha512_ctx ctx; uint8_t result[64]; uint8_t p_bytes[64]; uint8_t s_bytes[64]; }; -static inline struct sha512_ctx * -sha512_get_ctx (struct sha512_buffer *buf) -{ - uintptr_t ctxp = (uintptr_t) &buf->ctxbuf; - uintptr_t align = alignof (struct sha512_ctx); - ctxp = (ctxp + align - 1) & ~align; - return (struct sha512_ctx *)ctxp; -} - -static inline void -sha512_wipe_intermediate_data (struct sha512_buffer *buf) -{ - memset (((char *)buf) + offsetof (struct sha512_buffer, ctxbuf), - 0, - sizeof (struct sha512_buffer) - - offsetof (struct sha512_buffer, ctxbuf)); -} +static_assert (sizeof (struct sha512_buffer) <= ALG_SPECIFIC_SIZE, + "ALG_SPECIFIC_SIZE is too small for SHA512"); /* Table with characters for base64 transformation. */ @@ -108,26 +88,28 @@ sha512_process_recycled_bytes (unsigned char block[64], size_t len, sha512_process_bytes (block, cnt, ctx); } -char * -crypt_sha512_rn (const char *key, const char *salt, - char *data, size_t size) +uint8_t * +crypt_sha512_rn (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size) { - /* Ensure we have enough space for a sha512_buffer in DATA. */ - if (size < sizeof (struct sha512_buffer)) + /* This shouldn't ever happen, but... */ + if (o_size < SHA512_HASH_LENGTH || s_size < sizeof (struct sha512_buffer)) { errno = ERANGE; return 0; } - struct sha512_buffer *buf = (struct sha512_buffer *)data; - struct sha512_ctx *ctx = sha512_get_ctx (buf); + struct sha512_buffer *buf = scratch; + struct sha512_ctx *ctx = &buf->ctx; uint8_t *result = buf->result; uint8_t *p_bytes = buf->p_bytes; uint8_t *s_bytes = buf->s_bytes; - char *cp = buf->output; + char *cp = (char *)output; + const char *salt = setting; size_t salt_len; - size_t key_len; + size_t phrase_len; size_t cnt; /* Default number of rounds. */ size_t rounds = ROUNDS_DEFAULT; @@ -154,20 +136,20 @@ crypt_sha512_rn (const char *key, const char *salt, } salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); - key_len = strlen (key); + phrase_len = strlen (phrase); - /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The + /* Compute alternate SHA512 sum with input PHRASE, SALT, and PHRASE. The final result will be added to the first context. */ sha512_init_ctx (ctx); - /* Add key. */ - sha512_process_bytes (key, key_len, ctx); + /* Add phrase. */ + sha512_process_bytes (phrase, phrase_len, ctx); /* Add salt. */ sha512_process_bytes (salt, salt_len, ctx); - /* Add key again. */ - sha512_process_bytes (key, key_len, ctx); + /* Add phrase again. */ + sha512_process_bytes (phrase, phrase_len, ctx); /* Now get result of this (64 bytes) and add it to the other context. */ @@ -176,26 +158,26 @@ crypt_sha512_rn (const char *key, const char *salt, /* Prepare for the real work. */ sha512_init_ctx (ctx); - /* Add the key string. */ - sha512_process_bytes (key, key_len, ctx); + /* Add the phrase string. */ + sha512_process_bytes (phrase, phrase_len, ctx); /* The last part is the salt string. This must be at most 8 characters and it ends at the first `$' character (for compatibility with existing implementations). */ sha512_process_bytes (salt, salt_len, ctx); - /* Add for any character in the key one byte of the alternate sum. */ - for (cnt = key_len; cnt > 64; cnt -= 64) + /* Add for any character in the phrase one byte of the alternate sum. */ + for (cnt = phrase_len; cnt > 64; cnt -= 64) sha512_process_bytes (result, 64, ctx); sha512_process_bytes (result, cnt, ctx); - /* Take the binary representation of the length of the key and for every - 1 add the alternate sum, for every 0 the key. */ - for (cnt = key_len; cnt > 0; cnt >>= 1) + /* Take the binary representation of the length of the phrase and for every + 1 add the alternate sum, for every 0 the phrase. */ + for (cnt = phrase_len; cnt > 0; cnt >>= 1) if ((cnt & 1) != 0) sha512_process_bytes (result, 64, ctx); else - sha512_process_bytes (key, key_len, ctx); + sha512_process_bytes (phrase, phrase_len, ctx); /* Create intermediate result. */ sha512_finish_ctx (ctx, result); @@ -204,8 +186,8 @@ crypt_sha512_rn (const char *key, const char *salt, sha512_init_ctx (ctx); /* For every character in the password add the entire password. */ - for (cnt = 0; cnt < key_len; ++cnt) - sha512_process_bytes (key, key_len, ctx); + for (cnt = 0; cnt < phrase_len; ++cnt) + sha512_process_bytes (phrase, phrase_len, ctx); /* Finish the digest. */ sha512_finish_ctx (ctx, p_bytes); @@ -227,9 +209,9 @@ crypt_sha512_rn (const char *key, const char *salt, /* New context. */ sha512_init_ctx (ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) - sha512_process_recycled_bytes (p_bytes, key_len, ctx); + sha512_process_recycled_bytes (p_bytes, phrase_len, ctx); else sha512_process_bytes (result, 64, ctx); @@ -237,15 +219,15 @@ crypt_sha512_rn (const char *key, const char *salt, if (cnt % 3 != 0) sha512_process_recycled_bytes (s_bytes, salt_len, ctx); - /* Add key for numbers not divisible by 7. */ + /* Add phrase for numbers not divisible by 7. */ if (cnt % 7 != 0) - sha512_process_recycled_bytes (p_bytes, key_len, ctx); + sha512_process_recycled_bytes (p_bytes, phrase_len, ctx); - /* Add key or last result. */ + /* Add phrase or last result. */ if ((cnt & 1) != 0) sha512_process_bytes (result, 64, ctx); else - sha512_process_recycled_bytes (p_bytes, key_len, ctx); + sha512_process_recycled_bytes (p_bytes, phrase_len, ctx); /* Create intermediate result. */ sha512_finish_ctx (ctx, result); @@ -308,7 +290,5 @@ crypt_sha512_rn (const char *key, const char *salt, b64_from_24bit (0, 0, result[63], 2); *cp = '\0'; - - sha512_wipe_intermediate_data (buf); - return buf->output; + return output; } diff --git a/crypt-symbols.h b/crypt-symbols.h index df7b21c..a87cd13 100644 --- a/crypt-symbols.h +++ b/crypt-symbols.h @@ -6,6 +6,9 @@ #undef NDEBUG #include <assert.h> +#include <stddef.h> +#include <stdint.h> + #ifdef HAVE_SYS_CDEFS_H #include <sys/cdefs.h> #endif @@ -38,6 +41,27 @@ [!!sizeof (struct { int xcrypt_error_if_negative: (expr) ? 2 : -1; })] #endif +/* max_align_t shim. In the absence of official word from the + compiler, we guess that one of long double, uintmax_t, void *, and + void (*)(void) will have the maximum alignment. This is probably + not true in the presence of vector types, but we currently don't + use vector types, and hopefully any compiler with extra-aligned + vector types will provide max_align_t. */ +#ifndef HAVE_MAX_ALIGN_T +typedef union +{ + long double ld; + uintmax_t ui; + void *vp; + void (*vpf)(void); +} max_align_t; +#endif + +/* Several files expect the traditional definitions of these macros. */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + /* Per-symbol version tagging. Currently we only know how to do this using GCC extensions. */ @@ -14,23 +14,69 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "crypt-private.h" +#include "crypt-obsolete.h" + #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "crypt-private.h" -#include "crypt-obsolete.h" +/* The internal storage area within struct crypt_data is used as + follows. We don't know what alignment the algorithm modules will + need for their scratch data, so give it the maximum natural + alignment. Note that the C11 alignas() specifier can't be applied + directly to a struct type, but it can be applied to the first field + of a struct, which effectively forces alignment of the entire + struct, since the first field must always have offset 0. */ +struct crypt_internal +{ + char alignas (max_align_t) alg_specific[ALG_SPECIFIC_SIZE]; +}; -#define CRYPT_GENSALT_OUTPUT_SIZE (7 + 22 + 1) +static_assert(sizeof(struct crypt_internal) + alignof(struct crypt_internal) + <= sizeof(((struct crypt_data){0}).internal), + "crypt_data.internal is too small for crypt_internal"); + +/* struct crypt_data is allocated by application code and contains + only char-typed fields, so its 'internal' field may not be + sufficiently aligned. */ +static inline struct crypt_internal * +get_internal (struct crypt_data *data) +{ + uintptr_t internalp = (uintptr_t) data->internal; + uintptr_t align = alignof (struct crypt_internal); + internalp = (internalp + align - 1) & ~align; + return (struct crypt_internal *)internalp; +} + +typedef uint8_t *(*crypt_fn) (const char *phrase, const char *setting, + uint8_t *output, size_t o_size, + void *scratch, size_t s_size); + +typedef uint8_t *(*gensalt_fn) (unsigned long count, + const uint8_t *rbytes, size_t nrbytes, + uint8_t *output, size_t output_size); + +static inline char * +call_crypt_fn (crypt_fn cfn, + const char *phrase, const char *setting, + struct crypt_data *data) +{ + struct crypt_internal *cint = get_internal (data); + unsigned char *rv; + rv = cfn (phrase, setting, + (unsigned char *)data->output, sizeof data->output, + cint->alg_specific, sizeof cint->alg_specific); + memset (data->internal, 0, sizeof data->internal); + return (char *)rv; +} struct hashfn { const char *prefix; - char *(*crypt) (const char *key, const char *salt, char *data, size_t size); - char *(*gensalt) (unsigned long count, - const char *input, int input_size, - char *output, int output_size); + crypt_fn crypt; + gensalt_fn gensalt; }; /* This table should always begin with the algorithm that should be used @@ -73,21 +119,21 @@ is_des_salt_char (char c) #endif /* ENABLE_WEAK_HASHES */ static const struct hashfn * -get_hashfn (const char *salt) +get_hashfn (const char *setting) { - if (salt[0] == '$') + if (setting[0] == '$') { const struct hashfn *h; for (h = tagged_hashes; h->prefix; h++) - if (!strncmp (salt, h->prefix, strlen (h->prefix))) + if (!strncmp (setting, h->prefix, strlen (h->prefix))) return h; return NULL; } #if ENABLE_WEAK_HASHES - else if (salt[0] == '_') + else if (setting[0] == '_') return &bsdi_extended_hash; - else if (salt[0] == '\0' || - (is_des_salt_char (salt[0]) && is_des_salt_char (salt[1]))) + else if (setting[0] == '\0' || + (is_des_salt_char (setting[0]) && is_des_salt_char (setting[1]))) return &traditional_hash; #endif else @@ -97,47 +143,62 @@ get_hashfn (const char *salt) /* For historical reasons, crypt and crypt_r are not expected ever to return NULL. This function generates a "failure token" in the output buffer, which is guaranteed not to be equal to any valid - password hash, or to the salt(+hash) string; thus, a subsequent + password hash, or to the setting(+hash) string; thus, a subsequent blind attempt to authenticate someone by comparing the output to a previously recorded hash string will fail, even if that string - is itself one of these "failure tokens". */ + is itself one of these "failure tokens". + + crypt_gensalt_rn also uses this technique, for extra defensiveness + in case someone doesn't bother checking the return value. */ static void -make_failure_token (const char *salt, char *output, int size) +make_failure_token (const char *setting, char *output, int size) { - if (size < 3) - return; + if (size >= 3) + { + output[0] = '*'; + output[1] = '0'; + output[2] = '\0'; - output[0] = '*'; - output[1] = '0'; - output[2] = '\0'; + if (setting[0] == '*' && setting[1] == '0') + output[1] = '1'; + } - if (salt[0] == '*' && salt[1] == '0') - output[1] = '1'; + /* If there's not enough space for the full failure token, do the + best we can. */ + else if (size == 2) + { + output[0] = '*'; + output[1] = '\0'; + } + else if (size == 1) + { + output[0] = '\0'; + } } static char * -do_crypt_rn (const char *key, const char *salt, char *data, int size) +do_crypt_rn (const char *phrase, const char *setting, void *data, int size) { - if (size <= 0) + if (size < 0 || (size_t)size < sizeof (struct crypt_data)) { errno = ERANGE; return NULL; } - const struct hashfn *h = get_hashfn (salt); + const struct hashfn *h = get_hashfn (setting); if (!h) { /* Unrecognized hash algorithm */ errno = EINVAL; return NULL; } - return h->crypt (key, salt, data, (size_t)size); + return call_crypt_fn (h->crypt, phrase, setting, data); } static char * -do_crypt_ra (const char *key, const char *salt, void **data, int *size) +do_crypt_ra (const char *phrase, const char *setting, void **data, int *size) { - const struct hashfn *h = get_hashfn (salt); + const struct hashfn *h = get_hashfn (setting); if (!h) { /* Unrecognized hash algorithm */ @@ -159,16 +220,16 @@ do_crypt_ra (const char *key, const char *salt, void **data, int *size) return NULL; } - return h->crypt (key, salt, *data, (size_t)*size); + return call_crypt_fn (h->crypt, phrase, setting, *data); } #if INCLUDE_crypt_rn char * -crypt_rn (const char *key, const char *salt, void *data, int size) +crypt_rn (const char *phrase, const char *setting, void *data, int size) { - char *retval = do_crypt_rn (key, salt, data, size); + char *retval = do_crypt_rn (phrase, setting, data, size); if (!retval) - make_failure_token (salt, data, size); + make_failure_token (setting, data, size); return retval; } SYMVER_crypt_rn; @@ -176,11 +237,11 @@ SYMVER_crypt_rn; #if INCLUDE_crypt_ra char * -crypt_ra (const char *key, const char *salt, void **data, int *size) +crypt_ra (const char *phrase, const char *setting, void **data, int *size) { - char *retval = do_crypt_ra (key, salt, data, size); + char *retval = do_crypt_ra (phrase, setting, data, size); if (!retval) - make_failure_token (salt, *data, *size); + make_failure_token (setting, *data, *size); return retval; } SYMVER_crypt_ra; @@ -188,9 +249,9 @@ SYMVER_crypt_ra; #if INCLUDE_crypt_r char * -crypt_r (const char *key, const char *salt, struct crypt_data *data) +crypt_r (const char *phrase, const char *setting, struct crypt_data *data) { - char *retval = crypt_rn (key, salt, (char *) data, sizeof (*data)); + char *retval = crypt_rn (phrase, setting, (char *) data, sizeof (*data)); if (!retval) return (char *)data; /* return the failure token */ return retval; @@ -201,25 +262,41 @@ SYMVER_crypt_r; #if INCLUDE_crypt_gensalt_rn char * crypt_gensalt_rn (const char *prefix, unsigned long count, - const char *input, int size, char *output, + const char *rbytes, int nrbytes, char *output, int output_size) { - const struct hashfn *h; + make_failure_token ("", output, output_size); - /* This may be supported on some platforms in the future */ - if (!input) + /* Individual gensalt functions will check for adequate space for + their own breed of setting, but the shortest possible one is + three bytes (DES two-character salt + NUL terminator) and we + also want to rule out negative numbers early. */ + if (output_size < 3) + { + errno = ERANGE; + return NULL; + } + + /* Empty rbytes may be supported on some platforms in the future. + Individual gensalt functions will check for sufficient random bits + for their own breed of setting, but the shortest possible one has + 64**2 = 4096 possibilitiesm, which requires two bytes of input. */ + if (!rbytes || nrbytes < 2) { errno = EINVAL; return NULL; } - h = get_hashfn (prefix); + const struct hashfn *h = get_hashfn (prefix); if (!h) { errno = EINVAL; return NULL; } - return h->gensalt (count, input, size, output, output_size); + + return (char *)h->gensalt (count, + (const unsigned char *)rbytes, (size_t)nrbytes, + (unsigned char *)output, (size_t)output_size); } SYMVER_crypt_gensalt_rn; #endif @@ -227,13 +304,13 @@ SYMVER_crypt_gensalt_rn; #if INCLUDE_crypt_gensalt_ra char * crypt_gensalt_ra (const char *prefix, unsigned long count, - const char *input, int size) + const char *rbytes, int nrbytes) { char *output = malloc (CRYPT_GENSALT_OUTPUT_SIZE); if (!output) return output; - return crypt_gensalt_rn (prefix, count, input, size, output, + return crypt_gensalt_rn (prefix, count, rbytes, nrbytes, output, CRYPT_GENSALT_OUTPUT_SIZE); } SYMVER_crypt_gensalt_ra; @@ -242,12 +319,12 @@ SYMVER_crypt_gensalt_ra; #if INCLUDE_crypt_gensalt char * crypt_gensalt (const char *prefix, unsigned long count, - const char *input, int size) + const char *rbytes, int nrbytes) { static char output[CRYPT_GENSALT_OUTPUT_SIZE]; return crypt_gensalt_rn (prefix, count, - input, size, output, sizeof (output)); + rbytes, nrbytes, output, sizeof (output)); } SYMVER_crypt_gensalt; #endif diff --git a/m4/zw_alignment.m4 b/m4/zw_alignment.m4 index b55a60e..3467ae1 100644 --- a/m4/zw_alignment.m4 +++ b/m4/zw_alignment.m4 @@ -28,6 +28,7 @@ AC_DEFUN([zw_C_ALIGNAS], [Define as a type specifier which sets the alignment of a variable or type to N bytes.]) ]) + AC_DEFUN([zw_C_ALIGNOF], [AC_REQUIRE([AC_PROG_CC]) AC_CACHE_CHECK([how to query data alignment], [zw_cv_c_alignof], @@ -55,3 +56,23 @@ AC_DEFUN([zw_C_ALIGNOF], [Define as an expression which evaluates to the alignment of THING. Must be computed at compile time (an "integer constant expression").]) ]) + +AC_DEFUN([zw_C_MAX_ALIGN_T], + [AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([zw_C_ALIGNOF]) + AC_CACHE_CHECK([for max_align_t in stddef.h], [zw_cv_c_max_align_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #include <stddef.h> + ]], [[ + max_align_t var; + return alignof(var); + ]])], + [zw_cv_c_max_align_t=yes], + [zw_cv_c_max_align_t=no]) + ]) + AS_IF([test x$zw_cv_c_max_align_t = xyes], + [AC_DEFINE([HAVE_MAX_ALIGN_T], 1, + [Define if stddef.h provides max_align_t.]) + ]) +]) |