2007-08-27 20:10:27 +02:00
|
|
|
|
/* gpg-check-pattern.c - A tool to check passphrases against pattern.
|
2021-07-29 11:25:06 +02:00
|
|
|
|
* Copyright (C) 2021 g10 Code GmbH
|
2007-08-27 20:10:27 +02:00
|
|
|
|
* Copyright (C) 2007 Free Software Foundation, Inc.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2020-02-21 20:28:47 +01:00
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
2007-08-27 20:10:27 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#ifdef HAVE_LOCALE_H
|
|
|
|
|
# include <locale.h>
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_LANGINFO_CODESET
|
|
|
|
|
# include <langinfo.h>
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
# include <fcntl.h> /* for setmode() */
|
|
|
|
|
#endif
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
2017-03-07 12:21:23 +01:00
|
|
|
|
#include "../common/util.h"
|
|
|
|
|
#include "../common/i18n.h"
|
|
|
|
|
#include "../common/sysutils.h"
|
2012-02-06 20:50:47 +01:00
|
|
|
|
#include "../common/init.h"
|
2020-04-06 02:38:13 +02:00
|
|
|
|
#include "../regexp/jimregexp.h"
|
2007-08-27 20:10:27 +02:00
|
|
|
|
|
|
|
|
|
|
2012-02-06 20:50:47 +01:00
|
|
|
|
enum cmd_and_opt_values
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{ aNull = 0,
|
|
|
|
|
oVerbose = 'v',
|
|
|
|
|
|
|
|
|
|
oNull = '0',
|
|
|
|
|
|
|
|
|
|
oNoVerbose = 500,
|
|
|
|
|
oCheck,
|
|
|
|
|
|
|
|
|
|
oHomedir
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The list of commands and options. */
|
2020-02-21 20:28:47 +01:00
|
|
|
|
static gpgrt_opt_t opts[] = {
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{ 301, NULL, 0, N_("@Options:\n ") },
|
|
|
|
|
|
|
|
|
|
{ oVerbose, "verbose", 0, "verbose" },
|
|
|
|
|
|
2012-02-06 20:50:47 +01:00
|
|
|
|
{ oHomedir, "homedir", 2, "@" },
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{ oCheck, "check", 0, "run only a syntax check on the patternfile" },
|
|
|
|
|
{ oNull, "null", 0, "input is expected to be null delimited" },
|
|
|
|
|
|
2017-07-19 06:41:18 +02:00
|
|
|
|
ARGPARSE_end ()
|
2007-08-27 20:10:27 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Global options are accessed through the usual OPT structure. */
|
2012-02-06 20:50:47 +01:00
|
|
|
|
static struct
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
|
|
|
|
int verbose;
|
|
|
|
|
const char *homedir;
|
|
|
|
|
int checkonly;
|
|
|
|
|
int null;
|
|
|
|
|
} opt;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
PAT_NULL, /* Indicates end of the array. */
|
|
|
|
|
PAT_STRING, /* The pattern is a simple string. */
|
2018-10-25 22:52:58 +02:00
|
|
|
|
PAT_REGEX /* The pattern is an extended regular expression. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* An object to decibe an item of our pattern table. */
|
|
|
|
|
struct pattern_s
|
|
|
|
|
{
|
2012-02-06 20:50:47 +01:00
|
|
|
|
int type;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
unsigned int lineno; /* Line number of the pattern file. */
|
2021-07-29 11:25:06 +02:00
|
|
|
|
unsigned int newblock; /* First pattern in a new block. */
|
|
|
|
|
unsigned int icase:1; /* Case insensitive match. */
|
|
|
|
|
unsigned int accept:1; /* In accept mode. */
|
|
|
|
|
unsigned int reverse:1; /* Reverse the outcome of a regexp match. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
const char *string; /* Pointer to the actual string (nul termnated). */
|
|
|
|
|
size_t length; /* The length of this string (strlen). */
|
|
|
|
|
} s; /*PAT_STRING*/
|
|
|
|
|
struct {
|
|
|
|
|
/* We allocate the regex_t because this type is larger than what
|
|
|
|
|
we need for PAT_STRING and we expect only a few regex in a
|
|
|
|
|
patternfile. It would be a waste of core to have so many
|
|
|
|
|
unused stuff in the table. */
|
2012-02-06 20:50:47 +01:00
|
|
|
|
regex_t *regex;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
} r; /*PAT_REGEX*/
|
|
|
|
|
} u;
|
|
|
|
|
};
|
|
|
|
|
typedef struct pattern_s pattern_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*** Local prototypes ***/
|
|
|
|
|
static char *read_file (const char *fname, size_t *r_length);
|
|
|
|
|
static pattern_t *parse_pattern_file (char *data, size_t datalen);
|
|
|
|
|
static void process (FILE *fp, pattern_t *patarray);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Info function for usage(). */
|
|
|
|
|
static const char *
|
|
|
|
|
my_strusage (int level)
|
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
switch (level)
|
|
|
|
|
{
|
2020-02-21 20:28:47 +01:00
|
|
|
|
case 9: p = "GPL-3.0-or-later"; break;
|
2013-11-18 14:09:47 +01:00
|
|
|
|
case 11: p = "gpg-check-pattern (@GnuPG@)";
|
2007-08-27 20:10:27 +02:00
|
|
|
|
break;
|
|
|
|
|
case 13: p = VERSION; break;
|
2020-02-21 20:28:47 +01:00
|
|
|
|
case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
case 17: p = PRINTABLE_OS_NAME; break;
|
2009-07-21 16:21:05 +02:00
|
|
|
|
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
|
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
case 1:
|
2012-02-06 20:50:47 +01:00
|
|
|
|
case 40:
|
2007-08-27 20:10:27 +02:00
|
|
|
|
p = _("Usage: gpg-check-pattern [options] patternfile (-h for help)\n");
|
|
|
|
|
break;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
case 41:
|
2007-08-27 20:10:27 +02:00
|
|
|
|
p = _("Syntax: gpg-check-pattern [options] patternfile\n"
|
|
|
|
|
"Check a passphrase given on stdin against the patternfile\n");
|
|
|
|
|
break;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
default: p = NULL;
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main (int argc, char **argv )
|
|
|
|
|
{
|
2020-02-21 20:28:47 +01:00
|
|
|
|
gpgrt_argparse_t pargs;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
char *raw_pattern;
|
|
|
|
|
size_t raw_pattern_length;
|
|
|
|
|
pattern_t *patternarray;
|
|
|
|
|
|
2015-01-28 19:57:22 +01:00
|
|
|
|
early_system_init ();
|
2020-02-21 20:28:47 +01:00
|
|
|
|
gpgrt_set_strusage (my_strusage);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
Call log_set_prefix() with human-readable labels.
* agent/preset-passphrase.c, agent/protect-tool.c, dirmngr/dirmngr.c
* dirmngr/t-http.c, g10/gpg.c, g10/gpgv.c, g13/g13-syshelp.c
* g13/g13.c, kbx/kbxutil.c, scd/scdaemon.c, sm/gpgsm.c
* tests/gpgscm/main.c, tools/gpg-check-pattern.c
* tools/gpg-connect-agent.c, tools/gpgconf.c, tools/gpgtar.c
* tools/symcryptrun.c: Invoke log_set_prefix() with
human-readable labels.
--
Some invocations of log_set_prefix() were done with raw numeric values
instead of values that humans can understand. Use symbolic
representations instead of numeric for better readability.
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2016-08-12 07:37:57 +02:00
|
|
|
|
log_set_prefix ("gpg-check-pattern", GPGRT_LOG_WITH_PREFIX);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
|
|
|
|
|
/* Make sure that our subsystems are ready. */
|
|
|
|
|
i18n_init ();
|
2010-03-22 13:46:05 +01:00
|
|
|
|
init_common_subsystems (&argc, &argv);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
|
|
|
|
|
setup_libgcrypt_logging ();
|
|
|
|
|
gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);
|
|
|
|
|
|
|
|
|
|
pargs.argc = &argc;
|
|
|
|
|
pargs.argv = &argv;
|
2020-02-21 20:28:47 +01:00
|
|
|
|
pargs.flags= ARGPARSE_FLAG_KEEP;
|
|
|
|
|
while (gpgrt_argparse (NULL, &pargs, opts))
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
|
|
|
|
switch (pargs.r_opt)
|
|
|
|
|
{
|
|
|
|
|
case oVerbose: opt.verbose++; break;
|
2016-06-07 10:59:46 +02:00
|
|
|
|
case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
case oCheck: opt.checkonly = 1; break;
|
|
|
|
|
case oNull: opt.null = 1; break;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
default : pargs.err = 2; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 20:28:47 +01:00
|
|
|
|
gpgrt_argparse (NULL, &pargs, NULL); /* Release internal state. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (log_get_errorcount(0))
|
|
|
|
|
exit (2);
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (argc != 1)
|
2020-02-21 20:28:47 +01:00
|
|
|
|
gpgrt_usage (1);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
|
|
|
|
|
/* We read the entire pattern file into our memory and parse it
|
2021-07-29 11:25:06 +02:00
|
|
|
|
using a separate function. This allows us to eventually do the
|
2007-08-27 20:10:27 +02:00
|
|
|
|
reading while running setuid so that the pattern file can be
|
|
|
|
|
hidden from regular users. I am not sure whether this makes
|
|
|
|
|
sense, but lets be prepared for it. */
|
|
|
|
|
raw_pattern = read_file (*argv, &raw_pattern_length);
|
|
|
|
|
if (!raw_pattern)
|
|
|
|
|
exit (2);
|
|
|
|
|
|
|
|
|
|
patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
|
|
|
|
|
if (!patternarray)
|
|
|
|
|
exit (1);
|
|
|
|
|
if (opt.checkonly)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
|
|
|
setmode (fileno (stdin) , O_BINARY );
|
|
|
|
|
#endif
|
|
|
|
|
process (stdin, patternarray);
|
|
|
|
|
|
2021-07-29 11:25:06 +02:00
|
|
|
|
return 4; /*NOTREACHED*/
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read a file FNAME into a buffer and return that malloced buffer.
|
|
|
|
|
Caller must free the buffer. On error NULL is returned, on success
|
|
|
|
|
the valid length of the buffer is stored at R_LENGTH. The returned
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 15:34:42 +01:00
|
|
|
|
buffer is guaranteed to be nul terminated. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
static char *
|
|
|
|
|
read_file (const char *fname, size_t *r_length)
|
|
|
|
|
{
|
2020-10-20 11:52:16 +02:00
|
|
|
|
estream_t fp;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
char *buf;
|
|
|
|
|
size_t buflen;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (!strcmp (fname, "-"))
|
|
|
|
|
{
|
|
|
|
|
size_t nread, bufsize = 0;
|
|
|
|
|
|
2020-10-20 11:52:16 +02:00
|
|
|
|
fp = es_stdin;
|
|
|
|
|
es_set_binary (fp);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
buf = NULL;
|
|
|
|
|
buflen = 0;
|
|
|
|
|
#define NCHUNK 8192
|
2012-02-06 20:50:47 +01:00
|
|
|
|
do
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
|
|
|
|
bufsize += NCHUNK;
|
|
|
|
|
if (!buf)
|
|
|
|
|
buf = xmalloc (bufsize+1);
|
|
|
|
|
else
|
|
|
|
|
buf = xrealloc (buf, bufsize+1);
|
|
|
|
|
|
2020-10-20 11:52:16 +02:00
|
|
|
|
nread = es_fread (buf+buflen, 1, NCHUNK, fp);
|
|
|
|
|
if (nread < NCHUNK && es_ferror (fp))
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("error reading '[stdin]': %s\n", strerror (errno));
|
2007-08-27 20:10:27 +02:00
|
|
|
|
xfree (buf);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
buflen += nread;
|
|
|
|
|
}
|
|
|
|
|
while (nread == NCHUNK);
|
|
|
|
|
#undef NCHUNK
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
2020-10-20 11:52:16 +02:00
|
|
|
|
fp = es_fopen (fname, "rb");
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("can't open '%s': %s\n", fname, strerror (errno));
|
2007-08-27 20:10:27 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2020-10-20 11:52:16 +02:00
|
|
|
|
if (fstat (es_fileno (fp), &st))
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("can't stat '%s': %s\n", fname, strerror (errno));
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fclose (fp);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
buflen = st.st_size;
|
|
|
|
|
buf = xmalloc (buflen+1);
|
2024-03-13 15:32:10 +01:00
|
|
|
|
if (buflen && es_fread (buf, buflen, 1, fp) != 1)
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("error reading '%s': %s\n", fname, strerror (errno));
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fclose (fp);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
xfree (buf);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fclose (fp);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
buf[buflen] = 0;
|
|
|
|
|
*r_length = buflen;
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
get_regerror (int errcode, regex_t *compiled)
|
|
|
|
|
{
|
|
|
|
|
size_t length = regerror (errcode, compiled, NULL, 0);
|
|
|
|
|
char *buffer = xmalloc (length);
|
|
|
|
|
regerror (errcode, compiled, buffer, length);
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-29 11:25:06 +02:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
/* Parse the pattern given in the memory aread DATA/DATALEN and return
|
|
|
|
|
a new pattern array. The end of the array is indicated by a NULL
|
2015-11-16 12:41:46 +01:00
|
|
|
|
entry. On error an error message is printed and the function
|
2007-08-27 20:10:27 +02:00
|
|
|
|
returns NULL. Note that the function modifies DATA and assumes
|
|
|
|
|
that data is nul terminated (even if this is one byte past
|
|
|
|
|
DATALEN). */
|
|
|
|
|
static pattern_t *
|
|
|
|
|
parse_pattern_file (char *data, size_t datalen)
|
|
|
|
|
{
|
|
|
|
|
char *p, *p2;
|
|
|
|
|
size_t n;
|
|
|
|
|
pattern_t *array;
|
|
|
|
|
size_t arraysize, arrayidx;
|
|
|
|
|
unsigned int lineno = 0;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
unsigned int icase_mode = 1;
|
|
|
|
|
unsigned int accept_mode = 0;
|
2024-05-13 00:09:23 +02:00
|
|
|
|
unsigned int newblock = 1; /* The first implicit block. */
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
/* Estimate the number of entries by counting the non-comment lines. */
|
|
|
|
|
arraysize = 0;
|
|
|
|
|
p = data;
|
|
|
|
|
for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
|
|
|
|
|
if (*p != '#')
|
|
|
|
|
arraysize++;
|
|
|
|
|
arraysize += 2; /* For the terminating NULL and a last line w/o a LF. */
|
|
|
|
|
|
|
|
|
|
array = xcalloc (arraysize, sizeof *array);
|
|
|
|
|
arrayidx = 0;
|
|
|
|
|
|
|
|
|
|
/* Loop over all lines. */
|
|
|
|
|
while (datalen && data)
|
|
|
|
|
{
|
|
|
|
|
lineno++;
|
|
|
|
|
p = data;
|
|
|
|
|
p2 = data = memchr (p, '\n', datalen);
|
|
|
|
|
if (p2)
|
|
|
|
|
{
|
|
|
|
|
*data++ = 0;
|
|
|
|
|
datalen -= data - p;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
p2 = p + datalen;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
log_assert (!*p2);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
p2--;
|
|
|
|
|
while (isascii (*p) && isspace (*p))
|
|
|
|
|
p++;
|
|
|
|
|
if (*p == '#')
|
|
|
|
|
continue;
|
|
|
|
|
while (p2 > p && isascii (*p2) && isspace (*p2))
|
|
|
|
|
*p2-- = 0;
|
|
|
|
|
if (!*p)
|
|
|
|
|
continue;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (!strcmp (p, "[case]"))
|
|
|
|
|
{
|
|
|
|
|
icase_mode = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcmp (p, "[icase]"))
|
|
|
|
|
{
|
|
|
|
|
icase_mode = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcmp (p, "[accept]"))
|
|
|
|
|
{
|
|
|
|
|
accept_mode = 1;
|
|
|
|
|
newblock = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcmp (p, "[reject]"))
|
|
|
|
|
{
|
|
|
|
|
accept_mode = 0;
|
|
|
|
|
newblock = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_assert (arrayidx < arraysize);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
array[arrayidx].lineno = lineno;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
array[arrayidx].icase = icase_mode;
|
|
|
|
|
array[arrayidx].accept = accept_mode;
|
|
|
|
|
array[arrayidx].reverse = 0;
|
|
|
|
|
array[arrayidx].newblock = newblock;
|
|
|
|
|
newblock = 0;
|
|
|
|
|
|
|
|
|
|
if (*p == '/' || (*p == '!' && p[1] == '/'))
|
2007-08-27 20:10:27 +02:00
|
|
|
|
{
|
|
|
|
|
int rerr;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
int reverse;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
|
2021-07-29 11:25:06 +02:00
|
|
|
|
reverse = (*p == '!');
|
2007-08-27 20:10:27 +02:00
|
|
|
|
p++;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (reverse)
|
|
|
|
|
p++;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
array[arrayidx].type = PAT_REGEX;
|
|
|
|
|
if (*p && p[strlen(p)-1] == '/')
|
|
|
|
|
p[strlen(p)-1] = 0; /* Remove optional delimiter. */
|
|
|
|
|
array[arrayidx].u.r.regex = xcalloc (1, sizeof (regex_t));
|
2021-07-29 11:25:06 +02:00
|
|
|
|
array[arrayidx].reverse = reverse;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
rerr = regcomp (array[arrayidx].u.r.regex, p,
|
2021-07-29 11:25:06 +02:00
|
|
|
|
(array[arrayidx].icase? REG_ICASE:0)|REG_EXTENDED);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (rerr)
|
|
|
|
|
{
|
|
|
|
|
char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
|
2021-07-29 11:25:06 +02:00
|
|
|
|
log_error ("invalid regexp at line %u: %s\n", lineno, rerrbuf);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
xfree (rerrbuf);
|
|
|
|
|
if (!opt.checkonly)
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (*p == '[')
|
|
|
|
|
{
|
|
|
|
|
static int shown;
|
|
|
|
|
|
|
|
|
|
if (!shown)
|
|
|
|
|
{
|
|
|
|
|
log_info ("future warning: do no start a string with '['"
|
|
|
|
|
" but use a regexp (line %u)\n", lineno);
|
|
|
|
|
shown = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-08-27 20:10:27 +02:00
|
|
|
|
array[arrayidx].type = PAT_STRING;
|
|
|
|
|
array[arrayidx].u.s.string = p;
|
|
|
|
|
array[arrayidx].u.s.length = strlen (p);
|
|
|
|
|
}
|
2021-07-29 11:25:06 +02:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
arrayidx++;
|
|
|
|
|
}
|
2021-07-29 11:25:06 +02:00
|
|
|
|
log_assert (arrayidx < arraysize);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
array[arrayidx].type = PAT_NULL;
|
|
|
|
|
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (lineno && newblock)
|
|
|
|
|
log_info ("warning: pattern list ends with a singleton"
|
|
|
|
|
" accept or reject tag\n");
|
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
return array;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-07-29 11:25:06 +02:00
|
|
|
|
/* Check whether string matches any of the pattern in PATARRAY and
|
2007-08-27 20:10:27 +02:00
|
|
|
|
returns the matching pattern item or NULL. */
|
|
|
|
|
static pattern_t *
|
|
|
|
|
match_p (const char *string, pattern_t *patarray)
|
|
|
|
|
{
|
|
|
|
|
pattern_t *pat;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
int match;
|
|
|
|
|
int accept_match; /* Tracks matchinf state in an accept block. */
|
|
|
|
|
int accept_skip; /* Skip remaining patterns in an accept block. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
|
|
|
|
|
if (!*string)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("zero length input line - ignored\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-29 11:25:06 +02:00
|
|
|
|
accept_match = 0;
|
|
|
|
|
accept_skip = 0;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
for (pat = patarray; pat->type != PAT_NULL; pat++)
|
|
|
|
|
{
|
2021-07-29 11:25:06 +02:00
|
|
|
|
match = 0;
|
|
|
|
|
if (pat->newblock)
|
|
|
|
|
accept_match = accept_skip = 0;
|
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (pat->type == PAT_STRING)
|
|
|
|
|
{
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (pat->icase)
|
|
|
|
|
{
|
|
|
|
|
if (!strcasecmp (pat->u.s.string, string))
|
|
|
|
|
match = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp (pat->u.s.string, string))
|
|
|
|
|
match = 1;
|
|
|
|
|
}
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
else if (pat->type == PAT_REGEX)
|
|
|
|
|
{
|
|
|
|
|
int rerr;
|
|
|
|
|
|
|
|
|
|
rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (pat->reverse)
|
|
|
|
|
{
|
|
|
|
|
if (!rerr)
|
|
|
|
|
rerr = REG_NOMATCH;
|
|
|
|
|
else if (rerr == REG_NOMATCH)
|
|
|
|
|
rerr = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (!rerr)
|
2021-07-29 11:25:06 +02:00
|
|
|
|
match = 1;
|
2007-08-27 20:10:27 +02:00
|
|
|
|
else if (rerr != REG_NOMATCH)
|
|
|
|
|
{
|
|
|
|
|
char *rerrbuf = get_regerror (rerr, pat->u.r.regex);
|
2021-07-29 11:25:06 +02:00
|
|
|
|
log_error ("matching regexp failed: %s\n", rerrbuf);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
xfree (rerrbuf);
|
2021-07-29 11:25:06 +02:00
|
|
|
|
if (pat->accept)
|
|
|
|
|
match = 0; /* Better indicate no match on error. */
|
|
|
|
|
else
|
|
|
|
|
match = 1; /* Better indicate a match on error. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
BUG ();
|
2021-07-29 11:25:06 +02:00
|
|
|
|
|
|
|
|
|
if (pat->accept)
|
|
|
|
|
{
|
|
|
|
|
/* Accept mode: all patterns in the accept block must match.
|
|
|
|
|
* Thus we need to check whether the next pattern has a
|
|
|
|
|
* transition and act only then. */
|
|
|
|
|
if (match && !accept_skip)
|
|
|
|
|
accept_match = 1;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
accept_match = 0;
|
|
|
|
|
accept_skip = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pat[1].type == PAT_NULL || pat[1].newblock)
|
|
|
|
|
{
|
|
|
|
|
/* Transition detected. Note that this also handles the
|
|
|
|
|
* end of pattern loop case. */
|
|
|
|
|
if (accept_match)
|
|
|
|
|
return pat;
|
|
|
|
|
/* The next is not really but we do it for clarity. */
|
|
|
|
|
accept_match = accept_skip = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else /* Reject mode: Return true on the first match. */
|
|
|
|
|
{
|
|
|
|
|
if (match)
|
|
|
|
|
return pat;
|
|
|
|
|
}
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-11-16 12:41:46 +01:00
|
|
|
|
/* Actual processing of the input. This function does not return an
|
2007-08-27 20:10:27 +02:00
|
|
|
|
error code but exits as soon as a match has been found. */
|
|
|
|
|
static void
|
|
|
|
|
process (FILE *fp, pattern_t *patarray)
|
|
|
|
|
{
|
|
|
|
|
char buffer[2048];
|
|
|
|
|
size_t idx;
|
|
|
|
|
int c;
|
|
|
|
|
unsigned long lineno = 0;
|
|
|
|
|
pattern_t *pat;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
int last_is_accept;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
idx = 0;
|
|
|
|
|
c = 0;
|
|
|
|
|
while (idx < sizeof buffer -1 && c != EOF )
|
|
|
|
|
{
|
|
|
|
|
if ((c = getc (fp)) != EOF)
|
|
|
|
|
buffer[idx] = c;
|
|
|
|
|
if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
|
|
|
|
|
{
|
|
|
|
|
lineno++;
|
|
|
|
|
if (!opt.null)
|
|
|
|
|
{
|
|
|
|
|
while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
|
|
|
|
|
idx--;
|
|
|
|
|
}
|
|
|
|
|
buffer[idx] = 0;
|
|
|
|
|
pat = match_p (buffer, patarray);
|
|
|
|
|
if (pat)
|
|
|
|
|
{
|
2021-07-29 11:25:06 +02:00
|
|
|
|
/* Note that the accept mode works correctly only with
|
|
|
|
|
* one input line. */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (opt.verbose)
|
2021-07-29 11:25:06 +02:00
|
|
|
|
log_info ("input line %lu matches pattern at line %u"
|
|
|
|
|
" - %s\n",
|
|
|
|
|
lineno, pat->lineno,
|
|
|
|
|
pat->accept? "accepted":"rejected");
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
idx = 0;
|
2021-07-29 11:25:06 +02:00
|
|
|
|
wipememory (buffer, sizeof buffer);
|
|
|
|
|
if (pat)
|
|
|
|
|
{
|
|
|
|
|
if (pat->accept)
|
|
|
|
|
exit (0);
|
|
|
|
|
else
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
2021-07-29 11:25:06 +02:00
|
|
|
|
wipememory (buffer, sizeof buffer);
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (c != EOF)
|
|
|
|
|
{
|
|
|
|
|
log_error ("input line %lu too long - rejected\n", lineno+1);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
if (ferror (fp))
|
|
|
|
|
{
|
|
|
|
|
log_error ("input read error at line %lu: %s - rejected\n",
|
|
|
|
|
lineno+1, strerror (errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
2021-07-29 11:25:06 +02:00
|
|
|
|
|
|
|
|
|
/* Check last pattern to see whether we are in accept mode. */
|
|
|
|
|
last_is_accept = 0;
|
|
|
|
|
for (pat = patarray; pat->type != PAT_NULL; pat++)
|
|
|
|
|
last_is_accept = pat->accept;
|
|
|
|
|
|
2007-08-27 20:10:27 +02:00
|
|
|
|
if (opt.verbose)
|
2021-07-29 11:25:06 +02:00
|
|
|
|
log_info ("no input line matches the pattern - %s\n",
|
|
|
|
|
last_is_accept? "rejected":"accepted");
|
|
|
|
|
|
|
|
|
|
if (log_get_errorcount(0))
|
|
|
|
|
exit (2); /* Ooops - reject. */
|
|
|
|
|
else if (last_is_accept)
|
|
|
|
|
exit (1); /* Reject */
|
|
|
|
|
else
|
|
|
|
|
exit (0); /* Accept */
|
2007-08-27 20:10:27 +02:00
|
|
|
|
}
|