OpenVAS Libraries  9.0.3
pwpolicy.c
Go to the documentation of this file.
1 /* openvas-libraries/base
2  * $Id$
3  * Description: Check a password policy
4  *
5  * Authors:
6  * Werner Koch <wk@gnupg.org>
7  *
8  * Copyright:
9  * Copyright (C) 2013 Greenbone Networks GmbH
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 
40 #include "pwpolicy.h"
41 
42 
43 #ifndef DIM
44 # define DIM(v) (sizeof(v)/sizeof((v)[0]))
45 # define DIMof(type,member) DIM(((type *)0)->member)
46 #endif
47 
48 #undef G_LOG_DOMAIN
49 
52 #define G_LOG_DOMAIN "base plcy"
53 
115 #define PWPOLICY_FILE_NAME OPENVAS_SYSCONF_DIR "/pwpolicy.conf"
116 
120 static gboolean disable_password_policy;
121 
122 
123 
128 static char *
129 policy_checking_failed (void)
130 {
131  return g_strdup ("Password policy checking failed (internal error)");
132 }
133 
134 
147 static char *
148 is_keyword (char *string, const char *keyword)
149 {
150  int n = strlen (keyword);
151 
152  if (!strncmp (string, keyword, n))
153  {
154  if (string[n] == ':') /* Skip the optional colon. */
155  n++;
156  if (!string[n] || g_ascii_isspace (string[n]))
157  {
158  string += n;
159  while (g_ascii_isspace (*string))
160  string++;
161  return string;
162  }
163  }
164  return NULL;
165 }
166 
167 
180 static int
181 search_file (const char *fname, const char *password)
182 {
183  FILE *fp;
184  int c;
185  size_t len;
186  char line[256];
187 
188  fp = fopen (fname, "r");
189  if (!fp)
190  return -1;
191 
192  while (fgets (line, DIM(line)-1, fp))
193  {
194  len = strlen (line);
195  if (!len || line[len-1] != '\n')
196  {
197  /* Incomplete last line or line too long. Eat until end of
198  line. */
199  while ( (c=getc (fp)) != EOF && c != '\n')
200  ;
201  continue;
202  }
203  line[--len] = 0; /* Chop the LF. */
204  if (len && line[len-1] == '\r')
205  line[--len] = 0; /* Chop an optional CR. */
206  if (!len)
207  continue; /* Empty */
208  if (!g_ascii_strcasecmp (line, password))
209  {
210  fclose (fp);
211  return 1; /* Found. */
212  }
213  }
214  if (ferror (fp))
215  {
216  int save_errno = errno;
217  fclose (fp);
218  errno = save_errno;
219  return -1; /* Read error. */
220  }
221  fclose (fp);
222  return 0; /* Not found. */
223 }
224 
225 
242 static char *
243 parse_pattern_line (char *line, const char *fname, int lineno,
244  char **descp, const char *password, const char *username)
245 {
246  char *ret = NULL;
247  char *p;
248  size_t n;
249 
250  /* Skip leading spaces. */
251  while (g_ascii_isspace (*line))
252  line++;
253 
254  if (!*line) /* Empty line. */
255  {
256  ret = NULL;
257  }
258  else if (*line == '#' && line[1] == '+') /* Processing instruction. */
259  {
260  line += 2;
261  if ((p = is_keyword (line, "desc")))
262  {
263  g_free (*descp);
264  if (*p)
265  *descp = g_strdup (p);
266  else
267  *descp = NULL;
268  }
269  else if ((p = is_keyword (line, "nodesc")))
270  {
271  g_free (*descp);
272  *descp = NULL;
273  }
274  else if ((p = is_keyword (line, "search")))
275  {
276  int sret;
277 
278  sret = search_file (p, password);
279  if (sret == -1)
280  {
281  g_warning ("error searching '%s' (requested at line %d): %s",
282  p, lineno, g_strerror (errno));
283  ret = policy_checking_failed ();
284  }
285  else if (sret && *descp)
286  ret = g_strdup_printf ("Weak password (%s)", *descp);
287  else if (sret)
288  ret = g_strdup_printf ("Weak password (found in '%s')", p);
289  else
290  ret = NULL;
291  }
292  else if (is_keyword (line, "username"))
293  {
294  /* Fixme: The include check is case sensitive and the strcmp
295  does only work with ascii. Changing this required a bit
296  more more (g_utf8_casefold) and also requires checking
297  for valid utf8 sequences in the password and all pattern. */
298  if (!username)
299  ret = NULL;
300  else if (!g_ascii_strcasecmp (password, username))
301  ret = g_strdup_printf ("Weak password (%s)",
302  "user name matches password");
303  else if (strstr (password, username))
304  ret = g_strdup_printf ("Weak password (%s)",
305  "user name is part of the password");
306  else if (strstr (username, password))
307  ret = g_strdup_printf ("Weak password (%s)",
308  "password is part of the user name");
309  else
310  ret = NULL;
311  }
312  else
313  {
314  g_warning ("error reading '%s', line %d: %s",
315  fname, lineno, "unknown processing instruction");
316  ret = policy_checking_failed ();
317  }
318  }
319  else if (*line == '#') /* Comment */
320  {
321  ret = NULL;
322  }
323  else if (*line == '/'
324  || (*line == '!' && line[1] == '/')) /* Regular expression. */
325  {
326  int rev = (*line == '!');
327  if (rev)
328  line++;
329  line++;
330  n = strlen (line);
331  if (n && line[n-1] == '/')
332  line[n-1] = 0;
333  if (((!g_regex_match_simple (line, password, G_REGEX_CASELESS, 0)) ^ rev))
334  ret = NULL;
335  else if (*descp)
336  ret = g_strdup_printf ("Weak password (%s)", *descp);
337  else
338  ret = g_strdup_printf ("Weak password (see '%s' line %d)",
339  fname, lineno);
340  }
341  else /* Simple string. */
342  {
343  if (g_ascii_strcasecmp (line, password))
344  ret = NULL;
345  else if (*descp)
346  ret = g_strdup_printf ("Weak password (%s)", *descp);
347  else
348  ret = g_strdup_printf ("Weak password (see '%s' line %d)",
349  fname, lineno);
350  }
351 
352  return ret;
353 }
354 
355 
366 char *
367 openvas_validate_password (const char *password, const char *username)
368 {
369  const char *patternfile = PWPOLICY_FILE_NAME;
370  char *ret;
371  FILE *fp;
372  int lineno;
373  size_t len;
374  char line[256];
375  char *desc = NULL;
376 
377  if (disable_password_policy)
378  return NULL;
379 
380  if (!password || !*password)
381  return g_strdup ("Empty password");
382 
383  fp = fopen (patternfile, "r");
384  if (!fp)
385  {
386  g_warning ("error opening '%s': %s", patternfile, g_strerror (errno));
387  return policy_checking_failed ();
388  }
389  lineno = 0;
390  ret = NULL;
391  while (fgets (line, DIM(line)-1, fp))
392  {
393  lineno++;
394  len = strlen (line);
395  if (!len || line[len-1] != '\n')
396  {
397  g_warning ("error reading '%s', line %d: %s",
398  patternfile, lineno,
399  len? "line too long":"line without a LF");
400  ret = policy_checking_failed ();
401  break;
402  }
403  line[--len] = 0; /* Chop the LF. */
404  if (len && line[len-1] == '\r')
405  line[--len] = 0; /* Chop an optional CR. */
406  ret = parse_pattern_line (line, patternfile, lineno, &desc,
407  password, username);
408  if (ret)
409  break;
410  }
411 
412  fclose (fp);
413  g_free (desc);
414  return ret;
415 }
416 
417 
421 void
423 {
424  disable_password_policy = TRUE;
425  g_warning ("Password policy checking has been disabled.");
426 }
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition: pwpolicy.c:115
void openvas_disable_password_policy(void)
Disable all password policy checking.
Definition: pwpolicy.c:422
Protos and data structures for pwpolicy checking.
#define DIM(v)
Definition: pwpolicy.c:44
char * openvas_validate_password(const char *password, const char *username)
Validate a password against the pattern file.
Definition: pwpolicy.c:367
gchar * string