[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