summaryrefslogtreecommitdiffstats
path: root/sysif.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2020-02-14 21:55:24 -0800
committerKaz Kylheku <kaz@kylheku.com>2020-02-14 21:55:24 -0800
commitc3a0ceb2cea1a9d43f2baf5a2e63d0d712c8df19 (patch)
tree242ffa74838fcffdc039fce1cd58fc84a220a743 /sysif.c
parent176185cd6530bad1f8c3b36fe54bcab7580260ff (diff)
downloadtxr-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.c63
1 files changed, 60 insertions, 3 deletions
diff --git a/sysif.c b/sysif.c
index 638f9642..cb839900 100644
--- a/sysif.c
+++ b/sysif.c
@@ -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