1 /* gpg-check-pattern.c - A tool to check passphrases against pattern.
2 * Copyright (C) 2007 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
32 #ifdef HAVE_LANGINFO_CODESET
33 # include <langinfo.h>
35 #ifdef HAVE_DOSISH_SYSTEM
36 # include <fcntl.h> /* for setmode() */
39 #include <sys/types.h>
46 #include "../common/init.h"
49 enum cmd_and_opt_values
66 /* The list of commands and options. */
67 static ARGPARSE_OPTS opts[] = {
69 { 301, NULL, 0, N_("@Options:\n ") },
71 { oVerbose, "verbose", 0, "verbose" },
73 { oHomedir, "homedir", 2, "@" },
74 { oCheck, "check", 0, "run only a syntax check on the patternfile" },
75 { oNull, "null", 0, "input is expected to be null delimited" },
81 /* Global options are accessed through the usual OPT structure. */
92 PAT_NULL, /* Indicates end of the array. */
93 PAT_STRING, /* The pattern is a simple string. */
94 PAT_REGEX /* The pattern is an extended regualr expression. */
98 /* An object to decibe an item of our pattern table. */
102 unsigned int lineno; /* Line number of the pattern file. */
105 const char *string; /* Pointer to the actual string (nul termnated). */
106 size_t length; /* The length of this string (strlen). */
109 /* We allocate the regex_t because this type is larger than what
110 we need for PAT_STRING and we expect only a few regex in a
111 patternfile. It would be a waste of core to have so many
112 unused stuff in the table. */
117 typedef struct pattern_s pattern_t;
121 /*** Local prototypes ***/
122 static char *read_file (const char *fname, size_t *r_length);
123 static pattern_t *parse_pattern_file (char *data, size_t datalen);
124 static void process (FILE *fp, pattern_t *patarray);
129 /* Info function for usage(). */
131 my_strusage (int level)
136 case 11: p = "gpg-check-pattern (@GnuPG@)";
138 case 13: p = VERSION; break;
139 case 17: p = PRINTABLE_OS_NAME; break;
140 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
144 p = _("Usage: gpg-check-pattern [options] patternfile (-h for help)\n");
147 p = _("Syntax: gpg-check-pattern [options] patternfile\n"
148 "Check a passphrase given on stdin against the patternfile\n");
158 main (int argc, char **argv )
162 size_t raw_pattern_length;
163 pattern_t *patternarray;
165 early_system_init ();
166 set_strusage (my_strusage);
167 gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
168 log_set_prefix ("gpg-check-pattern", GPGRT_LOG_WITH_PREFIX);
170 /* Make sure that our subsystems are ready. */
172 init_common_subsystems (&argc, &argv);
174 setup_libgcrypt_logging ();
175 gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);
179 pargs.flags= 1; /* (do not remove the args) */
180 while (arg_parse (&pargs, opts) )
184 case oVerbose: opt.verbose++; break;
185 case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
186 case oCheck: opt.checkonly = 1; break;
187 case oNull: opt.null = 1; break;
189 default : pargs.err = 2; break;
192 if (log_get_errorcount(0))
198 /* We read the entire pattern file into our memory and parse it
199 using a separate function. This allows us to eventual do the
200 reading while running setuid so that the pattern file can be
201 hidden from regular users. I am not sure whether this makes
202 sense, but lets be prepared for it. */
203 raw_pattern = read_file (*argv, &raw_pattern_length);
207 patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
213 #ifdef HAVE_DOSISH_SYSTEM
214 setmode (fileno (stdin) , O_BINARY );
216 process (stdin, patternarray);
218 return log_get_errorcount(0)? 1 : 0;
223 /* Read a file FNAME into a buffer and return that malloced buffer.
224 Caller must free the buffer. On error NULL is returned, on success
225 the valid length of the buffer is stored at R_LENGTH. The returned
226 buffer is guarnteed to be nul terminated. */
228 read_file (const char *fname, size_t *r_length)
234 if (!strcmp (fname, "-"))
236 size_t nread, bufsize = 0;
239 #ifdef HAVE_DOSISH_SYSTEM
240 setmode ( fileno(fp) , O_BINARY );
249 buf = xmalloc (bufsize+1);
251 buf = xrealloc (buf, bufsize+1);
253 nread = fread (buf+buflen, 1, NCHUNK, fp);
254 if (nread < NCHUNK && ferror (fp))
256 log_error ("error reading '[stdin]': %s\n", strerror (errno));
262 while (nread == NCHUNK);
270 fp = fopen (fname, "rb");
273 log_error ("can't open '%s': %s\n", fname, strerror (errno));
277 if (fstat (fileno(fp), &st))
279 log_error ("can't stat '%s': %s\n", fname, strerror (errno));
285 buf = xmalloc (buflen+1);
286 if (fread (buf, buflen, 1, fp) != 1)
288 log_error ("error reading '%s': %s\n", fname, strerror (errno));
303 get_regerror (int errcode, regex_t *compiled)
305 size_t length = regerror (errcode, compiled, NULL, 0);
306 char *buffer = xmalloc (length);
307 regerror (errcode, compiled, buffer, length);
311 /* Parse the pattern given in the memory aread DATA/DATALEN and return
312 a new pattern array. The end of the array is indicated by a NULL
313 entry. On error an error message is printed and the function
314 returns NULL. Note that the function modifies DATA and assumes
315 that data is nul terminated (even if this is one byte past
318 parse_pattern_file (char *data, size_t datalen)
323 size_t arraysize, arrayidx;
324 unsigned int lineno = 0;
326 /* Estimate the number of entries by counting the non-comment lines. */
329 for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
332 arraysize += 2; /* For the terminating NULL and a last line w/o a LF. */
334 array = xcalloc (arraysize, sizeof *array);
337 /* Loop over all lines. */
338 while (datalen && data)
342 p2 = data = memchr (p, '\n', datalen);
352 while (isascii (*p) && isspace (*p))
356 while (p2 > p && isascii (*p2) && isspace (*p2))
360 assert (arrayidx < arraysize);
361 array[arrayidx].lineno = lineno;
367 array[arrayidx].type = PAT_REGEX;
368 if (*p && p[strlen(p)-1] == '/')
369 p[strlen(p)-1] = 0; /* Remove optional delimiter. */
370 array[arrayidx].u.r.regex = xcalloc (1, sizeof (regex_t));
371 rerr = regcomp (array[arrayidx].u.r.regex, p,
372 REG_ICASE|REG_NOSUB|REG_EXTENDED);
375 char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
376 log_error ("invalid r.e. at line %u: %s\n", lineno, rerrbuf);
384 array[arrayidx].type = PAT_STRING;
385 array[arrayidx].u.s.string = p;
386 array[arrayidx].u.s.length = strlen (p);
390 assert (arrayidx < arraysize);
391 array[arrayidx].type = PAT_NULL;
397 /* Check whether string macthes any of the pattern in PATARRAY and
398 returns the matching pattern item or NULL. */
400 match_p (const char *string, pattern_t *patarray)
407 log_info ("zero length input line - ignored\n");
411 for (pat = patarray; pat->type != PAT_NULL; pat++)
413 if (pat->type == PAT_STRING)
415 if (!strcasecmp (pat->u.s.string, string))
418 else if (pat->type == PAT_REGEX)
422 rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
425 else if (rerr != REG_NOMATCH)
427 char *rerrbuf = get_regerror (rerr, pat->u.r.regex);
428 log_error ("matching r.e. failed: %s\n", rerrbuf);
430 return pat; /* Better indicate a match on error. */
440 /* Actual processing of the input. This function does not return an
441 error code but exits as soon as a match has been found. */
443 process (FILE *fp, pattern_t *patarray)
448 unsigned long lineno = 0;
453 while (idx < sizeof buffer -1 && c != EOF )
455 if ((c = getc (fp)) != EOF)
457 if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
462 while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
466 pat = match_p (buffer, patarray);
470 log_error ("input line %lu matches pattern at line %u"
472 lineno, pat->lineno);
482 log_error ("input line %lu too long - rejected\n", lineno+1);
487 log_error ("input read error at line %lu: %s - rejected\n",
488 lineno+1, strerror (errno));
492 log_info ("no input line matches the pattern - accepted\n");