[asterisk-commits] dlee: branch dlee/ari-authn r393025 - in /team/dlee/ari-authn: include/asteri...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Jun 26 17:02:19 CDT 2013
Author: dlee
Date: Wed Jun 26 17:02:18 2013
New Revision: 393025
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393025
Log:
Added mkpasswd command
Modified:
team/dlee/ari-authn/include/asterisk/utils.h
team/dlee/ari-authn/main/utils.c
team/dlee/ari-authn/res/stasis_http/cli.c
team/dlee/ari-authn/tests/test_utils.c
Modified: team/dlee/ari-authn/include/asterisk/utils.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/include/asterisk/utils.h?view=diff&rev=393025&r1=393024&r2=393025
==============================================================================
--- team/dlee/ari-authn/include/asterisk/utils.h (original)
+++ team/dlee/ari-authn/include/asterisk/utils.h Wed Jun 26 17:02:18 2013
@@ -938,10 +938,28 @@
void _dtor_ ## varname (vartype * v) { dtor(*v); } \
vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
+/*!
+ * \brief Asterisk wrapper around crypt(3).
+ *
+ * The interpretation of the salt (which determines the password hashing
+ * algorithm) is system specific. Application code should prefer to use
+ * ast_crypt_encrypt() or ast_crypt_validate().
+ *
+ * The returned string is heap allocated, and should be freed with ast_free().
+ *
+ * \param key User's password to crypt.
+ * \param salt Salt to crypt with.
+ * \return Crypted password.
+ * \return \c NULL on error.
+ */
+char *ast_crypt(const char *key, const char *salt);
+
/*
* \brief Asterisk wrapper around crypt(3) for encrypting passwords.
*
* This function will generate a random salt and encrypt the given password.
+ *
+ * The returned string is heap allocated, and should be freed with ast_free().
*
* \param key User's password to crypt.
* \return Crypted password.
@@ -954,8 +972,8 @@
*
* \param key User's password to validate.
* \param expected Expected result from crypt.
- * \return True (non-zero) if key matches crypted.
- * \return False (zero) if key doesn't match.
+ * \return True (non-zero) if \a key matches \a expected.
+ * \return False (zero) if \a key doesn't match.
*/
int ast_crypt_validate(const char *key, const char *expected);
Modified: team/dlee/ari-authn/main/utils.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/main/utils.c?view=diff&rev=393025&r1=393024&r2=393025
==============================================================================
--- team/dlee/ari-authn/main/utils.c (original)
+++ team/dlee/ari-authn/main/utils.c Wed Jun 26 17:02:18 2013
@@ -2273,12 +2273,101 @@
return ret;
}
-char *ast_crypt_encrypt(const char *key)
-{
- return NULL;
+/*!
+ * \brief Max length of a salt string.
+ *
+ * $[1,5,6]$[aâzAâZ0â9./]{1,16}$, plus null terminator
+ */
+#define MAX_SALT_LEN 21
+
+static char salt_chars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "./";
+
+/*! Randomly select a character for a salt string */
+static char gen_salt_char(void)
+{
+ return salt_chars[ast_random() % ARRAY_LEN(salt_chars)];
+}
+
+/*!
+ * \brief Generates a salt to try with crypt.
+ *
+ * If given an empty string, will generate a salt for the most secure algorithm
+ * to try with crypt(). If given a previously generated salt, the algorithm will
+ * be lowered by one level of security.
+ *
+ * \param[out] current_salt Output string in which to generate the salt.
+ * This can be an empty string, or the results of a
+ * prior gen_salt call.
+ * \param max_len Length of \a current_salt.
+ * \return 0 on success.
+ * \return Non-zero on error.
+ */
+static int gen_salt(char *current_salt, size_t maxlen)
+{
+ int i;
+ if (maxlen < MAX_SALT_LEN || current_salt == NULL) {
+ return -1;
+ }
+
+ switch (current_salt[0]) {
+ case '\0':
+ /* Initial generation; $6$ = SHA-512 */
+ *current_salt++ = '$';
+ *current_salt++ = '6';
+ *current_salt++ = '$';
+ for (i = 0; i < 16; ++i) {
+ *current_salt++ = gen_salt_char();
+ }
+ *current_salt++ = '$';
+ *current_salt++ = '\0';
+ return 0;
+ case '$':
+ switch (current_salt[1]) {
+ case '6':
+ /* Downgrade to SHA-256 */
+ current_salt[1] = '5';
+ return 0;
+ case '5':
+ /* Downgrade to MD5 */
+ current_salt[1] = '1';
+ return 0;
+ case '1':
+ /* Downgrade to traditional crypt */
+ *current_salt++ = gen_salt_char();
+ *current_salt++ = gen_salt_char();
+ *current_salt++ = '\0';
+ return 0;
+ default:
+ /* Unrecognized algorithm */
+ return -1;
+ }
+ default:
+ /* Was already as insecure as it gets */
+ return -1;
+ }
+
}
#if defined(HAVE_CRYPT_R)
+
+char *ast_crypt(const char *key, const char *salt)
+{
+ struct crypt_data data = {};
+ const char *crypted = crypt_r(key, salt, &data);
+
+ /* Crypt may return success even if it doesn't recognize the salt. But
+ * in those cases it always mangles the salt in some way.
+ */
+ if (!crypted || !ast_begins_with(crypted, salt)) {
+ return NULL;
+ }
+
+ return ast_strdup(crypted);
+}
int ast_crypt_validate(const char *key, const char *expected)
{
@@ -2288,25 +2377,65 @@
#elif defined(HAVE_CRYPT)
+/* crypt is not reentrant. A global mutex is neither ideal nor perfect, but good
+ * enough if crypt_r support is unavailable
+ */
AST_MUTEX_DEFINE_STATIC(crypt_mutex);
+char *ast_crypt(const char *key, const char *salt)
+{
+ const char *crypted;
+ SCOPED_MUTEX(lock, &crypt_mutex);
+
+ crypted = crypt(key, salt);
+
+ /* Crypt may return success even if it doesn't recognize the salt. But
+ * in those cases it always mangles the salt in some way.
+ */
+ if (!crypted || !ast_begins_with(crypted, salt)) {
+ return NULL;
+ }
+
+ return ast_strdup(crypted);
+}
+
int ast_crypt_validate(const char *key, const char *expected)
{
- /* crypt is not reentrant. A global mutex is neither ideal nor
- * perfect, but good enough if crypt_r support is unavailable */
SCOPED_MUTEX(lock, &crypt_mutex);
return strcmp(expected, crypt(key, expected)) == 0;
}
#else /* No crypt support */
+char *ast_crypt(const char *key, const char *salt)
+{
+ ast_log(LOG_WARNING,
+ "crypt() support not available; cannot encrypt password\n");
+ return NULL;
+}
+
int ast_crypt_validate(const char *key, const char *expected)
{
- ast_log(LOG_WARNING, "crypt() support not available; cannot validate password\n");
+ ast_log(LOG_WARNING,
+ "crypt() support not available; cannot validate password\n");
return 0;
}
#endif /* No crypt support */
+
+char *ast_crypt_encrypt(const char *key)
+{
+ char salt[MAX_SALT_LEN] = {};
+ SCOPED_MUTEX(lock, &crypt_mutex);
+ while (gen_salt(salt, sizeof(salt)) == 0) {
+ char *crypted = ast_crypt(key, salt);
+ if (crypted) {
+ return crypted;
+ }
+ }
+ return NULL;
+}
+
char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
{
Modified: team/dlee/ari-authn/res/stasis_http/cli.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/res/stasis_http/cli.c?view=diff&rev=393025&r1=393024&r2=393025
==============================================================================
--- team/dlee/ari-authn/res/stasis_http/cli.c (original)
+++ team/dlee/ari-authn/res/stasis_http/cli.c Wed Jun 26 17:02:18 2013
@@ -214,11 +214,44 @@
return CLI_SUCCESS;
}
+static char *ari_mkpasswd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ RAII_VAR(char *, crypted, NULL, ast_free);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ari mkpasswd";
+ e->usage =
+ "Usage: ari mkpasswd <password>\n"
+ " Encrypts a password for use in stasis_http.conf\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ default:
+ break;
+ }
+
+ if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ crypted = ast_crypt_encrypt(a->argv[2]);
+ if (!crypted) {
+ ast_cli(a->fd, "Failed to encrypt password\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "password_format = crypt\n");
+ ast_cli(a->fd, "password = %s\n", crypted);
+
+ return CLI_SUCCESS;
+}
static struct ast_cli_entry cli_ari[] = {
AST_CLI_DEFINE(ari_show, "Show ARI settings"),
AST_CLI_DEFINE(ari_show_users, "List ARI users"),
AST_CLI_DEFINE(ari_show_user, "List single ARI user"),
+ AST_CLI_DEFINE(ari_mkpasswd, "Encrypts a password"),
};
int ari_cli_register(void) {
Modified: team/dlee/ari-authn/tests/test_utils.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/tests/test_utils.c?view=diff&rev=393025&r1=393024&r2=393025
==============================================================================
--- team/dlee/ari-authn/tests/test_utils.c (original)
+++ team/dlee/ari-authn/tests/test_utils.c Wed Jun 26 17:02:18 2013
@@ -421,6 +421,42 @@
return res;
}
+AST_TEST_DEFINE(crypt_test)
+{
+ RAII_VAR(char *, password_crypted, NULL, ast_free);
+ RAII_VAR(char *, blank_crypted, NULL, ast_free);
+ const char *password = "Passw0rd";
+ const char *not_a_password = "not-a-password";
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "crypt_test";
+ info->category = "/main/utils/";
+ info->summary = "Test ast_crypt wrappers";
+ info->description = "Verifies that the ast_crypt wrappers work as expected.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ password_crypted = ast_crypt_encrypt(password);
+ ast_test_validate(test, NULL != password_crypted);
+ ast_test_validate(test, 0 != strcmp(password, password_crypted));
+ ast_test_validate(test, ast_crypt_validate(password, password_crypted));
+ ast_test_validate(test,
+ !ast_crypt_validate(not_a_password, password_crypted));
+
+ blank_crypted = ast_crypt_encrypt("");
+ ast_test_validate(test, NULL != blank_crypted);
+ ast_test_validate(test, 0 != strcmp(blank_crypted, ""));
+ ast_test_validate(test, ast_crypt_validate("", blank_crypted));
+ ast_test_validate(test,
+ !ast_crypt_validate(not_a_password, blank_crypted));
+
+ return AST_TEST_PASS;
+}
+
+
static int unload_module(void)
{
AST_TEST_UNREGISTER(uri_encode_decode_test);
@@ -431,6 +467,7 @@
AST_TEST_UNREGISTER(crypto_loaded_test);
AST_TEST_UNREGISTER(adsi_loaded_test);
AST_TEST_UNREGISTER(agi_loaded_test);
+ AST_TEST_UNREGISTER(crypt_test);
return 0;
}
@@ -444,6 +481,7 @@
AST_TEST_REGISTER(crypto_loaded_test);
AST_TEST_REGISTER(adsi_loaded_test);
AST_TEST_REGISTER(agi_loaded_test);
+ AST_TEST_REGISTER(crypt_test);
return AST_MODULE_LOAD_SUCCESS;
}
More information about the asterisk-commits
mailing list