diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2020-02-14 21:55:24 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2020-02-14 21:55:24 -0800 |
commit | c3a0ceb2cea1a9d43f2baf5a2e63d0d712c8df19 (patch) | |
tree | 242ffa74838fcffdc039fce1cd58fc84a220a743 /sysif.c | |
parent | 176185cd6530bad1f8c3b36fe54bcab7580260ff (diff) | |
download | txr-c3a0ceb2cea1a9d43f2baf5a2e63d0d712c8df19.tar.gz txr-c3a0ceb2cea1a9d43f2baf5a2e63d0d712c8df19.tar.bz2 txr-c3a0ceb2cea1a9d43f2baf5a2e63d0d712c8df19.zip |
crypt: harden against crashes.
The crypt function on glibc, and maybe other platforms, simply
crashes when given a perfectly valid salt string that contains
invalid salt syntax. This is nasty; we want TXR Lisp
library functions to be robust; bringing down the image is not
acceptable.
Also, crypt may return a null pointer. glibc's crypt does
this in certain situations, like when the "2a" (Blowfish)
algorithm is specified when not available. We are not checking
for this null return, in which case the ensuing crash is our
fault.
* sysif.c (salt_char_p, validate_salt): New functions.
(crypt_wrap): Validate the salt via validate_salt.
Check the return value from crypt/crypt_r; if null,
then throw an exception that incorporates the errno
information.
Diffstat (limited to 'sysif.c')
-rw-r--r-- | sysif.c | 63 |
1 files changed, 60 insertions, 3 deletions
@@ -1830,10 +1830,65 @@ static val getgrnam_wrap(val wname) #if HAVE_CRYPT || HAVE_CRYPT_R +static int salt_char_p(wchar_t ch) +{ + return ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + (ch == '.') || (ch == '/')); +} + +static const wchar_t *validate_salt(val salt_in) +{ + const wchar_t *salt = c_str(salt_in), *s = salt; + + if (salt_char_p(*s)) { + if (salt_char_p(*++s)) + return salt; + else + goto badsalt; + } + + if (*s++ != '$') + goto badsalt; + + switch (*s++) { + case '1': case '5': case '6': + break; + case '2': + if (*s >= 'a' && *s++ <= 'z') + break; + /* fallthrough */ + default: + goto badsalt; + } + + if (*s++ != '$') + goto badsalt; + + if (wcsncmp(s, L"rounds=", 7) == 0) { + size_t ispn = wcsspn(s += 7, L"0123456789"); + s += ispn; + if (*s++ != '$') + goto badsalt; + } + + while (salt_char_p(*s)) + s++; + + if (*s && *s != '$') + goto badsalt; + + return salt; +badsalt: + uw_throwf(error_s, lit("crypt failed: ~d/~s"), num(EINVAL), + string_utf8(strerror(EINVAL)), nao); +} + static val crypt_wrap(val wkey, val wsalt) { const wchar_t *cwkey = c_str(wkey); - const wchar_t *cwsalt = c_str(wsalt); + const wchar_t *cwsalt = validate_salt(wsalt); char *key = utf8_dup_to(cwkey); char *salt = utf8_dup_to(cwsalt); #if HAVE_CRYPT_R @@ -1842,10 +1897,12 @@ static val crypt_wrap(val wkey, val wsalt) #else char *hash = crypt(key, salt); #endif - val whash = string_utf8(hash); free(key); free(salt); - return whash; + if (hash == 0) + uw_throwf(error_s, lit("crypt failed: ~d/~s"), num(errno), + string_utf8(strerror(errno)), nao); + return string_utf8(hash); } #endif |