Greenbone Vulnerability Management Libraries  10.0.0
pwpolicy.c
Go to the documentation of this file.
1 /* Copyright (C) 2013-2019 Greenbone Networks GmbH
2  *
3  * SPDX-License-Identifier: GPL-2.0-or-later
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (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, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
28 #include "pwpolicy.h"
29 
30 #include <errno.h> /* for errno */
31 #include <glib.h> /* for g_strdup_printf, g_ascii_strcasecmp, g_free, ... */
32 #include <stdio.h> /* for fclose, fgets, fopen, FILE, ferror, EOF, getc */
33 #include <stdlib.h>
34 #include <string.h> /* for strstr, strlen, strncmp */
35 
36 #ifndef DIM
37 #define DIM(v) (sizeof (v) / sizeof ((v)[0]))
38 #define DIMof(type, member) DIM (((type *) 0)->member)
39 #endif
40 
41 #undef G_LOG_DOMAIN
42 
45 #define G_LOG_DOMAIN "base plcy"
46 
108 #define PWPOLICY_FILE_NAME GVM_SYSCONF_DIR "/pwpolicy.conf"
109 
113 static gboolean disable_password_policy;
114 
119 static char *
121 {
122  return g_strdup ("Password policy checking failed (internal error)");
123 }
124 
137 static char *
138 is_keyword (char *string, const char *keyword)
139 {
140  int n = strlen (keyword);
141 
142  if (!strncmp (string, keyword, n))
143  {
144  if (string[n] == ':') /* Skip the optional colon. */
145  n++;
146  if (!string[n] || g_ascii_isspace (string[n]))
147  {
148  string += n;
149  while (g_ascii_isspace (*string))
150  string++;
151  return string;
152  }
153  }
154  return NULL;
155 }
156 
169 static int
170 search_file (const char *fname, const char *password)
171 {
172  FILE *fp;
173  int c;
174  char line[256];
175 
176  fp = fopen (fname, "r");
177  if (!fp)
178  return -1;
179 
180  while (fgets (line, DIM (line) - 1, fp))
181  {
182  size_t len;
183 
184  len = strlen (line);
185  if (!len || line[len - 1] != '\n')
186  {
187  /* Incomplete last line or line too long. Eat until end of
188  line. */
189  while ((c = getc (fp)) != EOF && c != '\n')
190  ;
191  continue;
192  }
193  line[--len] = 0; /* Chop the LF. */
194  if (len && line[len - 1] == '\r')
195  line[--len] = 0; /* Chop an optional CR. */
196  if (!len)
197  continue; /* Empty */
198  if (!g_ascii_strcasecmp (line, password))
199  {
200  fclose (fp);
201  return 1; /* Found. */
202  }
203  }
204  if (ferror (fp))
205  {
206  int save_errno = errno;
207  fclose (fp);
208  errno = save_errno;
209  return -1; /* Read error. */
210  }
211  fclose (fp);
212  return 0; /* Not found. */
213 }
214 
231 static char *
232 parse_pattern_line (char *line, const char *fname, int lineno, char **descp,
233  const char *password, const char *username)
234 {
235  char *ret = NULL;
236  char *p;
237  size_t n;
238 
239  /* Skip leading spaces. */
240  while (g_ascii_isspace (*line))
241  line++;
242 
243  if (!*line) /* Empty line. */
244  {
245  ret = NULL;
246  }
247  else if (*line == '#' && line[1] == '+') /* Processing instruction. */
248  {
249  line += 2;
250  if ((p = is_keyword (line, "desc")))
251  {
252  g_free (*descp);
253  if (*p)
254  *descp = g_strdup (p);
255  else
256  *descp = NULL;
257  }
258  else if ((p = is_keyword (line, "nodesc")))
259  {
260  g_free (*descp);
261  *descp = NULL;
262  }
263  else if ((p = is_keyword (line, "search")))
264  {
265  int sret;
266 
267  sret = search_file (p, password);
268  if (sret == -1)
269  {
270  g_warning ("error searching '%s' (requested at line %d): %s", p,
271  lineno, g_strerror (errno));
272  ret = policy_checking_failed ();
273  }
274  else if (sret && *descp)
275  ret = g_strdup_printf ("Weak password (%s)", *descp);
276  else if (sret)
277  ret = g_strdup_printf ("Weak password (found in '%s')", p);
278  else
279  ret = NULL;
280  }
281  else if (is_keyword (line, "username"))
282  {
283  /* Fixme: The include check is case sensitive and the strcmp
284  does only work with ascii. Changing this required a bit
285  more more (g_utf8_casefold) and also requires checking
286  for valid utf8 sequences in the password and all pattern. */
287  if (!username)
288  ret = NULL;
289  else if (!g_ascii_strcasecmp (password, username))
290  ret = g_strdup_printf ("Weak password (%s)",
291  "user name matches password");
292  else if (strstr (password, username))
293  ret = g_strdup_printf ("Weak password (%s)",
294  "user name is part of the password");
295  else if (strstr (username, password))
296  ret = g_strdup_printf ("Weak password (%s)",
297  "password is part of the user name");
298  else
299  ret = NULL;
300  }
301  else
302  {
303  g_warning ("error reading '%s', line %d: %s", fname, lineno,
304  "unknown processing instruction");
305  ret = policy_checking_failed ();
306  }
307  }
308  else if (*line == '#') /* Comment */
309  {
310  ret = NULL;
311  }
312  else if (*line == '/'
313  || (*line == '!' && line[1] == '/')) /* Regular expression. */
314  {
315  int rev = (*line == '!');
316  if (rev)
317  line++;
318  line++;
319  n = strlen (line);
320  if (n && line[n - 1] == '/')
321  line[n - 1] = 0;
322  if (((!g_regex_match_simple (line, password, G_REGEX_CASELESS, 0)) ^ rev))
323  ret = NULL;
324  else if (*descp)
325  ret = g_strdup_printf ("Weak password (%s)", *descp);
326  else
327  ret =
328  g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
329  }
330  else /* Simple string. */
331  {
332  if (g_ascii_strcasecmp (line, password))
333  ret = NULL;
334  else if (*descp)
335  ret = g_strdup_printf ("Weak password (%s)", *descp);
336  else
337  ret =
338  g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
339  }
340 
341  return ret;
342 }
343 
354 char *
355 gvm_validate_password (const char *password, const char *username)
356 {
357  const char *patternfile = PWPOLICY_FILE_NAME;
358  char *ret;
359  FILE *fp;
360  int lineno;
361  char line[256];
362  char *desc = NULL;
363 
365  return NULL;
366 
367  if (!password || !*password)
368  return g_strdup ("Empty password");
369 
370  fp = fopen (patternfile, "r");
371  if (!fp)
372  {
373  g_warning ("error opening '%s': %s", patternfile, g_strerror (errno));
374  return policy_checking_failed ();
375  }
376  lineno = 0;
377  ret = NULL;
378  while (fgets (line, DIM (line) - 1, fp))
379  {
380  size_t len;
381 
382  lineno++;
383  len = strlen (line);
384  if (!len || line[len - 1] != '\n')
385  {
386  g_warning ("error reading '%s', line %d: %s", patternfile, lineno,
387  len ? "line too long" : "line without a LF");
388  ret = policy_checking_failed ();
389  break;
390  }
391  line[--len] = 0; /* Chop the LF. */
392  if (len && line[len - 1] == '\r')
393  line[--len] = 0; /* Chop an optional CR. */
394  ret = parse_pattern_line (line, patternfile, lineno, &desc, password,
395  username);
396  if (ret)
397  break;
398 
399  bzero (line, sizeof (line));
400  }
401 
402  fclose (fp);
403  g_free (desc);
404  return ret;
405 }
406 
410 void
412 {
414  g_warning ("Password policy checking has been disabled.");
415 }
char * gvm_validate_password(const char *password, const char *username)
Validate a password against the pattern file.
Definition: pwpolicy.c:355
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition: pwpolicy.c:108
Protos and data structures for pwpolicy checking.
#define DIM(v)
Definition: pwpolicy.c:37
static char * policy_checking_failed(void)
Definition: pwpolicy.c:120
static int search_file(const char *fname, const char *password)
Search a file for a matching line.
Definition: pwpolicy.c:170
void gvm_disable_password_policy(void)
Disable all password policy checking.
Definition: pwpolicy.c:411
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:232
static gboolean disable_password_policy
Flag indicating that passwords are not checked.
Definition: pwpolicy.c:113
static char * is_keyword(char *string, const char *keyword)
Check whether a string starts with a keyword.
Definition: pwpolicy.c:138