~ubuntu-branches/ubuntu/oneiric/gnupg2/oneiric-updates

« back to all changes in this revision

Viewing changes to tools/gpg-check-pattern.c

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-10-04 10:25:53 UTC
  • mfrom: (5.1.15 intrepid)
  • Revision ID: james.westby@ubuntu.com-20081004102553-fv62pp8dsitxli47
Tags: 2.0.9-3.1
* Non-maintainer upload.
* agent/gpg-agent.c: Deinit the threading library before exec'ing
  the command to run in --daemon mode. And because that still doesn't
  restore the sigprocmask, do that manually. Closes: #499569

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* gpg-check-pattern.c - A tool to check passphrases against pattern.
 
2
 * Copyright (C) 2007 Free Software Foundation, Inc.
 
3
 *
 
4
 * This file is part of GnuPG.
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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/>.
 
18
 */
 
19
 
 
20
#include <config.h>
 
21
 
 
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>
 
42
 
 
43
 
 
44
#define JNLIB_NEED_LOG_LOGV
 
45
#include "util.h"
 
46
#include "i18n.h"
 
47
#include "sysutils.h"
 
48
 
 
49
 
 
50
enum cmd_and_opt_values 
 
51
{ aNull = 0,
 
52
  oVerbose        = 'v',
 
53
  oArmor          = 'a',
 
54
  oPassphrase     = 'P',
 
55
 
 
56
  oProtect        = 'p',
 
57
  oUnprotect      = 'u',
 
58
  oNull           = '0',
 
59
 
 
60
  oNoVerbose = 500,
 
61
  oCheck,
 
62
 
 
63
  oHomedir
 
64
};
 
65
 
 
66
 
 
67
/* The list of commands and options.  */
 
68
static ARGPARSE_OPTS opts[] = {
 
69
    
 
70
  { 301, NULL, 0, N_("@Options:\n ") },
 
71
 
 
72
  { oVerbose, "verbose",   0, "verbose" },
 
73
 
 
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" },
 
77
 
 
78
  {0}
 
79
};
 
80
 
 
81
 
 
82
/* Global options are accessed through the usual OPT structure. */
 
83
static struct 
 
84
{
 
85
  int verbose;
 
86
  const char *homedir;
 
87
  int checkonly;
 
88
  int null;
 
89
} opt;
 
90
 
 
91
 
 
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. */
 
96
};
 
97
 
 
98
 
 
99
/* An object to decibe an item of our pattern table. */
 
100
struct pattern_s
 
101
{
 
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;
 
117
};
 
118
typedef struct pattern_s pattern_t;
 
119
 
 
120
 
 
121
 
 
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);
 
126
 
 
127
 
 
128
 
 
129
 
 
130
/* Info function for usage().  */
 
131
static const char *
 
132
my_strusage (int level)
 
133
{
 
134
  const char *p;
 
135
  switch (level)
 
136
    {
 
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 <" PACKAGE_BUGREPORT ">.\n");
 
142
      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;
 
151
    
 
152
    default: p = NULL;
 
153
    }
 
154
  return p;
 
155
}
 
156
 
 
157
 
 
158
int
 
159
main (int argc, char **argv )
 
160
{
 
161
  ARGPARSE_ARGS pargs;
 
162
  char *raw_pattern;
 
163
  size_t raw_pattern_length;
 
164
  pattern_t *patternarray;
 
165
 
 
166
  set_strusage (my_strusage);
 
167
  gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
 
168
  log_set_prefix ("gpg-check-pattern", 1); 
 
169
 
 
170
  /* Make sure that our subsystems are ready.  */
 
171
  init_common_subsystems ();
 
172
 
 
173
  i18n_init ();
 
174
 
 
175
  /* We need Libgcrypt for hashing.  */
 
176
  if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
 
177
    {
 
178
      log_fatal ( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
 
179
                  NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
 
180
    }
 
181
 
 
182
  setup_libgcrypt_logging ();
 
183
  gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);
 
184
 
 
185
  opt.homedir = default_homedir ();
 
186
 
 
187
  pargs.argc = &argc;
 
188
  pargs.argv = &argv;
 
189
  pargs.flags=  1;  /* (do not remove the args) */
 
190
  while (arg_parse (&pargs, opts) )
 
191
    {
 
192
      switch (pargs.r_opt)
 
193
        {
 
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;
 
198
          
 
199
        default : pargs.err = 2; break;
 
200
        }
 
201
    }
 
202
  if (log_get_errorcount(0))
 
203
    exit (2);
 
204
  
 
205
  if (argc != 1)
 
206
    usage (1);
 
207
 
 
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);
 
214
  if (!raw_pattern)
 
