ARI: Add ability to raise arbitrary User Events
[asterisk/asterisk.git] / main / crypt.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Asterisk wrapper for crypt(3)
22  * \author David M. Lee, II <dlee@digium.com>
23  */
24
25 /*** MODULEINFO
26         <support_level>core</support_level>
27  ***/
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <unistd.h>
34 #if defined(HAVE_CRYPT_R)
35 #include <crypt.h>
36 #endif
37
38 #include "asterisk/utils.h"
39
40 /*!
41  * \brief Max length of a salt string.
42  *
43  * $[1,5,6]$[a–zA–Z0–9./]{1,16}$, plus null terminator
44  */
45 #define MAX_SALT_LEN 21
46
47 static char salt_chars[] =
48         "abcdefghijklmnopqrstuvwxyz"
49         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
50         "0123456789"
51         "./";
52
53 /*! Randomly select a character for a salt string */
54 static char gen_salt_char(void)
55 {
56         int which = ast_random_double() * 64;
57         return salt_chars[which];
58 }
59
60 /*!
61  * \brief Generates a salt to try with crypt.
62  *
63  * If given an empty string, will generate a salt for the most secure algorithm
64  * to try with crypt(). If given a previously generated salt, the algorithm will
65  * be lowered by one level of security.
66  *
67  * \param[out] current_salt Output string in which to generate the salt.
68  *                          This can be an empty string, or the results of a
69  *                          prior gen_salt call.
70  * \param max_len Length of \a current_salt.
71  * \return 0 on success.
72  * \return Non-zero on error.
73  */
74 static int gen_salt(char *current_salt, size_t maxlen)
75 {
76         int i;
77
78         if (maxlen < MAX_SALT_LEN || current_salt == NULL) {
79                 return -1;
80         }
81
82         switch (current_salt[0]) {
83         case '\0':
84                 /* Initial generation; $6$ = SHA-512 */
85                 *current_salt++ = '$';
86                 *current_salt++ = '6';
87                 *current_salt++ = '$';
88                 for (i = 0; i < 16; ++i) {
89                         *current_salt++ = gen_salt_char();
90                 }
91                 *current_salt++ = '$';
92                 *current_salt++ = '\0';
93                 return 0;
94         case '$':
95                 switch (current_salt[1]) {
96                 case '6':
97                         /* Downgrade to SHA-256 */
98                         current_salt[1] = '5';
99                         return 0;
100                 case '5':
101                         /* Downgrade to MD5 */
102                         current_salt[1] = '1';
103                         return 0;
104                 case '1':
105                         /* Downgrade to traditional crypt */
106                         *current_salt++ = gen_salt_char();
107                         *current_salt++ = gen_salt_char();
108                         *current_salt++ = '\0';
109                         return 0;
110                 default:
111                         /* Unrecognized algorithm */
112                         return -1;
113                 }
114         default:
115                 /* Was already as insecure as it gets */
116                 return -1;
117         }
118
119 }
120
121 #if defined(HAVE_CRYPT_R)
122
123 char *ast_crypt(const char *key, const char *salt)
124 {
125         struct crypt_data data = {};
126         const char *crypted = crypt_r(key, salt, &data);
127
128         /* Crypt may return success even if it doesn't recognize the salt. But
129          * in those cases it always mangles the salt in some way.
130          */
131         if (!crypted || !ast_begins_with(crypted, salt)) {
132                 return NULL;
133         }
134
135         return ast_strdup(crypted);
136 }
137
138 int ast_crypt_validate(const char *key, const char *expected)
139 {
140         struct crypt_data data = {};
141         return strcmp(expected, crypt_r(key, expected, &data)) == 0;
142 }
143
144 #elif defined(HAVE_CRYPT)
145
146 /* crypt is not reentrant. A global mutex is neither ideal nor perfect, but good
147  * enough if crypt_r support is unavailable
148  */
149 AST_MUTEX_DEFINE_STATIC(crypt_mutex);
150
151 char *ast_crypt(const char *key, const char *salt)
152 {
153         const char *crypted;
154         SCOPED_MUTEX(lock, &crypt_mutex);
155
156         crypted = crypt(key, salt);
157
158         /* Crypt may return success even if it doesn't recognize the salt. But
159          * in those cases it always mangles the salt in some way.
160          */
161         if (!crypted || !ast_begins_with(crypted, salt)) {
162                 return NULL;
163         }
164
165         return ast_strdup(crypted);
166 }
167
168 int ast_crypt_validate(const char *key, const char *expected)
169 {
170         SCOPED_MUTEX(lock, &crypt_mutex);
171         return strcmp(expected, crypt(key, expected)) == 0;
172 }
173
174 #else /* No crypt support */
175
176 char *ast_crypt(const char *key, const char *salt)
177 {
178         ast_log(LOG_WARNING,
179                 "crypt() support not available; cannot encrypt password\n");
180         return NULL;
181 }
182
183 int ast_crypt_validate(const char *key, const char *expected)
184 {
185         ast_log(LOG_WARNING,
186                 "crypt() support not available; cannot validate password\n");
187         return 0;
188 }
189
190 #endif  /* No crypt support */
191
192 char *ast_crypt_encrypt(const char *key)
193 {
194         char salt[MAX_SALT_LEN] = {};
195         while (gen_salt(salt, sizeof(salt)) == 0) {
196                 char *crypted = ast_crypt(key, salt);
197                 if (crypted) {
198                         return crypted;
199                 }
200         }
201         return NULL;
202 }