Greenbone Vulnerability Management Libraries 22.32.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:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define DIM(v)
#define DIMof(type, member)
#define G_LOG_DOMAIN   "libgvm base"
 GLib log domain.
#define PWPOLICY_FILE_NAME   GVM_SYSCONF_DIR "/pwpolicy.conf"
 The name of the pattern file.

Functions

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

Variables

static gboolean disable_password_policy
 Flag indicating that passwords are not checked.

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)
Value:
(sizeof (v) / sizeof ((v)[0]))

Definition at line 23 of file pwpolicy.c.

Referenced by gvm_validate_password(), and search_file().

◆ DIMof

#define DIMof ( type,
member )
Value:
DIM (((type *) 0)->member)
#define DIM(v)
Definition pwpolicy.c:23

Definition at line 24 of file pwpolicy.c.

◆ G_LOG_DOMAIN

#define G_LOG_DOMAIN   "libgvm base"

GLib log domain.

Definition at line 31 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 94 of file pwpolicy.c.

Referenced by gvm_validate_password().

Function Documentation

◆ gvm_disable_password_policy()

void gvm_disable_password_policy ( void )

Disable all password policy checking.

Definition at line 406 of file pwpolicy.c.

407{
409 g_warning ("Password policy checking has been disabled.");
410}
static gboolean disable_password_policy
Flag indicating that passwords are not checked.
Definition pwpolicy.c:99

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 350 of file pwpolicy.c.

351{
352 const char *patternfile = PWPOLICY_FILE_NAME;
353 char *ret;
354 FILE *fp;
355 int lineno;
356 char line[256];
357 char *desc = NULL;
358
360 return NULL;
361
362 if (!password || !*password)
363 return g_strdup ("Empty password");
364
365 fp = fopen (patternfile, "r");
366 if (!fp)
367 {
368 g_warning ("error opening '%s': %s", patternfile, g_strerror (errno));
369 return policy_checking_failed ();
370 }
371 lineno = 0;
372 ret = NULL;
373 while (fgets (line, DIM (line) - 1, fp))
374 {
375 size_t len;
376
377 lineno++;
378 len = strlen (line);
379 if (!len || line[len - 1] != '\n')
380 {
381 g_warning ("error reading '%s', line %d: %s", patternfile, lineno,
382 len ? "line too long" : "line without a LF");
383 ret = policy_checking_failed ();
384 break;
385 }
386 line[--len] = 0; /* Chop the LF. */
387 if (len && line[len - 1] == '\r')
388 line[--len] = 0; /* Chop an optional CR. */
389 ret = parse_pattern_line (line, patternfile, lineno, &desc, password,
390 username);
391 if (ret)
392 break;
393
394 bzero (line, sizeof (line));
395 }
396
397 fclose (fp);
398 g_free (desc);
399 return ret;
400}
static char * policy_checking_failed(void)
Definition pwpolicy.c:106
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition pwpolicy.c:94
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

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()

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 124 of file pwpolicy.c.

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}

Referenced by parse_pattern_line().

Here is the caller graph for this function:

◆ parse_pattern_line()

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 226 of file pwpolicy.c.

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 p = is_keyword (line, "desc");
245 if (p)
246 {
247 g_free (*descp);
248 if (*p)
249 *descp = g_strdup (p);
250 else
251 *descp = NULL;
252 }
253 else if ((is_keyword (line, "nodesc")))
254 {
255 g_free (*descp);
256 *descp = NULL;
257 }
258 else if ((p = is_keyword (line, "search")))
259 {
260 int sret;
261
262 sret = search_file (p, password);
263 if (sret == -1)
264 {
265 g_warning ("error searching '%s' (requested at line %d): %s", p,
266 lineno, g_strerror (errno));
267 ret = policy_checking_failed ();
268 }
269 else if (sret && *descp)
270 ret = g_strdup_printf ("Weak password (%s)", *descp);
271 else if (sret)
272 ret = g_strdup_printf ("Weak password (found in '%s')", p);
273 else
274 ret = NULL;
275 }
276 else if (is_keyword (line, "username"))
277 {
278 /* Fixme: The include check is case sensitive and the strcmp
279 does only work with ascii. Changing this required a bit
280 more more (g_utf8_casefold) and also requires checking
281 for valid utf8 sequences in the password and all pattern. */
282 if (!username)
283 ret = NULL;
284 else if (!g_ascii_strcasecmp (password, username))
285 ret = g_strdup_printf ("Weak password (%s)",
286 "user name matches password");
287 else if (strstr (password, username))
288 ret = g_strdup_printf ("Weak password (%s)",
289 "user name is part of the password");
290 else if (strstr (username, password))
291 ret = g_strdup_printf ("Weak password (%s)",
292 "password is part of the user name");
293 else
294 ret = NULL;
295 }
296 else
297 {
298 g_warning ("error reading '%s', line %d: %s", fname, lineno,
299 "unknown processing instruction");
300 ret = policy_checking_failed ();
301 }
302 }
303 else if (*line == '#') /* Comment */
304 {
305 ret = NULL;
306 }
307 else if (*line == '/'
308 || (*line == '!' && line[1] == '/')) /* Regular expression. */
309 {
310 int rev = (*line == '!');
311 if (rev)
312 line++;
313 line++;
314 n = strlen (line);
315 if (n && line[n - 1] == '/')
316 line[n - 1] = 0;
317 if (((!g_regex_match_simple (line, password, G_REGEX_CASELESS, 0)) ^ rev))
318 ret = NULL;
319 else if (*descp)
320 ret = g_strdup_printf ("Weak password (%s)", *descp);
321 else
322 ret =
323 g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
324 }
325 else /* Simple string. */
326 {
327 if (g_ascii_strcasecmp (line, password))
328 ret = NULL;
329 else if (*descp)
330 ret = g_strdup_printf ("Weak password (%s)", *descp);
331 else
332 ret =
333 g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
334 }
335
336 return ret;
337}
static int search_file(const char *fname, const char *password)
Search a file for a matching line.
Definition pwpolicy.c:164
static char * is_keyword(char *string, const char *keyword)
Check whether a string starts with a keyword.
Definition pwpolicy.c:124

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

Referenced by Ensure(), Ensure(), Ensure(), and gvm_validate_password().

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

◆ policy_checking_failed()

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

Definition at line 106 of file pwpolicy.c.

107{
108 return g_strdup ("Password policy checking failed (internal error)");
109}

Referenced by gvm_validate_password(), and parse_pattern_line().

Here is the caller graph for this function:

◆ search_file()

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 164 of file pwpolicy.c.

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}

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 99 of file pwpolicy.c.

Referenced by gvm_disable_password_policy(), and gvm_validate_password().