215
    exit (2);
 
216
 
 
217
  patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
 
218
  if (!patternarray)
 
219
    exit (1);
 
220
  if (opt.checkonly)
 
221
    return 0;
 
222
 
 
223
#ifdef HAVE_DOSISH_SYSTEM
 
224
  setmode (fileno (stdin) , O_BINARY );
 
225
#endif
 
226
  process (stdin, patternarray);
 
227
 
 
228
  return log_get_errorcount(0)? 1 : 0;
 
229
}
 
230
 
 
231
 
 
232
 
 
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.  */
 
237
static char *
 
238
read_file (const char *fname, size_t *r_length)
 
239
{
 
240
  FILE *fp;
 
241
  char *buf;
 
242
  size_t buflen;
 
243
  
 
244
  if (!strcmp (fname, "-"))
 
245
    {
 
246
      size_t nread, bufsize = 0;
 
247
 
 
248
      fp = stdin;
 
249
#ifdef HAVE_DOSISH_SYSTEM
 
250
      setmode ( fileno(fp) , O_BINARY );
 
251
#endif
 
252
      buf = NULL;
 
253
      buflen = 0;
 
254
#define NCHUNK 8192
 
255
      do 
 
256
        {
 
257
          bufsize += NCHUNK;
 
258
          if (!buf)
 
259
            buf = xmalloc (bufsize+1);
 
260
          else
 
261
            buf = xrealloc (buf, bufsize+1);
 
262
 
 
263
          nread = fread (buf+buflen, 1, NCHUNK, fp);
 
264
          if (nread < NCHUNK && ferror (fp))
 
265
            {
 
266
              log_error ("error reading `[stdin]': %s\n", strerror (errno));
 
267
              xfree (buf);
 
268
              return NULL;
 
269
            }
 
270
          buflen += nread;
 
271
        }
 
272
      while (nread == NCHUNK);
 
273
#undef NCHUNK
 
274
 
 
275
    }
 
276
  else
 
277
    {
 
278
      struct stat st;
 
279
 
 
280
      fp = fopen (fname, "rb");
 
281
      if (!fp)
 
282
        {
 
283
          log_error ("can't open `%s': %s\n", fname, strerror (errno));
 
284
          return NULL;
 
285
        }
 
286
  
 
287
      if (fstat (fileno(fp), &st))
 
288
        {
 
289
          log_error ("can't stat `%s': %s\n", fname, strerror (errno));
 
290
          fclose (fp);
 
291
          return NULL;
 
292
        }
 
293
      
 
294
      buflen = st.st_size;
 
295
      buf = xmalloc (buflen+1);
 
296
      if (fread (buf, buflen, 1, fp) != 1)
 
297
        {
 
298
          log_error ("error reading `%s': %s\n", fname, strerror (errno));
 
299
          fclose (fp);
 
300
          xfree (buf);
 
301
          return NULL;
 
302
        }
 
303
      fclose (fp);
 
304
    }
 
305
  buf[buflen] = 0;
 
306
  *r_length = buflen;
 
307
  return buf;
 
308
}
 
309
 
 
310
 
 
311
 
 
312
static char *
 
313
get_regerror (int errcode, regex_t *compiled)
 
314
{
 
315
  size_t length = regerror (errcode, compiled, NULL, 0);
 
316
  char *buffer = xmalloc (length);
 
317
  regerror (errcode, compiled, buffer, length);
 
318
  return buffer;
 
319
}
 
320
 
 
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
 
326
   DATALEN).  */
 
327
static pattern_t *
 
328
parse_pattern_file (char *data, size_t datalen)
 
