201 lines
4.6 KiB
C
201 lines
4.6 KiB
C
/*
|
||
* 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"
|
||
|
||
#include <unistd.h>
|
||
#if defined(HAVE_CRYPT_R) && !defined(__FreeBSD__)
|
||
#include <crypt.h>
|
||
#endif
|
||
|
||
#include "asterisk/utils.h"
|
||
|
||
/*!
|
||
* \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)
|
||
{
|
||
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 maxlen 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;
|
||
}
|