Comment by kazinator
15 hours ago
In the TXR Lisp run-time, I spent a disproportionate amount of time on the crypt wrapper, making sure that it detects errors on numerous version of crypt, and converts them to an exception.
I settled on this. There is a block comment on the error handling, dating back to just 2023. There is a tiny infelicity in the wrapper in that we rely on the value of errno in the error path, but we do not set errno to zero and check for it being nonzero (evidence that the function set the value).
Applications using crypt directly can do the same thing: if it returns null, that's an error, of course, but don't use the result if it is less than 13 characters: that's also some kind of error token. The classic crypt() puts out a 13 character hash. Everything else is longer.
static val crypt_wrap(val wkey, val wsalt)
{
val self = lit("crypt");
const wchar_t *cwkey = c_str(wkey, self);
const wchar_t *cwsalt = c_str(wsalt, self);
char *key = utf8_dup_to(cwkey);
char *salt = utf8_dup_to(cwsalt);
#if HAVE_CRYPT_R
struct crypt_data *cd = coerce(struct crypt_data *, chk_malloc(sizeof *cd));
char *hash = (cd->initialized = 0, crypt_r(key, salt, cd));
#else
char *hash = crypt(key, salt);
#endif
free(key);
free(salt);
/* libraries cannot agree on how to report unrecognized or bad hashes:
*
* - older glibc versions, other libraries return null
* - libxcrypt, integrated into newer glibc puts out two
* possible failure tokens "*0" or "*1", documenting
* that an error token starts with "*" and is less than 13
* characters long.
* - musl uses "*" and "x", the latter being in the valid hash charset!
*
* let's go with: null or less than 13 chars means error.
*/
if (hash != 0 && memchr(hash, 0, 13) == 0) {
val ret = string_utf8(hash);
#if HAVE_CRYPT_R
free(cd);
#endif
return ret;
}
#if HAVE_CRYPT_R
free(cd);
#endif
uw_ethrowf(error_s, lit("crypt failed: ~d/~s"), num(errno),
errno_to_str(errno), nao);
}
No comments yet
Contribute on Hacker News ↗