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 <http://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>
44
#define JNLIB_NEED_LOG_LOGV
50
enum cmd_and_opt_values
67
/* The list of commands and options. */
68
static ARGPARSE_OPTS opts[] = {
70
{ 301, NULL, 0, N_("@Options:\n ") },
72
{ oVerbose, "verbose", 0, "verbose" },
74
{ oHomedir, "homedir", 2, "@" },
75
{ oCheck, "check", 0, "run only a syntax check on the patternfile" },
76
{ oNull, "null", 0, "input is expected to be null delimited" },
82
/* Global options are accessed through the usual OPT structure. */
93
PAT_NULL, /* Indicates end of the array. */
94
PAT_STRING, /* The pattern is a simple string. */
95
PAT_REGEX /* The pattern is an extended regualr expression. */
99
/* An object to decibe an item of our pattern table. */
103
unsigned int lineno; /* Line number of the pattern file. */
106
const char *string; /* Pointer to the actual string (nul termnated). */
107
size_t length; /* The length of this string (strlen). */
110
/* We allocate the regex_t because this type is larger than what
111
we need for PAT_STRING and we expect only a few regex in a
112
patternfile. It would be a waste of core to have so many
113
unused stuff in the table. */
118
typedef struct pattern_s pattern_t;
122
/*** Local prototypes ***/
123
static char *read_file (const char *fname, size_t *r_length);
124
static pattern_t *parse_pattern_file (char *data, size_t datalen);
125
static void process (FILE *fp, pattern_t *patarray);
130
/* Info function for usage(). */
132
my_strusage (int level)
137
case 11: p = "gpg-check-pattern (GnuPG)";
139
case 13: p = VERSION; break;
140
case 17: p = PRINTABLE_OS_NAME; break;
141
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
145
p = _("Usage: gpg-check-pattern [options] patternfile (-h for help)\n");
148
p = _("Syntax: gpg-check-pattern [options] patternfile\n"
149
"Check a passphrase given on stdin against the patternfile\n");
159
main (int argc, char **argv )
163
size_t raw_pattern_length;
164
pattern_t *patternarray;
166
set_strusage (my_strusage);
167
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
168
log_set_prefix ("gpg-check-pattern", 1);
170
/* Make sure that our subsystems are ready. */
171
init_common_subsystems ();
175
/* We need Libgcrypt for hashing. */
176
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
178
log_fatal ( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
179
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
182
setup_libgcrypt_logging ();
183
gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);
185
opt.homedir = default_homedir ();
189
pargs.flags= 1; /* (do not remove the args) */
190
while (arg_parse (&pargs, opts) )
194
case oVerbose: opt.verbose++; break;
195
case oHomedir: opt.homedir = pargs.r.ret_str; break;
196
case oCheck: opt.checkonly = 1; break;
197
case oNull: opt.null = 1; break;
199
default : pargs.err = 2; break;
202
if (log_get_errorcount(0))
208
/* We read the entire pattern file into our memory and parse it
209
using a separate function. This allows us to eventual do the
210
reading while running setuid so that the pattern file can be
211
hidden from regular users. I am not sure whether this makes
212
sense, but lets be prepared for it. */
213
raw_pattern = read_file (*argv, &raw_pattern_length);
217
patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
223
#ifdef HAVE_DOSISH_SYSTEM
224
setmode (fileno (stdin) , O_BINARY );
226
process (stdin, patternarray);
228
return log_get_errorcount(0)? 1 : 0;
233
/* Read a file FNAME into a buffer and return that malloced buffer.
234
Caller must free the buffer. On error NULL is returned, on success
235
the valid length of the buffer is stored at R_LENGTH. The returned
236
buffer is guarnteed to be nul terminated. */
238
read_file (const char *fname, size_t *r_length)
244
if (!strcmp (fname, "-"))
246
size_t nread, bufsize = 0;
249
#ifdef HAVE_DOSISH_SYSTEM
250
setmode ( fileno(fp) , O_BINARY );
259
buf = xmalloc (bufsize+1);
261
buf = xrealloc (buf, bufsize+1);
263
nread = fread (buf+buflen, 1, NCHUNK, fp);
264
if (nread < NCHUNK && ferror (fp))
266
log_error ("error reading `[stdin]': %s\n", strerror (errno));
272
while (nread == NCHUNK);
280
fp = fopen (fname, "rb");
283
log_error ("can't open `%s': %s\n", fname, strerror (errno));
287
if (fstat (fileno(fp), &st))
289
log_error ("can't stat `%s': %s\n", fname, strerror (errno));
295
buf = xmalloc (buflen+1);
296
if (fread (buf, buflen, 1, fp) != 1)
298
log_error ("error reading `%s': %s\n", fname, strerror (errno));
313
get_regerror (int errcode, regex_t *compiled)
315
size_t length = regerror (errcode, compiled, NULL, 0);
316
char *buffer = xmalloc (length);
317
regerror (errcode, compiled, buffer, length);
321
/* Parse the pattern given in the memory aread DATA/DATALEN and return
322
a new pattern array. The end of the array is indicated by a NULL
323
entry. On error an error message is printed and the fucntion
324
returns NULL. Note that the function modifies DATA and assumes
325
that data is nul terminated (even if this is one byte past
328
parse_pattern_file (char *data, size_t datalen)
333
size_t arraysize, arrayidx;
334
unsigned int lineno = 0;
336
/* Estimate the number of entries by counting the non-comment lines. */
339
for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
342
arraysize += 2; /* For the terminating NULL and a last line w/o a LF. */
344
array = xcalloc (arraysize, sizeof *array);
347
/* Loop over all lines. */
348
while (datalen && data)
352
p2 = data = memchr (p, '\n', datalen);
362
while (isascii (*p) && isspace (*p))
366
while (p2 > p && isascii (*p2) && isspace (*p2))
370
assert (arrayidx < arraysize);
371
array[arrayidx].lineno = lineno;
377
array[arrayidx].type = PAT_REGEX;
378
if (*p && p[strlen(p)-1] == '/')
379
p[strlen(p)-1] = 0; /* Remove optional delimiter. */
380
array[arrayidx].u.r.regex = xcalloc (1, sizeof (regex_t));
381
rerr = regcomp (array[arrayidx].u.r.regex, p,
382
REG_ICASE|REG_NOSUB|REG_EXTENDED);
385
char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
386
log_error ("invalid r.e. at line %u: %s\n", lineno, rerrbuf);
394
array[arrayidx].type = PAT_STRING;
395
array[arrayidx].u.s.string = p;
396
array[arrayidx].u.s.length = strlen (p);
400
assert (arrayidx < arraysize);
401
array[arrayidx].type = PAT_NULL;
407
/* Check whether string macthes any of the pattern in PATARRAY and
408
returns the matching pattern item or NULL. */
410
match_p (const char *string, pattern_t *patarray)
417
log_info ("zero length input line - ignored\n");
421
for (pat = patarray; pat->type != PAT_NULL; pat++)
423
if (pat->type == PAT_STRING)
425
if (!strcasecmp (pat->u.s.string, string))
428
else if (pat->type == PAT_REGEX)
432
rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
435
else if (rerr != REG_NOMATCH)
437
char *rerrbuf = get_regerror (rerr, pat->u.r.regex);
438
log_error ("matching r.e. failed: %s\n", rerrbuf);
440
return pat; /* Better indicate a match on error. */
450
/* Actual processing of the input. This fucntion does not return an
451
error code but exits as soon as a match has been found. */
453
process (FILE *fp, pattern_t *patarray)
458
unsigned long lineno = 0;
463
while (idx < sizeof buffer -1 && c != EOF )
465
if ((c = getc (fp)) != EOF)
467
if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
472
while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
476
pat = match_p (buffer, patarray);
480
log_error ("input line %lu matches pattern at line %u"
482
lineno, pat->lineno);
492
log_error ("input line %lu too long - rejected\n", lineno+1);
497
log_error ("input read error at line %lu: %s - rejected\n",
498
lineno+1, strerror (errno));
502
log_info ("no input line matches the pattern - accepted\n");