Merge "res_calendar: Specialized calendars depend on symbols of general calendar."
[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 #include <unistd.h>
32 #if defined(HAVE_CRYPT_R) && !defined(__FreeBSD__)
33 #include <crypt.h>
34 #endif
35
36 #include "asterisk/utils.h"
37
38 /*!
39  * \brief Max length of a salt string.
40  *
41  * $[1,5,6]$[a–zA–Z0–9./]{1,16}$, plus null terminator
42  */
43 #define MAX_SALT_LEN 21
44
45 static char salt_chars[] =
46         "abcdefghijklmnopqrstuvwxyz"
47         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
48         "0123456789"
49         "./";
50
51 /*! Randomly select a character for a salt string */
52 static char gen_salt_char(void)
53 {
54         int which = ast_random_double() * 64;
55         return salt_chars[which];
56 }
57
58 /*!
59  * \brief Generates a salt to try with crypt.
60  *
61  * If given an empty string, will generate a salt for the most secure algorithm
62  * to try with crypt(). If given a previously generated salt, the algorithm will
63  * be lowered by one level of security.
64  *
65  * \param[out] current_salt Output string in which to generate the salt.
66  *                          This can be an empty string, or the results of a
67  *                          prior gen_salt call.
68  * \param max_len Length of \a current_salt.
69  * \return 0 on success.
70  * \return Non-zero on error.
71  */
72 static int gen_salt(char *current_salt, size_t maxlen)
73 {
74         int i;
75
76         if (maxlen < MAX_SALT_LEN || current_salt == NULL) {
77                 return -1;
78         }
79
80         switch (current_salt[0]) {
81         case '\0':
82                 /* Initial generation; $6$ = SHA-512 */
83                 *current_salt++ = '$';
84                 *current_salt++ = '6';
85                 *current_salt++ = '$';
86                 for (i = 0; i < 16; ++i) {
87                         *current_salt++ = gen_salt_char();
88                 }
89                 *current_salt++ = '$';
90                 *current_salt++ = '\0';
91                 return 0;
92         case '$':
93                 switch (current_salt[1]) {
94                 case '6':
95                         /* Downgrade to SHA-256 */
96                         current_salt[1] = '5';
97                         return 0;
98                 case '5':
99                         /* Downgrade to MD5 */
100                         current_salt[1] = '1';
101                         return 0;
102                 case '1':
103                         /* Downgrade to traditional crypt */
104                         *current_salt++ = gen_salt_char();
105                         *current_salt++ = gen_salt_char();
106                         *current_salt++ = '\0';
107                         return 0;
108                 default:
109                         /* Unrecognized algorithm */
110                         return -1;
111                 }
112         default:
113                 /* Was already as insecure as it gets */
114                 return -1;
115         }
116
117 }
118
119 #if defined(HAVE_CRYPT_R)
120
121 char *ast_crypt(const char *key, const char *salt)
122 {
123         struct crypt_data data = {};
124         const char *crypted = crypt_r(key, salt, &data);
125
126         /* Crypt may return success even if it doesn't recognize the salt. But
127          * in those cases it always mangles the salt in some way.
128          */
129         if (!crypted || !ast_begins_with(crypted, salt)) {
130                 return NULL;
131         }
132
133         return ast_strdup(crypted);
134 }
135
136 int ast_crypt_validate(const char *key, const char *expected)
137 {
138         struct crypt_data data = {};
139         return strcmp(expected, crypt_r(key, expected, &data)) == 0;
140 }
141
142 #elif defined(HAVE_CRYPT)
143
144 /* crypt is not reentrant. A global mutex is neither ideal nor perfect, but good
145  * enough if crypt_r support is unavailable
146  */
147 AST_MUTEX_DEFINE_STATIC(crypt_mutex);
148
149 char *ast_crypt(const char *key, const char *salt)
150 {
151         const char *crypted;
152         SCOPED_MUTEX(lock, &crypt_mutex);
153
154         crypted = crypt(key, salt);
155
156         /* Crypt may return success even if it doesn't recognize the salt. But
157          * in those cases it always mangles the salt in some way.
158          */
159         if (!crypted || !ast_begins_with(crypted, salt)) {
160                 return NULL;
161         }
162
163         return ast_strdup(crypted);
164 }
165
166 int ast_crypt_validate(const char *key, const char *expected)
167 {
168         SCOPED_MUTEX(lock, &crypt_mutex);
169         return strcmp(expected, crypt(key, expected)) == 0;
170 }
171
172 #else /* No crypt support */
173
174 char *ast_crypt(const char *key, const char *salt)
175 {
176         ast_log(LOG_WARNING,
177                 "crypt() support not available; cannot encrypt password\n");
178         return NULL;
179 }
180
181 int ast_crypt_validate(const char *key, const char *expected)
182 {
183         ast_log(LOG_WARNING,
184                 "crypt() support not available; cannot validate password\n");
185         return 0;
186 }
187
188 #endif  /* No crypt support */
189
190 char *ast_crypt_encrypt(const char *key)
191 {
192         char salt[MAX_SALT_LEN] = {};
193         while (gen_salt(salt, sizeof(salt)) == 0) {
194                 char *crypted = ast_crypt(key, salt);
195                 if (crypted) {
196                         return crypted;
197                 }
198         }
199         return NULL;
200 }