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;
|
|||
|
}
|