asterisk/main/crypt.c

203 lines
4.6 KiB
C
Raw Normal View History

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2012 - 2013, Digium, Inc.
*
* David M. Lee, II <dlee@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Asterisk wrapper for crypt(3)
* \author David M. Lee, II <dlee@digium.com>
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <unistd.h>
#if defined(HAVE_CRYPT_R)
#include <crypt.h>
#endif
#include "asterisk/utils.h"
/*!
* \brief Max length of a salt string.
*
* $[1,5,6]$[azAZ09./]{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)
{
int which = ast_random_double() * 64;
return salt_chars[which];
}
/*!
* \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)
{
struct crypt_data data = {};
return strcmp(expected, crypt_r(key, expected, &data)) == 0;
}
#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)
{
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");
return 0;
}
#endif /* No crypt support */
char *ast_crypt_encrypt(const char *key)
{
char salt[MAX_SALT_LEN] = {};
while (gen_salt(salt, sizeof(salt)) == 0) {
char *crypted = ast_crypt(key, salt);
if (crypted) {
return crypted;
}
}
return NULL;
}