LCOV - code coverage report
Current view: top level - tools - gpg-check-pattern.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 187 0.0 %
Date: 2016-11-29 15:00:56 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 <https://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", GPGRT_LOG_WITH_PREFIX);
     169             : 
     170             :   /* Make sure that our subsystems are ready.  */
     171           0 :   i18n_init ();
     172           0 :   init_common_subsystems (&argc, &argv);
     173             : 
     174           0 :   setup_libgcrypt_logging ();
     175           0 :   gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);
     176             : 
     177           0 :   pargs.argc = &argc;
     178           0 :   pargs.argv = &argv;
     179           0 :   pargs.flags=  1;  /* (do not remove the args) */
     180           0 :   while (arg_parse (&pargs, opts) )
     181             :     {
     182           0 :       switch (pargs.r_opt)
     183             :         {
     184           0 :         case oVerbose: opt.verbose++; break;
     185           0 :         case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
     186           0 :         case oCheck: opt.checkonly = 1; break;
     187           0 :         case oNull: opt.null = 1; break;
     188             : 
     189           0 :         default : pargs.err = 2; break;
     190             :         }
     191             :     }
     192           0 :   if (log_get_errorcount(0))
     193           0 :     exit (2);
     194             : 
     195           0 :   if (argc != 1)
     196           0 :     usage (1);
     197             : 
     198             :   /* We read the entire pattern file into our memory and parse it
     199             :      using a separate function.  This allows us to eventual do the
     200             :      reading while running setuid so that the pattern file can be
     201             :      hidden from regular users.  I am not sure whether this makes
     202             :      sense, but lets be prepared for it.  */
     203           0 :   raw_pattern = read_file (*argv, &raw_pattern_length);
     204           0 :   if (!raw_pattern)
     205           0 :     exit (2);
     206             : 
     207           0 :   patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
     208           0 :   if (!patternarray)
     209           0 :     exit (1);
     210           0 :   if (opt.checkonly)
     211           0 :     return 0;
     212             : 
     213             : #ifdef HAVE_DOSISH_SYSTEM
     214             :   setmode (fileno (stdin) , O_BINARY );
     215             : #endif
     216           0 :   process (stdin, patternarray);
     217             : 
     218           0 :   return log_get_errorcount(0)? 1 : 0;
     219             : }
     220             : 
     221             : 
     222             : 
     223             : /* Read a file FNAME into a buffer and return that malloced buffer.
     224             :    Caller must free the buffer.  On error NULL is returned, on success
     225             :    the valid length of the buffer is stored at R_LENGTH.  The returned
     226             :    buffer is guarnteed to be nul terminated.  */
     227             : static char *
     228           0 : read_file (const char *fname, size_t *r_length)
     229             : {
     230             :   FILE *fp;
     231             :   char *buf;
     232             :   size_t buflen;
     233             : 
     234           0 :   if (!strcmp (fname, "-"))
     235             :     {
     236           0 :       size_t nread, bufsize = 0;
     237             : 
     238           0 :       fp = stdin;
     239             : #ifdef HAVE_DOSISH_SYSTEM
     240             :       setmode ( fileno(fp) , O_BINARY );
     241             : #endif
     242           0 :       buf = NULL;
     243           0 :       buflen = 0;
     244             : #define NCHUNK 8192
     245             :       do
     246             :         {
     247           0 :           bufsize += NCHUNK;
     248           0 :           if (!buf)
     249           0 :             buf = xmalloc (bufsize+1);
     250             :           else
     251           0 :             buf = xrealloc (buf, bufsize+1);
     252             : 
     253           0 :           nread = fread (buf+buflen, 1, NCHUNK, fp);
     254           0 :           if (nread < NCHUNK && ferror (fp))
     255             :             {
     256           0 :               log_error ("error reading '[stdin]': %s\n", strerror (errno));
     257           0 :               xfree (buf);
     258           0 :               return NULL;
     259             :             }
     260           0 :           buflen += nread;
     261             :         }
     262           0 :       while (nread == NCHUNK);
     263             : #undef NCHUNK
     264             : 
     265             :     }
     266             :   else
     267             :     {
     268             :       struct stat st;
     269             : 
     270           0 :       fp = fopen (fname, "rb");
     271           0 :       if (!fp)
     272             :         {
     273           0 :           log_error ("can't open '%s': %s\n", fname, strerror (errno));
     274           0 :           return NULL;
     275             :         }
     276             : 
     277           0 :       if (fstat (fileno(fp), &st))
     278             :         {
     279           0 :           log_error ("can't stat '%s': %s\n", fname, strerror (errno));
     280           0 :           fclose (fp);
     281           0 :           return NULL;
     282             :         }
     283             : 
     284           0 :       buflen = st.st_size;
     285           0 :       buf = xmalloc (buflen+1);
     286           0 :       if (fread (buf, buflen, 1, fp) != 1)
     287             :         {
     288           0 :           log_error ("error reading '%s': %s\n", fname, strerror (errno));
     289           0 :           fclose (fp);
     290           0 :           xfree (buf);
     291           0 :           return NULL;
     292             :         }
     293           0 :       fclose (fp);
     294             :     }
     295           0 :   buf[buflen] = 0;
     296           0 :   *r_length = buflen;
     297           0 :   return buf;
     298             : }
     299             : 
     300             : 
     301             : 
     302             : static char *
     303           0 : get_regerror (int errcode, regex_t *compiled)
     304             : {
     305           0 :   size_t length = regerror (errcode, compiled, NULL, 0);
     306           0 :   char *buffer = xmalloc (length);
     307           0 :   regerror (errcode, compiled, buffer, length);
     308           0 :   return buffer;
     309             : }
     310             : 
     311             : /* Parse the pattern given in the memory aread DATA/DATALEN and return
     312             :    a new pattern array.  The end of the array is indicated by a NULL
     313             :    entry.  On error an error message is printed and the function
     314             :    returns NULL.  Note that the function modifies DATA and assumes
     315             :    that data is nul terminated (even if this is one byte past
     316             :    DATALEN).  */
     317             : static pattern_t *
     318           0 : parse_pattern_file (char *data, size_t datalen)
     319             : {
     320             :   char *p, *p2;
     321             :   size_t n;
     322             :   pattern_t *array;
     323             :   size_t arraysize, arrayidx;
     324           0 :   unsigned int lineno = 0;
     325             : 
     326             :   /* Estimate the number of entries by counting the non-comment lines.  */
     327           0 :   arraysize = 0;
     328           0 :   p = data;
     329           0 :   for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
     330           0 :     if (*p != '#')
     331           0 :       arraysize++;
     332           0 :   arraysize += 2; /* For the terminating NULL and a last line w/o a LF.  */
     333             : 
     334           0 :   array = xcalloc (arraysize, sizeof *array);
     335           0 :   arrayidx = 0;
     336             : 
     337             :   /* Loop over all lines.  */
     338           0 :   while (datalen && data)
     339             :     {
     340           0 :       lineno++;
     341           0 :       p = data;
     342           0 :       p2 = data = memchr (p, '\n', datalen);
     343           0 :       if (p2)
     344             :         {
     345           0 :           *data++ = 0;
     346           0 :           datalen -= data - p;
     347             :         }
     348             :       else
     349           0 :         p2 = p + datalen;
     350           0 :       assert (!*p2);
     351           0 :       p2--;
     352           0 :       while (isascii (*p) && isspace (*p))
     353           0 :         p++;
     354           0 :       if (*p == '#')
     355           0 :         continue;
     356           0 :       while (p2 > p && isascii (*p2) && isspace (*p2))
     357           0 :         *p2-- = 0;
     358           0 :       if (!*p)
     359           0 :         continue;
     360           0 :       assert (arrayidx < arraysize);
     361           0 :       array[arrayidx].lineno = lineno;
     362           0 :       if (*p == '/')
     363             :         {
     364             :           int rerr;
     365             : 
     366           0 :           p++;
     367           0 :           array[arrayidx].type = PAT_REGEX;
     368           0 :           if (*p && p[strlen(p)-1] == '/')
     369           0 :             p[strlen(p)-1] = 0;  /* Remove optional delimiter.  */
     370           0 :           array[arrayidx].u.r.regex = xcalloc (1, sizeof (regex_t));
     371           0 :           rerr = regcomp (array[arrayidx].u.r.regex, p,
     372             :                           REG_ICASE|REG_NOSUB|REG_EXTENDED);
     373           0 :           if (rerr)
     374             :             {
     375           0 :               char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
     376           0 :               log_error ("invalid r.e. at line %u: %s\n", lineno, rerrbuf);
     377           0 :               xfree (rerrbuf);
     378           0 :               if (!opt.checkonly)
     379           0 :                 exit (1);
     380             :             }
     381             :         }
     382             :       else
     383             :         {
     384           0 :           array[arrayidx].type = PAT_STRING;
     385           0 :           array[arrayidx].u.s.string = p;
     386           0 :           array[arrayidx].u.s.length = strlen (p);
     387             :         }
     388           0 :       arrayidx++;
     389             :     }
     390           0 :   assert (arrayidx < arraysize);
     391           0 :   array[arrayidx].type = PAT_NULL;
     392             : 
     393           0 :   return array;
     394             : }
     395             : 
     396             : 
     397             : /* Check whether string macthes any of the pattern in PATARRAY and
     398             :    returns the matching pattern item or NULL.  */
     399             : static pattern_t *
     400           0 : match_p (const char *string, pattern_t *patarray)
     401             : {
     402             :   pattern_t *pat;
     403             : 
     404           0 :   if (!*string)
     405             :     {
     406           0 :       if (opt.verbose)
     407           0 :         log_info ("zero length input line - ignored\n");
     408           0 :       return NULL;
     409             :     }
     410             : 
     411           0 :   for (pat = patarray; pat->type != PAT_NULL; pat++)
     412             :     {
     413           0 :       if (pat->type == PAT_STRING)
     414             :         {
     415           0 :           if (!strcasecmp (pat->u.s.string, string))
     416           0 :             return pat;
     417             :         }
     418           0 :       else if (pat->type == PAT_REGEX)
     419             :         {
     420             :           int rerr;
     421             : 
     422           0 :           rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
     423           0 :           if (!rerr)
     424           0 :             return pat;
     425           0 :           else if (rerr != REG_NOMATCH)
     426             :             {
     427           0 :               char *rerrbuf = get_regerror (rerr, pat->u.r.regex);
     428           0 :               log_error ("matching r.e. failed: %s\n", rerrbuf);
     429           0 :               xfree (rerrbuf);
     430           0 :               return pat;  /* Better indicate a match on error.  */
     431             :             }
     432             :         }
     433             :       else
     434           0 :         BUG ();
     435             :     }
     436           0 :   return NULL;
     437             : }
     438             : 
     439             : 
     440             : /* Actual processing of the input.  This function does not return an
     441             :    error code but exits as soon as a match has been found.  */
     442             : static void
     443           0 : process (FILE *fp, pattern_t *patarray)
     444             : {
     445             :   char buffer[2048];
     446             :   size_t idx;
     447             :   int c;
     448           0 :   unsigned long lineno = 0;
     449             :   pattern_t *pat;
     450             : 
     451           0 :   idx = 0;
     452           0 :   c = 0;
     453           0 :   while (idx < sizeof buffer -1 && c != EOF )
     454             :     {
     455           0 :       if ((c = getc (fp)) != EOF)
     456           0 :         buffer[idx] = c;
     457           0 :       if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
     458             :         {
     459           0 :           lineno++;
     460           0 :           if (!opt.null)
     461             :             {
     462           0 :               while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
     463           0 :                 idx--;
     464             :             }
     465           0 :           buffer[idx] = 0;
     466           0 :           pat = match_p (buffer, patarray);
     467           0 :           if (pat)
     468             :             {
     469           0 :               if (opt.verbose)
     470           0 :                 log_error ("input line %lu matches pattern at line %u"
     471             :                            " - rejected\n",
     472             :                            lineno, pat->lineno);
     473           0 :               exit (1);
     474             :             }
     475           0 :           idx = 0;
     476             :         }
     477             :       else
     478           0 :         idx++;
     479             :     }
     480           0 :   if (c != EOF)
     481             :     {
     482           0 :       log_error ("input line %lu too long - rejected\n", lineno+1);
     483           0 :       exit (1);
     484             :     }
     485           0 :   if (ferror (fp))
     486             :     {
     487           0 :       log_error ("input read error at line %lu: %s - rejected\n",
     488           0 :                  lineno+1, strerror (errno));
     489           0 :       exit (1);
     490             :     }
     491           0 :   if (opt.verbose)
     492           0 :     log_info ("no input line matches the pattern - accepted\n");
     493           0 : }
     494             : 

Generated by: LCOV version 1.11