329
{
 
330
  char *p, *p2;
 
331
  size_t n;
 
332
  pattern_t *array;
 
333
  size_t arraysize, arrayidx;
 
334
  unsigned int lineno = 0;
 
335
  
 
336
  /* Estimate the number of entries by counting the non-comment lines.  */
 
337
  arraysize = 0;
 
338
  p = data;
 
339
  for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
 
340
    if (*p != '#')
 
341
      arraysize++;
 
342
  arraysize += 2; /* For the terminating NULL and a last line w/o a LF.  */
 
343
 
 
344
  array = xcalloc (arraysize, sizeof *array);
 
345
  arrayidx = 0;
 
346
 
 
347
  /* Loop over all lines.  */
 
348
  while (datalen && data)
 
349
    {
 
350
      lineno++;
 
351
      p = data;
 
352
      p2 = data = memchr (p, '\n', datalen);
 
353
      if (p2)
 
354
        {
 
355
          *data++ = 0;
 
356
          datalen -= data - p;
 
357
        }
 
358
      else
 
359
        p2 = p + datalen;
 
360
      assert (!*p2);
 
361
      p2--;
 
362
      while (isascii (*p) && isspace (*p))
 
363
        p++;
 
364
      if (*p == '#')
 
365
        continue;
 
366
      while (p2 > p && isascii (*p2) && isspace (*p2))
 
367
        *p2-- = 0;
 
368
      if (!*p)
 
369
        continue;
 
370
      assert (arrayidx < arraysize);
 
371
      array[arrayidx].lineno = lineno;
 
372
      if (*p == '/')
 
373
        {
 
374
          int rerr;
 
375
 
 
376
          p++;
 
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);
 
383
          if (rerr)
 
384
            {
 
385
              char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
 
386
              log_error ("invalid r.e. at line %u: %s\n", lineno, rerrbuf);
 
387
              xfree (rerrbuf);
 
388
              if (!opt.checkonly)
 
389
                exit (1);
 
390
            }
 
391
        }
 
392
      else
 
393
        {
 
394
          array[arrayidx].type = PAT_STRING;
 
395
          array[arrayidx].u.s.string = p;
 
396
          array[arrayidx].u.s.length = strlen (p);
 
397
        }
 
398
      arrayidx++;
 
399
    }
 
400
  assert (arrayidx < arraysize);
 
401
  array[arrayidx].type = PAT_NULL;
 
402
 
 
403
  return array;
 
404
}
 
405
 
 
406
 
 
407
/* Check whether string macthes any of the pattern in PATARRAY and
 
408
   returns the matching pattern item or NULL.  */
 
409
static pattern_t *
 
410
match_p (const char *string, pattern_t *patarray)
 
411
{
 
412
  pattern_t *pat;
 
413
 
 
414
  if (!*string)
 
415
    {
 
416
      if (opt.verbose)
 
417
        log_info ("zero length input line - ignored\n");
 
418
      return NULL;
 
419
    }
 
420
 
 
421
  for (pat = patarray; pat->type != PAT_NULL; pat++)
 
422
    {
 
423
      if (pat->type == PAT_STRING)
 
424
        {
 
425
          if (!strcasecmp (pat->u.s.string, string))
 
426
            return pat;
 
427
        }
 
428
      else if (pat->type == PAT_REGEX)
 
429
        {
 
430
          int rerr;
 
431
 
 
432
          rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
 
433
          if (!rerr)
 
434
            return pat;
 
435
          else if (rerr != REG_NOMATCH)
 
436
            {
 
437
              char *rerrbuf = get_regerror (rerr, pat->u.r.regex);
 
438
              log_error ("matching r.e. failed: %s\n", rerrbuf);
 
439
              xfree (rerrbuf);
 
440
              return pat;  /* Better indicate a match on error.  */
 
441
            }
 
442
        }
 
443
      else
 
444
        BUG ();
 
445
    }
 
446
  return NULL;
 
447
}
 
448
 
 
449
 
 
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.  */
 
452
static void
 
453
process (FILE *fp, pattern_t *patarray)
 
454
{
 
455
  char buffer[2048];
 
456
  size_t idx;
 
457
  int c;
 
458
  unsigned long lineno = 0;
 
459
  pattern_t *pat;
 
460
  
 
461
  idx = 0;
 
462
  c = 0;
 
463
  while (idx < sizeof buffer -1 && c != EOF )
 
464
    {
 
465
      if ((c = getc (fp)) != EOF)
 
466
        buffer[idx] = c;
 
467
      if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
 
468
        {
 
469
          lineno++;
 
470
          if (!opt.null)
 
471
            {
 
472
              while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
 
473
                idx--;
 
474
            }
 
475
          buffer[idx] = 0;
 
476
          pat = match_p (buffer, patarray);
 
477
          if (pat)
 
478
            {
 
479
              if (opt.verbose)
 
480
                log_error ("input line %lu matches pattern at line %u"
 
481
                           " - rejected\n",
 
482
                           lineno, pat->lineno);
 
483
              exit (1);
 
484
            }
 
485
          idx = 0;
 
486
        }
 
487
      else
 
488
        idx++;
 
489
    }
 
490
  if (c != EOF)
 
491
    {
 
492
      log_error ("input line %lu too long - rejected\n", lineno+1);
 
493
      exit (1);
 
494
    }
 
495
  if (ferror (fp))
 
496
    {
 
497
      log_error ("input read error at line %lu: %s - rejected\n",
 
498
                 lineno+1, strerror (errno));
 
499
      exit (1);
 
500
    }
 
501
  if (opt.verbose)
 
502
    log_info ("no input line matches the pattern - accepted\n");
 
503
}
 
504