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 <@EMAIL@>.\n"); break;
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. */
172 init_common_subsystems ();
174 /* We need Libgcrypt for hashing. */
175 if (!gcry_check_version (NEED_LIBGCRYPT_VERSION
) )
177 log_fatal ( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
178 NEED_LIBGCRYPT_VERSION
, gcry_check_version (NULL
) );
181 setup_libgcrypt_logging ();
182 gcry_control (GCRYCTL_INIT_SECMEM
, 4096, 0);
184 opt
.homedir
= default_homedir ();
188 pargs
.flags
= 1; /* (do not remove the args) */
189 while (arg_parse (&pargs
, opts
) )
193 case oVerbose
: opt
.verbose
++; break;
194 case oHomedir
: opt
.homedir
= pargs
.r
.ret_str
; break;
195 case oCheck
: opt
.checkonly
= 1; break;
196 case oNull
: opt
.null
= 1; break;
198 default : pargs
.err
= 2; break;
201 if (log_get_errorcount(0))
207 /* We read the entire pattern file into our memory and parse it
208 using a separate function. This allows us to eventual do the
209 reading while running setuid so that the pattern file can be
210 hidden from regular users. I am not sure whether this makes
211 sense, but lets be prepared for it. */
212 raw_pattern
= read_file (*argv
, &raw_pattern_length
);
216 patternarray
= parse_pattern_file (raw_pattern
, raw_pattern_length
);
222 #ifdef HAVE_DOSISH_SYSTEM
223 setmode (fileno (stdin
) , O_BINARY
);
225 process (stdin
, patternarray
);
227 return log_get_errorcount(0)? 1 : 0;
232 /* Read a file FNAME into a buffer and return that malloced buffer.
233 Caller must free the buffer. On error NULL is returned, on success
234 the valid length of the buffer is stored at R_LENGTH. The returned
235 buffer is guarnteed to be nul terminated. */
237 read_file (const char *fname
, size_t *r_length
)
243 if (!strcmp (fname
, "-"))
245 size_t nread
, bufsize
= 0;
248 #ifdef HAVE_DOSISH_SYSTEM
249 setmode ( fileno(fp
) , O_BINARY
);
258 buf
= xmalloc (bufsize
+1);
260 buf
= xrealloc (buf
, bufsize
+1);
262 nread
= fread (buf
+buflen
, 1, NCHUNK
, fp
);
263 if (nread
< NCHUNK
&& ferror (fp
))
265 log_error ("error reading `[stdin]': %s\n", strerror (errno
));
271 while (nread
== NCHUNK
);
279 fp
= fopen (fname
, "rb");
282 log_error ("can't open `%s': %s\n", fname
, strerror (errno
));
286 if (fstat (fileno(fp
), &st
))
288 log_error ("can't stat `%s': %s\n", fname
, strerror (errno
));
294 buf
= xmalloc (buflen
+1);
295 if (fread (buf
, buflen
, 1, fp
) != 1)
297 log_error ("error reading `%s': %s\n", fname
, strerror (errno
));
312 get_regerror (int errcode
, regex_t
*compiled
)
314 size_t length
= regerror (errcode
, compiled
, NULL
, 0);
315 char *buffer
= xmalloc (length
);
316 regerror (errcode
, compiled
, buffer
, length
);
320 /* Parse the pattern given in the memory aread DATA/DATALEN and return
321 a new pattern array. The end of the array is indicated by a NULL
322 entry. On error an error message is printed and the fucntion
323 returns NULL. Note that the function modifies DATA and assumes
324 that data is nul terminated (even if this is one byte past
327 parse_pattern_file (char *data
, size_t datalen
)
332 size_t arraysize
, arrayidx
;
333 unsigned int lineno
= 0;
335 /* Estimate the number of entries by counting the non-comment lines. */
338 for (n
= datalen
; n
&& (p2
= memchr (p
, '\n', n
)); p2
++, n
-= p2
- p
, p
= p2
)
341 arraysize
+= 2; /* For the terminating NULL and a last line w/o a LF. */
343 array
= xcalloc (arraysize
, sizeof *array
);
346 /* Loop over all lines. */
347 while (datalen
&& data
)
351 p2
= data
= memchr (p
, '\n', datalen
);
361 while (isascii (*p
) && isspace (*p
))
365 while (p2
> p
&& isascii (*p2
) && isspace (*p2
))
369 assert (arrayidx
< arraysize
);
370 array
[arrayidx
].lineno
= lineno
;
376 array
[arrayidx
].type
= PAT_REGEX
;
377 if (*p
&& p
[strlen(p
)-1] == '/')
378 p
[strlen(p
)-1] = 0; /* Remove optional delimiter. */
379 array
[arrayidx
].u
.r
.regex
= xcalloc (1, sizeof (regex_t
));
380 rerr
= regcomp (array
[arrayidx
].u
.r
.regex
, p
,
381 REG_ICASE
|REG_NOSUB
|REG_EXTENDED
);
384 char *rerrbuf
= get_regerror (rerr
, array
[arrayidx
].u
.r
.regex
);
385 log_error ("invalid r.e. at line %u: %s\n", lineno
, rerrbuf
);
393 array
[arrayidx
].type
= PAT_STRING
;
394 array
[arrayidx
].u
.s
.string
= p
;
395 array
[arrayidx
].u
.s
.length
= strlen (p
);
399 assert (arrayidx
< arraysize
);
400 array
[arrayidx
].type
= PAT_NULL
;
406 /* Check whether string macthes any of the pattern in PATARRAY and
407 returns the matching pattern item or NULL. */
409 match_p (const char *string
, pattern_t
*patarray
)
416 log_info ("zero length input line - ignored\n");
420 for (pat
= patarray
; pat
->type
!= PAT_NULL
; pat
++)
422 if (pat
->type
== PAT_STRING
)
424 if (!strcasecmp (pat
->u
.s
.string
, string
))
427 else if (pat
->type
== PAT_REGEX
)
431 rerr
= regexec (pat
->u
.r
.regex
, string
, 0, NULL
, 0);
434 else if (rerr
!= REG_NOMATCH
)
436 char *rerrbuf
= get_regerror (rerr
, pat
->u
.r
.regex
);
437 log_error ("matching r.e. failed: %s\n", rerrbuf
);
439 return pat
; /* Better indicate a match on error. */
449 /* Actual processing of the input. This fucntion does not return an
450 error code but exits as soon as a match has been found. */
452 process (FILE *fp
, pattern_t
*patarray
)
457 unsigned long lineno
= 0;
462 while (idx
< sizeof buffer
-1 && c
!= EOF
)
464 if ((c
= getc (fp
)) != EOF
)
466 if ((c
== '\n' && !opt
.null
) || (!c
&& opt
.null
) || c
== EOF
)
471 while (idx
&& isascii (buffer
[idx
-1]) && isspace (buffer
[idx
-1]))
475 pat
= match_p (buffer
, patarray
);
479 log_error ("input line %lu matches pattern at line %u"
481 lineno
, pat
->lineno
);
491 log_error ("input line %lu too long - rejected\n", lineno
+1);
496 log_error ("input read error at line %lu: %s - rejected\n",
497 lineno
+1, strerror (errno
));
501 log_info ("no input line matches the pattern - accepted\n");