LCOV - code coverage report
Current view: top level - tools - gpg-check-pattern.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 190 0.0 %
Date: 2015-11-05 17:10:59 Functions: 0 7 0.0 %

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

Generated by: LCOV version 1.11