Print status of CRL checks in the audit log.
[gnupg.git] / tools / gpg-check-pattern.c
blob8d152631d9f83112e1443c85de784a2fc18a88f6
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/>.
20 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29 #ifdef HAVE_LOCALE_H
30 # include <locale.h>
31 #endif
32 #ifdef HAVE_LANGINFO_CODESET
33 # include <langinfo.h>
34 #endif
35 #ifdef HAVE_DOSISH_SYSTEM
36 # include <fcntl.h> /* for setmode() */
37 #endif
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <regex.h>
41 #include <ctype.h>
44 #define JNLIB_NEED_LOG_LOGV
45 #include "util.h"
46 #include "i18n.h"
47 #include "sysutils.h"
50 enum cmd_and_opt_values
51 { aNull = 0,
52 oVerbose = 'v',
53 oArmor = 'a',
54 oPassphrase = 'P',
56 oProtect = 'p',
57 oUnprotect = 'u',
58 oNull = '0',
60 oNoVerbose = 500,
61 oCheck,
63 oHomedir
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" },
78 {0}
82 /* Global options are accessed through the usual OPT structure. */
83 static struct
85 int verbose;
86 const char *homedir;
87 int checkonly;
88 int null;
89 } opt;
92 enum {
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. */
100 struct pattern_s
102 int type;
103 unsigned int lineno; /* Line number of the pattern file. */
104 union {
105 struct {
106 const char *string; /* Pointer to the actual string (nul termnated). */
107 size_t length; /* The length of this string (strlen). */
108 } s; /*PAT_STRING*/
109 struct {
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. */
114 regex_t *regex;
115 } r; /*PAT_REGEX*/
116 } u;
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(). */
131 static const char *
132 my_strusage (int level)
134 const char *p;
135 switch (level)
137 case 11: p = "gpg-check-pattern (GnuPG)";
138 break;
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;
143 case 1:
144 case 40:
145 p = _("Usage: gpg-check-pattern [options] patternfile (-h for help)\n");
146 break;
147 case 41:
148 p = _("Syntax: gpg-check-pattern [options] patternfile\n"
149 "Check a passphrase given on stdin against the patternfile\n");
150 break;
152 default: p = NULL;
154 return p;
159 main (int argc, char **argv )
161 ARGPARSE_ARGS pargs;
162 char *raw_pattern;
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 i18n_init ();
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 ();
186 pargs.argc = &argc;
187 pargs.argv = &argv;
188 pargs.flags= 1; /* (do not remove the args) */
189 while (arg_parse (&pargs, opts) )
191 switch (pargs.r_opt)
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))
202 exit (2);
204 if (argc != 1)
205 usage (1);
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);
213 if (!raw_pattern)
214 exit (2);
216 patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
217 if (!patternarray)
218 exit (1);
219 if (opt.checkonly)
220 return 0;
222 #ifdef HAVE_DOSISH_SYSTEM
223 setmode (fileno (stdin) , O_BINARY );
224 #endif
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. */
236 static char *
237 read_file (const char *fname, size_t *r_length)
239 FILE *fp;
240 char *buf;
241 size_t buflen;
243 if (!strcmp (fname, "-"))
245 size_t nread, bufsize = 0;
247 fp = stdin;
248 #ifdef HAVE_DOSISH_SYSTEM
249 setmode ( fileno(fp) , O_BINARY );
250 #endif
251 buf = NULL;
252 buflen = 0;
253 #define NCHUNK 8192
256 bufsize += NCHUNK;
257 if (!buf)
258 buf = xmalloc (bufsize+1);
259 else
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));
266 xfree (buf);
267 return NULL;
269 buflen += nread;
271 while (nread == NCHUNK);
272 #undef NCHUNK
275 else
277 struct stat st;
279 fp = fopen (fname, "rb");
280 if (!fp)
282 log_error ("can't open `%s': %s\n", fname, strerror (errno));
283 return NULL;
286 if (fstat (fileno(fp), &st))
288 log_error ("can't stat `%s': %s\n", fname, strerror (errno));
289 fclose (fp);
290 return NULL;
293 buflen = st.st_size;
294 buf = xmalloc (buflen+1);
295 if (fread (buf, buflen, 1, fp) != 1)
297 log_error ("error reading `%s': %s\n", fname, strerror (errno));
298 fclose (fp);
299 xfree (buf);
300 return NULL;
302 fclose (fp);
304 buf[buflen] = 0;
305 *r_length = buflen;
306 return buf;
311 static char *
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);
317 return buffer;
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
325 DATALEN). */
326 static pattern_t *
327 parse_pattern_file (char *data, size_t datalen)
329 char *p, *p2;
330 size_t n;
331 pattern_t *array;
332 size_t arraysize, arrayidx;
333 unsigned int lineno = 0;
335 /* Estimate the number of entries by counting the non-comment lines. */
336 arraysize = 0;
337 p = data;
338 for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
339 if (*p != '#')
340 arraysize++;
341 arraysize += 2; /* For the terminating NULL and a last line w/o a LF. */
343 array = xcalloc (arraysize, sizeof *array);
344 arrayidx = 0;
346 /* Loop over all lines. */
347 while (datalen && data)
349 lineno++;
350 p = data;
351 p2 = data = memchr (p, '\n', datalen);
352 if (p2)
354 *data++ = 0;
355 datalen -= data - p;
357 else
358 p2 = p + datalen;
359 assert (!*p2);
360 p2--;
361 while (isascii (*p) && isspace (*p))
362 p++;
363 if (*p == '#')
364 continue;
365 while (p2 > p && isascii (*p2) && isspace (*p2))
366 *p2-- = 0;
367 if (!*p)
368 continue;
369 assert (arrayidx < arraysize);
370 array[arrayidx].lineno = lineno;
371 if (*p == '/')
373 int rerr;
375 p++;
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);
382 if (rerr)
384 char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
385 log_error ("invalid r.e. at line %u: %s\n", lineno, rerrbuf);
386 xfree (rerrbuf);
387 if (!opt.checkonly)
388 exit (1);
391 else
393 array[arrayidx].type = PAT_STRING;
394 array[arrayidx].u.s.string = p;
395 array[arrayidx].u.s.length = strlen (p);
397 arrayidx++;
399 assert (arrayidx < arraysize);
400 array[arrayidx].type = PAT_NULL;
402 return array;
406 /* Check whether string macthes any of the pattern in PATARRAY and
407 returns the matching pattern item or NULL. */
408 static pattern_t *
409 match_p (const char *string, pattern_t *patarray)
411 pattern_t *pat;
413 if (!*string)
415 if (opt.verbose)
416 log_info ("zero length input line - ignored\n");
417 return NULL;
420 for (pat = patarray; pat->type != PAT_NULL; pat++)
422 if (pat->type == PAT_STRING)
424 if (!strcasecmp (pat->u.s.string, string))
425 return pat;
427 else if (pat->type == PAT_REGEX)
429 int rerr;
431 rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
432 if (!rerr)
433 return pat;
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);
438 xfree (rerrbuf);
439 return pat; /* Better indicate a match on error. */
442 else
443 BUG ();
445 return NULL;
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. */
451 static void
452 process (FILE *fp, pattern_t *patarray)
454 char buffer[2048];
455 size_t idx;
456 int c;
457 unsigned long lineno = 0;
458 pattern_t *pat;
460 idx = 0;
461 c = 0;
462 while (idx < sizeof buffer -1 && c != EOF )
464 if ((c = getc (fp)) != EOF)
465 buffer[idx] = c;
466 if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
468 lineno++;
469 if (!opt.null)
471 while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
472 idx--;
474 buffer[idx] = 0;
475 pat = match_p (buffer, patarray);
476 if (pat)
478 if (opt.verbose)
479 log_error ("input line %lu matches pattern at line %u"
480 " - rejected\n",
481 lineno, pat->lineno);
482 exit (1);
484 idx = 0;
486 else
487 idx++;
489 if (c != EOF)
491 log_error ("input line %lu too long - rejected\n", lineno+1);
492 exit (1);
494 if (ferror (fp))
496 log_error ("input read error at line %lu: %s - rejected\n",
497 lineno+1, strerror (errno));
498 exit (1);
500 if (opt.verbose)
501 log_info ("no input line matches the pattern - accepted\n");