crypt.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2012 - 2013, Digium, Inc.
00005  *
00006  * David M. Lee, II <dlee@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Asterisk wrapper for crypt(3)
00022  * \author David M. Lee, II <dlee@digium.com>
00023  */
00024 
00025 /*** MODULEINFO
00026    <support_level>core</support_level>
00027  ***/
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 393675 $")
00032 
00033 #include <unistd.h>
00034 #if defined(HAVE_CRYPT_R)
00035 #include <crypt.h>
00036 #endif
00037 
00038 #include "asterisk/utils.h"
00039 
00040 /*!
00041  * \brief Max length of a salt string.
00042  *
00043  * $[1,5,6]$[a–zA–Z0–9./]{1,16}$, plus null terminator
00044  */
00045 #define MAX_SALT_LEN 21
00046 
00047 static char salt_chars[] =
00048    "abcdefghijklmnopqrstuvwxyz"
00049    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00050    "0123456789"
00051    "./";
00052 
00053 /*! Randomly select a character for a salt string */
00054 static char gen_salt_char(void)
00055 {
00056    int which = ast_random_double() * 64;
00057    return salt_chars[which];
00058 }
00059 
00060 /*!
00061  * \brief Generates a salt to try with crypt.
00062  *
00063  * If given an empty string, will generate a salt for the most secure algorithm
00064  * to try with crypt(). If given a previously generated salt, the algorithm will
00065  * be lowered by one level of security.
00066  *
00067  * \param[out] current_salt Output string in which to generate the salt.
00068  *                          This can be an empty string, or the results of a
00069  *                          prior gen_salt call.
00070  * \param max_len Length of \a current_salt.
00071  * \return 0 on success.
00072  * \return Non-zero on error.
00073  */
00074 static int gen_salt(char *current_salt, size_t maxlen)
00075 {
00076    int i;
00077 
00078    if (maxlen < MAX_SALT_LEN || current_salt == NULL) {
00079       return -1;
00080    }
00081 
00082    switch (current_salt[0]) {
00083    case '\0':
00084       /* Initial generation; $6$ = SHA-512 */
00085       *current_salt++ = '$';
00086       *current_salt++ = '6';
00087       *current_salt++ = '$';
00088       for (i = 0; i < 16; ++i) {
00089          *current_salt++ = gen_salt_char();
00090       }
00091       *current_salt++ = '$';
00092       *current_salt++ = '\0';
00093       return 0;
00094    case '$':
00095       switch (current_salt[1]) {
00096       case '6':
00097          /* Downgrade to SHA-256 */
00098          current_salt[1] = '5';
00099          return 0;
00100       case '5':
00101          /* Downgrade to MD5 */
00102          current_salt[1] = '1';
00103          return 0;
00104       case '1':
00105          /* Downgrade to traditional crypt */
00106          *current_salt++ = gen_salt_char();
00107          *current_salt++ = gen_salt_char();
00108          *current_salt++ = '\0';
00109          return 0;
00110       default:
00111          /* Unrecognized algorithm */
00112          return -1;
00113       }
00114    default:
00115       /* Was already as insecure as it gets */
00116       return -1;
00117    }
00118 
00119 }
00120 
00121 #if defined(HAVE_CRYPT_R)
00122 
00123 char *ast_crypt(const char *key, const char *salt)
00124 {
00125    struct crypt_data data = {};
00126    const char *crypted = crypt_r(key, salt, &data);
00127 
00128    /* Crypt may return success even if it doesn't recognize the salt. But
00129     * in those cases it always mangles the salt in some way.
00130     */
00131    if (!crypted || !ast_begins_with(crypted, salt)) {
00132       return NULL;
00133    }
00134 
00135    return ast_strdup(crypted);
00136 }
00137 
00138 int ast_crypt_validate(const char *key, const char *expected)
00139 {
00140    struct crypt_data data = {};
00141    return strcmp(expected, crypt_r(key, expected, &data)) == 0;
00142 }
00143 
00144 #elif defined(HAVE_CRYPT)
00145 
00146 /* crypt is not reentrant. A global mutex is neither ideal nor perfect, but good
00147  * enough if crypt_r support is unavailable
00148  */
00149 AST_MUTEX_DEFINE_STATIC(crypt_mutex);
00150 
00151 char *ast_crypt(const char *key, const char *salt)
00152 {
00153    const char *crypted;
00154    SCOPED_MUTEX(lock, &crypt_mutex);
00155 
00156    crypted = crypt(key, salt);
00157 
00158    /* Crypt may return success even if it doesn't recognize the salt. But
00159     * in those cases it always mangles the salt in some way.
00160     */
00161    if (!crypted || !ast_begins_with(crypted, salt)) {
00162       return NULL;
00163    }
00164 
00165    return ast_strdup(crypted);
00166 }
00167 
00168 int ast_crypt_validate(const char *key, const char *expected)
00169 {
00170    SCOPED_MUTEX(lock, &crypt_mutex);
00171    return strcmp(expected, crypt(key, expected)) == 0;
00172 }
00173 
00174 #else /* No crypt support */
00175 
00176 char *ast_crypt(const char *key, const char *salt)
00177 {
00178    ast_log(LOG_WARNING,
00179       "crypt() support not available; cannot encrypt password\n");
00180    return NULL;
00181 }
00182 
00183 int ast_crypt_validate(const char *key, const char *expected)
00184 {
00185    ast_log(LOG_WARNING,
00186       "crypt() support not available; cannot validate password\n");
00187    return 0;
00188 }
00189 
00190 #endif  /* No crypt support */
00191 
00192 char *ast_crypt_encrypt(const char *key)
00193 {
00194    char salt[MAX_SALT_LEN] = {};
00195    while (gen_salt(salt, sizeof(salt)) == 0) {
00196       char *crypted = ast_crypt(key, salt);
00197       if (crypted) {
00198          return crypted;
00199       }
00200    }
00201    return NULL;
00202 }

Generated on Thu Apr 16 06:27:32 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6