mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-04 05:15:22 +00:00 
			
		
		
		
	FreeBSD does not include a crypt.h include file. Definitions for crypt() and crypt_r() are in unistd.h ASTERISK-27042 #close Change-Id: Ib307ee5e384870c6af50efa89fb73722dd0c3a7e
		
			
				
	
	
		
			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 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;
 | 
						||
}
 |