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