Greenbone Vulnerability Management Libraries  22.8.0
pwpolicy.c
Go to the documentation of this file.
1 /* SPDX-FileCopyrightText: 2013-2023 Greenbone AG
2  *
3  * SPDX-License-Identifier: GPL-2.0-or-later
4  */
5 
14 #include "pwpolicy.h"
15 
16 #include <errno.h> /* for errno */
17 #include <glib.h> /* for g_strdup_printf, g_ascii_strcasecmp, g_free, ... */
18 #include <stdio.h> /* for fclose, fgets, fopen, FILE, ferror, EOF, getc */
19 #include <stdlib.h>
20 #include <string.h> /* for strstr, strlen, strncmp */
21 
22 #ifndef DIM
23 #define DIM(v) (sizeof (v) / sizeof ((v)[0]))
24 #define DIMof(type, member) DIM (((type *) 0)->member)
25 #endif
26 
27 #undef G_LOG_DOMAIN
28 
31 #define G_LOG_DOMAIN "libgvm base"
32 
94 #define PWPOLICY_FILE_NAME GVM_SYSCONF_DIR "/pwpolicy.conf"
95 
99 static gboolean disable_password_policy;
100 
105 static char *
107 {
108  return g_strdup ("Password policy checking failed (internal error)");
109 }
110 
123 static char *
124 is_keyword (char *string, const char *keyword)
125 {
126  int idx, slen;
127  char *tmp;
128  idx = strlen (keyword);
129  slen = strlen (string);
130 
131  if (!strncmp (string, keyword, idx))
132  {
133  tmp = string + idx;
134  if (tmp - string > slen)
135  return NULL;
136  // skip optional:
137  if (*tmp == ':')
138  tmp++;
139  if (tmp - string > slen)
140  return NULL;
141 
142  for (; tmp - string < slen && g_ascii_isspace (*tmp); tmp++)
143  {
144  // skip whitespace
145  }
146  return tmp;
147  }
148  return NULL;
149 }
150 
163 static int
164 search_file (const char *fname, const char *password)
165 {
166  FILE *fp;
167  int c;
168  char line[256];
169 
170  fp = fopen (fname, "r");
171  if (!fp)
172  return -1;
173 
174  while (fgets (line, DIM (line) - 1, fp))
175  {
176  size_t len;
177 
178  len = strlen (line);
179  if (!len || line[len - 1] != '\n')
180  {
181  /* Incomplete last line or line too long. Eat until end of
182  line. */
183  while ((c = getc (fp)) != EOF && c != '\n')
184  ;
185  continue;
186  }
187  line[--len] = 0; /* Chop the LF. */
188  if (len && line[len - 1] == '\r')
189  line[--len] = 0; /* Chop an optional CR. */
190  if (!len)
191  continue; /* Empty */
192  if (!g_ascii_strcasecmp (line, password))
193  {
194  fclose (fp);
195  return 1; /* Found. */
196  }
197  }
198  if (ferror (fp))
199  {
200  int save_errno = errno;
201  fclose (fp);
202  errno = save_errno;
203  return -1; /* Read error. */
204  }
205  fclose (fp);
206  return 0; /* Not found. */
207 }
208 
225 static char *
226 parse_pattern_line (char *line, const char *fname, int lineno, char **descp,
227  const char *password, const char *username)
228 {
229  char *ret = NULL;
230  char *p;
231  size_t n;
232 
233  /* Skip leading spaces. */
234  while (g_ascii_isspace (*line))
235  line++;
236 
237  if (!*line) /* Empty line. */
238  {
239  ret = NULL;
240  }
241  else if (*line == '#' && line[1] == '+') /* Processing instruction. */
242  {
243  line += 2;
244  if ((p = is_keyword (line, "desc")))
245  {
246  g_free (*descp);
247  if (*p)
248  *descp = g_strdup (p);
249  else
250  *descp = NULL;
251  }
252  else if ((is_keyword (line, "nodesc")))
253  {
254  g_free (*descp);
255  *descp = NULL;
256  }
257  else if ((p = is_keyword (line, "search")))
258  {
259  int sret;
260 
261  sret = search_file (p, password);
262  if (sret == -1)
263  {
264  g_warning ("error searching '%s' (requested at line %d): %s", p,
265  lineno, g_strerror (errno));
266  ret = policy_checking_failed ();
267  }
268  else if (sret && *descp)
269  ret = g_strdup_printf ("Weak password (%s)", *descp);
270  else if (sret)
271  ret = g_strdup_printf ("Weak password (found in '%s')", p);
272  else
273  ret = NULL;
274  }
275  else if (is_keyword (line, "username"))
276  {
277  /* Fixme: The include check is case sensitive and the strcmp
278  does only work with ascii. Changing this required a bit
279  more more (g_utf8_casefold) and also requires checking
280  for valid utf8 sequences in the password and all pattern. */
281  if (!username)
282  ret = NULL;
283  else if (!g_ascii_strcasecmp (password, username))
284  ret = g_strdup_printf ("Weak password (%s)",
285  "user name matches password");
286  else if (strstr (password, username))
287  ret = g_strdup_printf ("Weak password (%s)",
288  "user name is part of the password");
289  else if (strstr (username, password))
290  ret = g_strdup_printf ("Weak password (%s)",
291  "password is part of the user name");
292  else
293  ret = NULL;
294  }
295  else
296  {
297  g_warning ("error reading '%s', line %d: %s", fname, lineno,
298  "unknown processing instruction");
299  ret = policy_checking_failed ();
300  }
301  }
302  else if (*line == '#') /* Comment */
303  {
304  ret = NULL;
305  }
306  else if (*line == '/'
307  || (*line == '!' && line[1] == '/')) /* Regular expression. */
308  {
309  int rev = (*line == '!');
310  if (rev)
311  line++;
312  line++;
313  n = strlen (line);
314  if (n && line[n - 1] == '/')
315  line[n - 1] = 0;
316  if (((!g_regex_match_simple (line, password, G_REGEX_CASELESS, 0)) ^ rev))
317  ret = NULL;
318  else if (*descp)
319  ret = g_strdup_printf ("Weak password (%s)", *descp);
320  else
321  ret =
322  g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
323  }
324  else /* Simple string. */
325  {
326  if (g_ascii_strcasecmp (line, password))
327  ret = NULL;
328  else if (*descp)
329  ret = g_strdup_printf ("Weak password (%s)", *descp);
330  else
331  ret =
332  g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
333  }
334 
335  return ret;
336 }
337 
348 char *
349 gvm_validate_password (const char *password, const char *username)
350 {
351  const char *patternfile = PWPOLICY_FILE_NAME;
352  char *ret;
353  FILE *fp;
354  int lineno;
355  char line[256];
356  char *desc = NULL;
357 
359  return NULL;
360 
361  if (!password || !*password)
362  return g_strdup ("Empty password");
363 
364  fp = fopen (patternfile, "r");
365  if (!fp)
366  {
367  g_warning ("error opening '%s': %s", patternfile, g_strerror (errno));
368  return policy_checking_failed ();
369  }
370  lineno = 0;
371  ret = NULL;
372  while (fgets (line, DIM (line) - 1, fp))
373  {
374  size_t len;
375 
376  lineno++;
377  len = strlen (line);
378  if (!len || line[len - 1] != '\n')
379  {
380  g_warning ("error reading '%s', line %d: %s", patternfile, lineno,
381  len ? "line too long" : "line without a LF");
382  ret = policy_checking_failed ();
383  break;
384  }
385  line[--len] = 0; /* Chop the LF. */
386  if (len && line[len - 1] == '\r')
387  line[--len] = 0; /* Chop an optional CR. */
388  ret = parse_pattern_line (line, patternfile, lineno, &desc, password,
389  username);
390  if (ret)
391  break;
392 
393  bzero (line, sizeof (line));
394  }
395 
396  fclose (fp);
397  g_free (desc);
398  return ret;
399 }
400 
404 void
406 {
408  g_warning ("Password policy checking has been disabled.");
409 }
parse_pattern_line
static char * parse_pattern_line(char *line, const char *fname, int lineno, char **descp, const char *password, const char *username)
Parse one line of a pettern file.
Definition: pwpolicy.c:226
search_file
static int search_file(const char *fname, const char *password)
Search a file for a matching line.
Definition: pwpolicy.c:164
PWPOLICY_FILE_NAME
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition: pwpolicy.c:94
DIM
#define DIM(v)
Definition: pwpolicy.c:23
policy_checking_failed
static char * policy_checking_failed(void)
Definition: pwpolicy.c:106
pwpolicy.h
Protos and data structures for pwpolicy checking.
gvm_disable_password_policy
void gvm_disable_password_policy(void)
Disable all password policy checking.
Definition: pwpolicy.c:405
disable_password_policy
static gboolean disable_password_policy
Flag indicating that passwords are not checked.
Definition: pwpolicy.c:99
is_keyword
static char * is_keyword(char *string, const char *keyword)
Check whether a string starts with a keyword.
Definition: pwpolicy.c:124
gvm_validate_password
char * gvm_validate_password(const char *password, const char *username)
Validate a password against the pattern file.
Definition: pwpolicy.c:349