Greenbone Vulnerability Management Libraries  11.0.0
pwpolicy.c File Reference

Check passwords against a list of pattern. More...

#include "pwpolicy.h"
#include <errno.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Include dependency graph for pwpolicy.c:

Go to the source code of this file.

Macros

#define DIM(v)   (sizeof (v) / sizeof ((v)[0]))
 
#define DIMof(type, member)   DIM (((type *) 0)->member)
 
#define G_LOG_DOMAIN   "base plcy"
 GLib log domain. More...
 
#define PWPOLICY_FILE_NAME   GVM_SYSCONF_DIR "/pwpolicy.conf"
 The name of the pattern file. More...
 

Functions

static char * policy_checking_failed (void)
 
static char * is_keyword (char *string, const char *keyword)
 Check whether a string starts with a keyword. More...
 
static int search_file (const char *fname, const char *password)
 Search a file for a matching line. More...
 
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. More...
 
char * gvm_validate_password (const char *password, const char *username)
 Validate a password against the pattern file. More...
 
void gvm_disable_password_policy (void)
 Disable all password policy checking. More...
 

Variables

static gboolean disable_password_policy
 Flag indicating that passwords are not checked. More...
 

Detailed Description

Check passwords against a list of pattern.

See PWPOLICY_FILE_NAME for a syntax description of the pattern file.

Definition in file pwpolicy.c.

Macro Definition Documentation

◆ DIM

#define DIM (   v)    (sizeof (v) / sizeof ((v)[0]))

Definition at line 37 of file pwpolicy.c.

◆ DIMof

#define DIMof (   type,
  member 
)    DIM (((type *) 0)->member)

Definition at line 38 of file pwpolicy.c.

◆ G_LOG_DOMAIN

#define G_LOG_DOMAIN   "base plcy"

GLib log domain.

Definition at line 45 of file pwpolicy.c.

◆ PWPOLICY_FILE_NAME

#define PWPOLICY_FILE_NAME   GVM_SYSCONF_DIR "/pwpolicy.conf"

The name of the pattern file.

This file contains pattern with bad passphrases. The file is line based with maximum length of 255 bytes per line and expected to be in UTF-8 encoding. Each line may either be a comment line, a simple string, a regular expression or a processing instruction. The lines are parsed sequentially.

Comments are indicated by a hash mark ('#') as the first non white-space character of a line followed immediately by a space or end of line. Such a comment line is completely ignored.

Simple strings start after optional leading white-space. They are compared to the password under validation. The comparison is case insensitive for all ASCII characters.

Regular expressions start after optional leading white-space with either a single slash ('/') or an exclamation mark ('!') directly followed by a slash. They extend to the end of the line but may be terminated with another slash which may then only be followed by more white-space. The regular expression are Perl Compatible Regular Expressions (PCRE) and are by default case insensitive. If the regular expression line starts with the exclamation mark, the match is reversed; i.e. an error is returned if the password does not match.

Processing instructions are special comments to control the operation of the policy checking. The start like a comment but the hash mark is immediately followed by a plus ('+') signed, a keyword, an optional colon (':') and an optional value string. The following processing instructions are supported:

#+desc[:] STRING

This is used to return a meaningful error message. STRING is used a the description for all errors up to the next /desc/ or /nodesc/ processing instruction.

#+nodesc

This is syntactic sugar for /desc/ without a value. It switches back to a default error description (pattern file name and line number).

#+search[:] FILENAME

This searches the file with name FILENAME for a match. The comparison is case insensitive for all ASCII characters. This is a simple linear search and stops at the first match. Comments are not allowed in that file. A line in that file may not be longer than 255 characters. An example for such a file is "/usr/share/dict/words".

#+username

This is used to perform checks on the name/password combination. Currently this checks whether the password matches or is included in the password. It may eventually be extended to further tests.

Definition at line 108 of file pwpolicy.c.

Function Documentation

◆ gvm_disable_password_policy()

void gvm_disable_password_policy ( void  )

Disable all password policy checking.

Definition at line 411 of file pwpolicy.c.

412 {
414  g_warning ("Password policy checking has been disabled.");
415 }

References disable_password_policy.

◆ gvm_validate_password()

char* gvm_validate_password ( const char *  password,
const char *  username 
)

Validate a password against the pattern file.

Parameters
[in]passwordThe password to check
[in]usernameThe user name or NULL. This is used to check the passphrase against the user name.
Returns
NULL on success or a malloced string with an error description.

Definition at line 355 of file pwpolicy.c.

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 }

References DIM, disable_password_policy, parse_pattern_line(), policy_checking_failed(), and PWPOLICY_FILE_NAME.

Here is the call graph for this function:

◆ is_keyword()

static char* is_keyword ( char *  string,
const char *  keyword 
)
static

Check whether a string starts with a keyword.

Note that the keyword may optionally be terminated by a colon.

Parameters
stringThe string to check
keywordThe keyword
Returns
NULL if the keyword is not found. If found a pointer into string to the value of the keyword with removed leading spaces is returned.

Definition at line 138 of file pwpolicy.c.

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 }

Referenced by parse_pattern_line().

Here is the caller graph for this function:

◆ parse_pattern_line()

static char* parse_pattern_line ( char *  line,
const char *  fname,
int  lineno,
char **  descp,
const char *  password,
const char *  username 
)
static

Parse one line of a pettern file.

Parameters
lineA null terminated buffer with the content of the line. The line terminator has already been stripped. It may be modified after return.
fnameThe name of the pattern file for error reporting
linenoThe current line number for error reporting
descpPointer to a variable holding the current description string or NULL for no description.
passwordThe password to check.
usernameThe username to check.
Returns
NULL on success or a malloced string with an error description.

Definition at line 232 of file pwpolicy.c.

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 }

References is_keyword(), policy_checking_failed(), and search_file().

Referenced by gvm_validate_password().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ policy_checking_failed()

static char* policy_checking_failed ( void  )
static
Returns
A malloced string to be returned on read and configuration errors.

Definition at line 120 of file pwpolicy.c.

121 {
122  return g_strdup ("Password policy checking failed (internal error)");
123 }

Referenced by gvm_validate_password(), and parse_pattern_line().

Here is the caller graph for this function:

◆ search_file()

static int search_file ( const char *  fname,
const char *  password 
)
static

Search a file for a matching line.

This is a case insensitive search for a password in a file. The file is assumed to be a simple LF delimited list of words.

Parameters
fnameName of the file to search.
passwordPassword to search for.
Returns
-1 if the file could not be opened or a read error occurred, 0 if password was not found and 1 if password was found.

Definition at line 170 of file pwpolicy.c.

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 }

References DIM.

Referenced by parse_pattern_line().

Here is the caller graph for this function:

Variable Documentation

◆ disable_password_policy

gboolean disable_password_policy
static

Flag indicating that passwords are not checked.

Definition at line 113 of file pwpolicy.c.

Referenced by gvm_disable_password_policy(), and gvm_validate_password().

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:232
search_file
static int search_file(const char *fname, const char *password)
Search a file for a matching line.
Definition: pwpolicy.c:170
PWPOLICY_FILE_NAME
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition: pwpolicy.c:108
DIM
#define DIM(v)
Definition: pwpolicy.c:37
policy_checking_failed
static char * policy_checking_failed(void)
Definition: pwpolicy.c:120
disable_password_policy
static gboolean disable_password_policy
Flag indicating that passwords are not checked.
Definition: pwpolicy.c:113
is_keyword
static char * is_keyword(char *string, const char *keyword)
Check whether a string starts with a keyword.
Definition: pwpolicy.c:138