Greenbone Vulnerability Management Libraries: base/pwpolicy.c Source File

Greenbone Vulnerability Management Libraries 23.1.1

pwpolicy.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2013-2023 Greenbone AG
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
13
14#include "pwpolicy.h"
15
16#include <errno.h> /* for errno */
17#include <glib.h> /* for g_strdup_printf, g_ascii_strcasecmp, g_free, ... */
18#include <stdio.h> /* for fclose, fgets, fopen, FILE, ferror, EOF, getc */
19#include <stdlib.h>
20#include <string.h> /* for strstr, strlen, strncmp */
21
22#ifndef DIM
23#define DIM(v) (sizeof (v) / sizeof ((v)[0]))
24#define DIMof(type, member) DIM (((type *) 0)->member)
25#endif
26
27#undef G_LOG_DOMAIN
31#define G_LOG_DOMAIN "libgvm base"
32
94#define PWPOLICY_FILE_NAME GVM_SYSCONF_DIR "/pwpolicy.conf"
95
99 static gboolean disable_password_policy;
100
105static char *
106policy_checking_failed (void)
107{
108 return g_strdup ("Password policy checking failed (internal error)");
109}
110
123static char *
124is_keyword (char *string, const char *keyword)
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}
150
163static int
164search_file (const char *fname, const char *password)
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}
208
225static char *
226parse_pattern_line (char *line, const char *fname, int lineno, char **descp,
227 const char *password, const char *username)
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}
338
349char *
350gvm_validate_password (const char *password, const char *username)
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
359 if (disable_password_policy)
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}
401
405void
406gvm_disable_password_policy (void)
407{
408 disable_password_policy = TRUE;
409 g_warning ("Password policy checking has been disabled.");
410}
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.