1 /* Copyright (C) 2020-2021 Greenbone Networks GmbH
2 *
3 * SPDX-License-Identifier: GPL-3.0-or-later
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "passwordbasedauthentication.h"
19 // internal usage to have access to gvm_auth initialized to verify if
20 // initialization is needed
21 #include "authutils.c"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 // UFC_crypt defines crypt_r when only when __USE_GNU is set
27 // this shouldn't affect other implementations
28 #define __USE_GNU
29 #if defined(__FreeBSD__) || defined(__DragonFly__)
30 #if HAS_CRYPT_R
31 #include <unistd.h>
32 // glibc compat
33 struct crypt_data {
34 int initialized; /* For compatibility with glibc. */
35 char __buf[256]; /* Buffer returned by crypt_r(). */
36 };
37 #endif
38 #else
39 #include <crypt.h>
40 #endif
41 // INVALID_HASH is used on verify when the given hash is a NULL pointer.
42 // This is done to not directly jump to exit with a INVALID_HASH result
43 // but rather keep calculating to make it a little bit harder to guess
44 // if a user exists or not based on timing.
45 #define INVALID_HASH "1234567890$"
46 #ifndef CRYPT_GENSALT_OUTPUT_SIZE
47 #define CRYPT_GENSALT_OUTPUT_SIZE 192
48 #endif
49
50 #ifndef CRYPT_OUTPUT_SIZE
51 #define CRYPT_OUTPUT_SIZE 384
52 #endif
53
54 int
is_prefix_supported(const char * id)55 is_prefix_supported (const char *id)
56 {
57 return strcmp (PREFIX_DEFAULT, id) == 0;
58 }
59
60 // we assume something else than libxcrypt > 3.1; like UFC-crypt
61 // libxcrypt sets a macro of crypt_gensalt_r to crypt_gensalt_rn
62 // therefore we could use that mechanism to figure out if we are on
63 // debian buster or newer.
64 #ifndef EXTERNAL_CRYPT_GENSALT_R
65
66 // used printables within salt
67 const char ascii64[64] =
68 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
69
70 /* Tries to get BUFLEN random bytes into BUF; returns 0 on success. */
71 int
get_random(char * buf,size_t buflen)72 get_random (char *buf, size_t buflen)
73 {
74 FILE *fp = fopen ("/dev/urandom", "r");
75 int result = 0;
76 if (fp == NULL)
77 {
78 result = -1;
79 goto exit;
80 }
81 size_t nread = fread (buf, 1, buflen, fp);
82 fclose (fp);
83 if (nread < buflen)
84 {
85 result = -2;
86 }
87
88 exit:
89 return result;
90 }
91 /* Generate a string suitable for use as the setting when hashing a passphrase.
92 * PREFIX controls which hash function will be used,
93 * COUNT controls the computional cost of the hash,
94 * RBYTES should point to NRBYTES bytes of random data.
95 *
96 * If PREFIX is a NULL pointer, the current best default is used; if RBYTES
97 * is a NULL pointer, random data will be retrieved from the operating system
98 * if possible.
99 *
100 * The generated setting string is written to OUTPUT, which is OUTPUT_SIZE long.
101 * OUTPUT_SIZE must be at least CRYPT_GENSALT_OUTPUT_SIZE.
102 *
103 * */
104 char *
crypt_gensalt_r(const char * prefix,unsigned long count,const char * rbytes,int nrbytes,char * output,int output_size)105 crypt_gensalt_r (const char *prefix, unsigned long count, const char *rbytes,
106 int nrbytes, char *output, int output_size)
107 {
108 char *internal_rbytes = NULL;
109 unsigned int written = 0, used = 0;
110 unsigned long value = 0;
111 if ((rbytes != NULL && nrbytes < 3) || output_size < 16
112 || !is_prefix_supported (prefix))
113 {
114 output[0] = '*';
115 goto exit;
116 }
117 if (rbytes == NULL)
118 {
119 internal_rbytes = malloc (16);
120 if (get_random (internal_rbytes, 16) != 0)
121 {
122 output[0] = '*';
123 goto exit;
124 }
125 nrbytes = 16;
126 rbytes = internal_rbytes;
127 }
128 written = snprintf (output, output_size, "%srounds=%lu$",
129 prefix == NULL ? PREFIX_DEFAULT : prefix, count);
130 while (written + 5 < (unsigned int) output_size
131 && used + 3 < (unsigned int) nrbytes && (used * 4 / 3) < 16)
132 {
133 value = ((unsigned long) rbytes[used + 0] << 0)
134 | ((unsigned long) rbytes[used + 1] << 8)
135 | ((unsigned long) rbytes[used + 2] << 16);
136 output[written] = ascii64[value & 0x3f];
137 output[written + 1] = ascii64[(value >> 6) & 0x3f];
138 output[written + 2] = ascii64[(value >> 12) & 0x3f];
139 output[written + 3] = ascii64[(value >> 18) & 0x3f];
140 written += 4;
141 used += 3;
142 }
143 output[written] = '\0';
144 exit:
145 if (internal_rbytes != NULL)
146 free (internal_rbytes);
147 return output[0] == '*' ? 0 : output;
148 }
149
150 #endif
151
152 struct PBASettings *
pba_init(const char * pepper,unsigned int pepper_size,unsigned int count,char * prefix)153 pba_init (const char *pepper, unsigned int pepper_size, unsigned int count,
154 char *prefix)
155 {
156 unsigned int i = 0;
157 struct PBASettings *result = NULL;
158 if (pepper_size > MAX_PEPPER_SIZE)
159 goto exit;
160 if (prefix != NULL && !is_prefix_supported (prefix))
161 goto exit;
162 result = malloc (sizeof (struct PBASettings));
163 for (i = 0; i < MAX_PEPPER_SIZE; i++)
164 result->pepper[i] = pepper != NULL && i < pepper_size ? pepper[i] : 0;
165 result->count = count == 0 ? COUNT_DEFAULT : count;
166 result->prefix = prefix == NULL ? PREFIX_DEFAULT : prefix;
167 exit:
168 return result;
169 }
170
171 void
pba_finalize(struct PBASettings * settings)172 pba_finalize (struct PBASettings *settings)
173 {
174 free (settings);
175 }
176
177 int
pba_is_phc_compliant(const char * setting)178 pba_is_phc_compliant (const char *setting)
179 {
180 if (setting == NULL)
181 {
182 return 1;
183 }
184 return strlen (setting) > 1 && setting[0] == '$';
185 }
186
187 #if HAS_CRYPT_R
188 char *
pba_hash(struct PBASettings * setting,const char * password)189 pba_hash (struct PBASettings *setting, const char *password)
190 {
191 char *result = NULL, *settings = NULL, *tmp, *rslt;
192 int i;
193 struct crypt_data *data = NULL;
194
195 if (!setting || !password)
196 goto exit;
197 if (!is_prefix_supported (setting->prefix))
198 goto exit;
199 settings = malloc (CRYPT_GENSALT_OUTPUT_SIZE);
200 if (crypt_gensalt_r (setting->prefix, setting->count, NULL, 0, settings,
201 CRYPT_GENSALT_OUTPUT_SIZE)
202 == NULL)
203 goto exit;
204 tmp = settings + strlen (settings) - 1;
205 for (i = MAX_PEPPER_SIZE - 1; i > -1; i--)
206 {
207 if (setting->pepper[i] != 0)
208 tmp[0] = setting->pepper[i];
209 tmp--;
210 }
211
212 data = calloc (1, sizeof (struct crypt_data));
213 rslt = crypt_r (password, settings, data);
214 if (rslt == NULL)
215 goto exit;
216 result = malloc (CRYPT_OUTPUT_SIZE);
217 strncpy (result, rslt, CRYPT_OUTPUT_SIZE);
218 // remove pepper, by jumping to begin of applied pepper within result
219 // and overriding it.
220 tmp = result + (tmp - settings);
221 for (i = 0; i < MAX_PEPPER_SIZE; i++)
222 {
223 tmp++;
224 if (setting->pepper[i] != 0)
225 tmp[0] = '0';
226 }
227 exit:
228 if (data != NULL)
229 free (data);
230 if (settings != NULL)
231 free (settings);
232 return result;
233 }
234
235 enum pba_rc
pba_verify_hash(const struct PBASettings * setting,const char * hash,const char * password)236 pba_verify_hash (const struct PBASettings *setting, const char *hash,
237 const char *password)
238 {
239 char *cmp, *tmp = NULL;
240 struct crypt_data *data = NULL;
241 int i = 0;
242 enum pba_rc result = ERR;
243 if (!setting)
244 goto exit;
245 if (!is_prefix_supported (setting->prefix))
246 goto exit;
247 if (pba_is_phc_compliant (hash) != 0)
248 {
249 data = calloc (1, sizeof (struct crypt_data));
250 // manipulate hash to reapply pepper
251 tmp = malloc (CRYPT_OUTPUT_SIZE);
252 strncpy (tmp, hash ? hash : INVALID_HASH, CRYPT_OUTPUT_SIZE);
253 cmp = strrchr (tmp, '$');
254 for (i = MAX_PEPPER_SIZE - 1; i > -1; i--)
255 {
256 cmp--;
257 if (setting->pepper[i] != 0)
258 cmp[0] = setting->pepper[i];
259 }
260 // some crypt_r implementations cannot handle if password is a
261 // NULL pointer and run into SEGMENTATION faults.
262 // Therefore we set it to ""
263 cmp = crypt_r (password ? password : "", tmp, data);
264 if (strcmp (tmp, cmp) == 0)
265 result = VALID;
266 else
267 result = INVALID;
268 }
269 else
270 {
271 // assume authutils hash handling
272 // initialize gvm_auth utils if not already initialized
273 if (initialized == FALSE && gvm_auth_init () != 0)
274 {
275 goto exit;
276 }
277 // verify result of gvm_authenticate_classic
278 i = gvm_authenticate_classic (NULL, password, hash);
279 if (i == 0)
280 result = UPDATE_RECOMMENDED;
281 else if (i == 1)
282 result = INVALID;
283 }
284 exit:
285 if (data != NULL)
286 free (data);
287 if (tmp != NULL)
288 free (tmp);
289 return result;
290 }
291 #else
pba_hash(struct PBASettings * setting,const char * password)292 char *pba_hash (struct PBASettings *setting, const char *password) { return NULL; }
pba_verify_hash(const struct PBASettings * setting,const char * hash,const char * password)293 enum pba_rc pba_verify_hash (const struct PBASettings *setting, const char *hash, const char *password) { return ERR; }
294 #endif /* #if HAS_CRYPT_R */
295