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