pwpolicy.c
Go to the documentation of this file.
/* SPDX-FileCopyrightText: 2013-2023 Greenbone AG
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "pwpolicy.h"
#include <errno.h> /* for errno */
#include <glib.h> /* for g_strdup_printf, g_ascii_strcasecmp, g_free, ... */
#include <stdio.h> /* for fclose, fgets, fopen, FILE, ferror, EOF, getc */
#include <stdlib.h>
#include <string.h> /* for strstr, strlen, strncmp */
#ifndef DIM
#define DIM(v) (sizeof (v) / sizeof ((v)[0]))
#define DIMof(type, member) DIM (((type *) 0)->member)
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "libgvm base"
#define PWPOLICY_FILE_NAME GVM_SYSCONF_DIR "/pwpolicy.conf"
static gboolean disable_password_policy;
static char *
policy_checking_failed (void)
{
return g_strdup ("Password policy checking failed (internal error)");
}
static char *
is_keyword (char *string, const char *keyword)
{
int idx, slen;
char *tmp;
idx = strlen (keyword);
slen = strlen (string);
if (!strncmp (string, keyword, idx))
{
tmp = string + idx;
if (tmp - string > slen)
return NULL;
// skip optional:
if (*tmp == ':')
tmp++;
if (tmp - string > slen)
return NULL;
for (; tmp - string < slen && g_ascii_isspace (*tmp); tmp++)
{
// skip whitespace
}
return tmp;
}
return NULL;
}
static int
search_file (const char *fname, const char *password)
{
FILE *fp;
int c;
char line[256];
fp = fopen (fname, "r");
if (!fp)
return -1;
while (fgets (line, DIM (line) - 1, fp))
{
size_t len;
len = strlen (line);
if (!len || line[len - 1] != '\n')
{
/* Incomplete last line or line too long. Eat until end of
line. */
while ((c = getc (fp)) != EOF && c != '\n')
;
continue;
}
line[--len] = 0; /* Chop the LF. */
if (len && line[len - 1] == '\r')
line[--len] = 0; /* Chop an optional CR. */
if (!len)
continue; /* Empty */
if (!g_ascii_strcasecmp (line, password))
{
fclose (fp);
return 1; /* Found. */
}
}
if (ferror (fp))
{
int save_errno = errno;
fclose (fp);
errno = save_errno;
return -1; /* Read error. */
}
fclose (fp);
return 0; /* Not found. */
}
static char *
parse_pattern_line (char *line, const char *fname, int lineno, char **descp,
const char *password, const char *username)
{
char *ret = NULL;
char *p;
size_t n;
/* Skip leading spaces. */
while (g_ascii_isspace (*line))
line++;
if (!*line) /* Empty line. */
{
ret = NULL;
}
else if (*line == '#' && line[1] == '+') /* Processing instruction. */
{
line += 2;
p = is_keyword (line, "desc");
if (p)
{
g_free (*descp);
if (*p)
*descp = g_strdup (p);
else
*descp = NULL;
}
else if ((is_keyword (line, "nodesc")))
{
g_free (*descp);
*descp = NULL;
}
else if ((p = is_keyword (line, "search")))
{
int sret;
sret = search_file (p, password);
if (sret == -1)
{
g_warning ("error searching '%s' (requested at line %d): %s", p,
lineno, g_strerror (errno));
ret = policy_checking_failed ();
}
else if (sret && *descp)
ret = g_strdup_printf ("Weak password (%s)", *descp);
else if (sret)
ret = g_strdup_printf ("Weak password (found in '%s')", p);
else
ret = NULL;
}
else if (is_keyword (line, "username"))
{
/* Fixme: The include check is case sensitive and the strcmp
does only work with ascii. Changing this required a bit
more more (g_utf8_casefold) and also requires checking
for valid utf8 sequences in the password and all pattern. */
if (!username)
ret = NULL;
else if (!g_ascii_strcasecmp (password, username))
ret = g_strdup_printf ("Weak password (%s)",
"user name matches password");
else if (strstr (password, username))
ret = g_strdup_printf ("Weak password (%s)",
"user name is part of the password");
else if (strstr (username, password))
ret = g_strdup_printf ("Weak password (%s)",
"password is part of the user name");
else
ret = NULL;
}
else
{
g_warning ("error reading '%s', line %d: %s", fname, lineno,
"unknown processing instruction");
ret = policy_checking_failed ();
}
}
else if (*line == '#') /* Comment */
{
ret = NULL;
}
else if (*line == '/'
|| (*line == '!' && line[1] == '/')) /* Regular expression. */
{
int rev = (*line == '!');
if (rev)
line++;
line++;
n = strlen (line);
if (n && line[n - 1] == '/')
line[n - 1] = 0;
if (((!g_regex_match_simple (line, password, G_REGEX_CASELESS, 0)) ^ rev))
ret = NULL;
else if (*descp)
ret = g_strdup_printf ("Weak password (%s)", *descp);
else
ret =
g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
}
else /* Simple string. */
{
if (g_ascii_strcasecmp (line, password))
ret = NULL;
else if (*descp)
ret = g_strdup_printf ("Weak password (%s)", *descp);
else
ret =
g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
}
return ret;
}
char *
gvm_validate_password (const char *password, const char *username)
{
const char *patternfile = PWPOLICY_FILE_NAME;
char *ret;
FILE *fp;
int lineno;
char line[256];
char *desc = NULL;
if (disable_password_policy)
return NULL;
if (!password || !*password)
return g_strdup ("Empty password");
fp = fopen (patternfile, "r");
if (!fp)
{
g_warning ("error opening '%s': %s", patternfile, g_strerror (errno));
return policy_checking_failed ();
}
lineno = 0;
ret = NULL;
while (fgets (line, DIM (line) - 1, fp))
{
size_t len;
lineno++;
len = strlen (line);
if (!len || line[len - 1] != '\n')
{
g_warning ("error reading '%s', line %d: %s", patternfile, lineno,
len ? "line too long" : "line without a LF");
ret = policy_checking_failed ();
break;
}
line[--len] = 0; /* Chop the LF. */
if (len && line[len - 1] == '\r')
line[--len] = 0; /* Chop an optional CR. */
ret = parse_pattern_line (line, patternfile, lineno, &desc, password,
username);
if (ret)
break;
bzero (line, sizeof (line));
}
fclose (fp);
g_free (desc);
return ret;
}
void
gvm_disable_password_policy (void)
{
disable_password_policy = TRUE;
g_warning ("Password policy checking has been disabled.");
}
search_file
static int search_file(const char *fname, const char *password)
Search a file for a matching line.
Definition pwpolicy.c:164
gvm_disable_password_policy
void gvm_disable_password_policy(void)
Disable all password policy checking.
Definition pwpolicy.c:406
DIM
#define DIM(v)
Definition pwpolicy.c:23
policy_checking_failed
static char * policy_checking_failed(void)
Definition pwpolicy.c:106
disable_password_policy
static gboolean disable_password_policy
Flag indicating that passwords are not checked.
Definition pwpolicy.c:99
PWPOLICY_FILE_NAME
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition pwpolicy.c:94
gvm_validate_password
char * gvm_validate_password(const char *password, const char *username)
Validate a password against the pattern file.
Definition pwpolicy.c:350
is_keyword
static char * is_keyword(char *string, const char *keyword)
Check whether a string starts with a keyword.
Definition pwpolicy.c:124
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
pwpolicy.h
Protos and data structures for pwpolicy checking.