Greenbone Vulnerability Management Libraries 22.8.0
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
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
100
105static char *
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 if ((p = is_keyword (line, "desc")))
245 {
246 g_free (*descp);
247 if (*p)
248 *descp = g_strdup (p);
249 else
250 *descp = NULL;
251 }
252 else if ((is_keyword (line, "nodesc")))
253 {
254 g_free (*descp);
255 *descp = NULL;
256 }
257 else if ((p = is_keyword (line, "search")))
258 {
259 int sret;
260
261 sret = search_file (p, password);
262 if (sret == -1)
263 {
264 g_warning ("error searching '%s' (requested at line %d): %s", p,
265 lineno, g_strerror (errno));
266 ret = policy_checking_failed ();
267 }
268 else if (sret && *descp)
269 ret = g_strdup_printf ("Weak password (%s)", *descp);
270 else if (sret)
271 ret = g_strdup_printf ("Weak password (found in '%s')", p);
272 else
273 ret = NULL;
274 }
275 else if (is_keyword (line, "username"))
276 {
277 /* Fixme: The include check is case sensitive and the strcmp
278 does only work with ascii. Changing this required a bit
279 more more (g_utf8_casefold) and also requires checking
280 for valid utf8 sequences in the password and all pattern. */
281 if (!username)
282 ret = NULL;
283 else if (!g_ascii_strcasecmp (password, username))
284 ret = g_strdup_printf ("Weak password (%s)",
285 "user name matches password");
286 else if (strstr (password, username))
287 ret = g_strdup_printf ("Weak password (%s)",
288 "user name is part of the password");
289 else if (strstr (username, password))
290 ret = g_strdup_printf ("Weak password (%s)",
291 "password is part of the user name");
292 else
293 ret = NULL;
294 }
295 else
296 {
297 g_warning ("error reading '%s', line %d: %s", fname, lineno,
298 "unknown processing instruction");
299 ret = policy_checking_failed ();
300 }
301 }
302 else if (*line == '#') /* Comment */
303 {
304 ret = NULL;
305 }
306 else if (*line == '/'
307 || (*line == '!' && line[1] == '/')) /* Regular expression. */
308 {
309 int rev = (*line == '!');
310 if (rev)
311 line++;
312 line++;
313 n = strlen (line);
314 if (n && line[n - 1] == '/')
315 line[n - 1] = 0;
316 if (((!g_regex_match_simple (line, password, G_REGEX_CASELESS, 0)) ^ rev))
317 ret = NULL;
318 else if (*descp)
319 ret = g_strdup_printf ("Weak password (%s)", *descp);
320 else
321 ret =
322 g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
323 }
324 else /* Simple string. */
325 {
326 if (g_ascii_strcasecmp (line, password))
327 ret = NULL;
328 else if (*descp)
329 ret = g_strdup_printf ("Weak password (%s)", *descp);
330 else
331 ret =
332 g_strdup_printf ("Weak password (see '%s' line %d)", fname, lineno);
333 }
334
335 return ret;
336}
337
348char *
349gvm_validate_password (const char *password, const char *username)
350{
351 const char *patternfile = PWPOLICY_FILE_NAME;
352 char *ret;
353 FILE *fp;
354 int lineno;
355 char line[256];
356 char *desc = NULL;
357
359 return NULL;
360
361 if (!password || !*password)
362 return g_strdup ("Empty password");
363
364 fp = fopen (patternfile, "r");
365 if (!fp)
366 {
367 g_warning ("error opening '%s': %s", patternfile, g_strerror (errno));
368 return policy_checking_failed ();
369 }
370 lineno = 0;
371 ret = NULL;
372 while (fgets (line, DIM (line) - 1, fp))
373 {
374 size_t len;
375
376 lineno++;
377 len = strlen (line);
378 if (!len || line[len - 1] != '\n')
379 {
380 g_warning ("error reading '%s', line %d: %s", patternfile, lineno,
381 len ? "line too long" : "line without a LF");
382 ret = policy_checking_failed ();
383 break;
384 }
385 line[--len] = 0; /* Chop the LF. */
386 if (len && line[len - 1] == '\r')
387 line[--len] = 0; /* Chop an optional CR. */
388 ret = parse_pattern_line (line, patternfile, lineno, &desc, password,
389 username);
390 if (ret)
391 break;
392
393 bzero (line, sizeof (line));
394 }
395
396 fclose (fp);
397 g_free (desc);
398 return ret;
399}
400
404void
406{
408 g_warning ("Password policy checking has been disabled.");
409}
static int search_file(const char *fname, const char *password)
Search a file for a matching line.
Definition: pwpolicy.c:164
void gvm_disable_password_policy(void)
Disable all password policy checking.
Definition: pwpolicy.c:405
#define DIM(v)
Definition: pwpolicy.c:23
static char * policy_checking_failed(void)
Definition: pwpolicy.c:106
static gboolean disable_password_policy
Flag indicating that passwords are not checked.
Definition: pwpolicy.c:99
#define PWPOLICY_FILE_NAME
The name of the pattern file.
Definition: pwpolicy.c:94
char * gvm_validate_password(const char *password, const char *username)
Validate a password against the pattern file.
Definition: pwpolicy.c:349
static char * is_keyword(char *string, const char *keyword)
Check whether a string starts with a keyword.
Definition: pwpolicy.c:124
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
Protos and data structures for pwpolicy checking.