diff -urNp -x '*.orig' cyrus-sasl-2.1.27.org/Makefile.in cyrus-sasl-2.1.27/Makefile.in --- cyrus-sasl-2.1.27.org/Makefile.in 2018-10-09 16:58:13.000000000 +0200 +++ cyrus-sasl-2.1.27/Makefile.in 2021-09-28 23:58:30.012662863 +0200 @@ -308,7 +308,7 @@ LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ +LIBS = -lcrypt @LIBS@ LIBTOOL = @LIBTOOL@ LIB_CRYPT = @LIB_CRYPT@ LIB_DES = @LIB_DES@ diff -urNp -x '*.orig' cyrus-sasl-2.1.27.org/doc/legacy/options.html cyrus-sasl-2.1.27/doc/legacy/options.html --- cyrus-sasl-2.1.27.org/doc/legacy/options.html 2017-07-24 14:53:03.000000000 +0200 +++ cyrus-sasl-2.1.27/doc/legacy/options.html 2021-09-28 23:58:30.012662863 +0200 @@ -163,6 +163,14 @@ database. 126 +password_format +Method of password storage (possible values: 'plain', 'crypt', 'crypt_trad'). +Default 'plain' is down-compatible with earlier versions. 'crypt_trad' +uses old crypt format of 2 chars salt, 'crypt' automagically recognizes crypt +formats from md5 crypt, blowfish crypt and old crypt (2 chars salt). +plain + + sql_engineSQL plugin Name of SQL engine to use (possible values: 'mysql', 'pgsql', 'sqlite', 'sqlite3'). mysql diff -urNp -x '*.orig' cyrus-sasl-2.1.27.org/lib/checkpw.c cyrus-sasl-2.1.27/lib/checkpw.c --- cyrus-sasl-2.1.27.org/lib/checkpw.c 2018-11-08 18:29:57.000000000 +0100 +++ cyrus-sasl-2.1.27/lib/checkpw.c 2021-09-28 23:58:30.012662863 +0200 @@ -95,6 +95,23 @@ # endif #endif +/****************************** + * crypt(3) patch start * + ******************************/ +char *crypt(const char *key, const char *salt); + +/* cleartext password formats */ +#define PASSWORD_FORMAT_CLEARTEXT 1 +#define PASSWORD_FORMAT_CRYPT 2 +#define PASSWORD_FORMAT_CRYPTTRAD 3 +#define PASSWORD_SALT_BUF_LEN 22 + +/* weeds out crypt(3) password's salt */ +int _sasl_get_salt (char *dest, char *src, int format); + +/****************************** + * crypt(3) patch stop * + ******************************/ /* we store the following secret to check plaintext passwords: * @@ -142,7 +159,51 @@ static int auxprop_verify_password(sasl_ "*cmusaslsecretPLAIN", NULL }; struct propval auxprop_values[3]; - + + /****************************** + * crypt(3) patch start * + * for password format check * + ******************************/ + sasl_getopt_t *getopt; + void *context; + const char *p = NULL; + /** + * MD5: 12 char salt + * BLOWFISH: 16 char salt + */ + char salt[PASSWORD_SALT_BUF_LEN]; + int password_format; + + /* get password format from auxprop configuration */ + if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) { + getopt(context, NULL, "password_format", &p, NULL); + } + + /* set password format */ + if (p) { + /* + memset(pass_format_str, '\0', PASSWORD_FORMAT_STR_LEN); + strncpy(pass_format_str, p, (PASSWORD_FORMAT_STR_LEN - 1)); + */ + /* modern, modular crypt(3) */ + if (strncmp(p, "crypt", 11) == 0) + password_format = PASSWORD_FORMAT_CRYPT; + /* traditional crypt(3) */ + else if (strncmp(p, "crypt_trad", 11) == 0) + password_format = PASSWORD_FORMAT_CRYPTTRAD; + /* cleartext password */ + else + password_format = PASSWORD_FORMAT_CLEARTEXT; + } else { + /* cleartext password */ + password_format = PASSWORD_FORMAT_CLEARTEXT; + } + + /****************************** + * crypt(3) patch stop * + * for password format check * + ******************************/ + if (!conn || !userstr) return SASL_BADPARAM; @@ -188,14 +249,31 @@ static int auxprop_verify_password(sasl_ return SASL_NOUSER; } - /* At the point this has been called, the username has been canonified - * and we've done the auxprop lookup. This should be easy. */ - if(auxprop_values[0].name - && auxprop_values[0].values - && auxprop_values[0].values[0] - && !strcmp(auxprop_values[0].values[0], passwd)) { - /* We have a plaintext version and it matched! */ - return SASL_OK; + + /****************************** + * crypt(3) patch start * + ******************************/ + + /* get salt */ + _sasl_get_salt(salt, (char *) auxprop_values[0].values[0], password_format); + + /* crypt(3)-ed password? */ + if (password_format != PASSWORD_FORMAT_CLEARTEXT) { + /* compare password */ + if (auxprop_values[0].name && auxprop_values[0].values && auxprop_values[0].values[0] && strcmp(crypt(passwd, salt), auxprop_values[0].values[0]) == 0) + return SASL_OK; + else + ret = SASL_BADAUTH; + } + else if (password_format == PASSWORD_FORMAT_CLEARTEXT) { + /* compare passwords */ + if (auxprop_values[0].name && auxprop_values[0].values && auxprop_values[0].values[0] && strcmp(auxprop_values[0].values[0], passwd) == 0) + return SASL_OK; + else + ret = SASL_BADAUTH; + /****************************** + * crypt(3) patch stop * + ******************************/ } else if(auxprop_values[1].name && auxprop_values[1].values && auxprop_values[1].values[0]) { @@ -1105,3 +1183,37 @@ struct sasl_verify_password_s _sasl_veri #endif { NULL, NULL } }; + +/* weeds out crypt(3) password's salt */ +int _sasl_get_salt (char *dest, char *src, int format) { + int num; /* how many characters is salt long? */ + switch (format) { + case PASSWORD_FORMAT_CRYPT: + /* md5 crypt */ + if (src[1] == '1') + num = 12; + /* blowfish crypt */ + else if (src[1] == '2') + num = (src[1] == '2' && src[2] == 'a') ? 17 : 16; + /* traditional crypt */ + else + num = 2; + break; + + case PASSWORD_FORMAT_CRYPTTRAD: + num = 2; + break; + + default: + return 1; + } + + /* destroy destination */ + memset(dest, '\0', (num + 1)); + + /* copy salt to destination */ + strncpy(dest, src, num); + + return 1; +} +