mirror of
https://kernel.googlesource.com/pub/scm/network/tftp/tftp-hpa
synced 2025-04-26 01:49:52 +03:00
remap: fix timeouts for "g", add a "gg" flag to match sed s///g
Make sure that when using the global option, we still bump the deadman timer. The "g" option really should only have applied to the right-hand unmatched part of the string, like in sed. Add a "gg" option which does that. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
parent
74c5d8a020
commit
ac7f98e4d8
2 changed files with 189 additions and 119 deletions
287
tftpd/remap.c
287
tftpd/remap.c
|
@ -22,7 +22,7 @@
|
|||
#include "tftpd.h"
|
||||
#include "remap.h"
|
||||
|
||||
#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */
|
||||
#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */
|
||||
#define MAXLINE 16384 /* Truncate a line at this many bytes */
|
||||
|
||||
#define RULE_REWRITE 0x01 /* This is a rewrite rule */
|
||||
|
@ -37,6 +37,7 @@
|
|||
#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */
|
||||
#define RULE_RRQ 0x200 /* Get (read) only */
|
||||
#define RULE_WRQ 0x400 /* Put (write) only */
|
||||
#define RULE_SEDG 0x800 /* sed-style global */
|
||||
|
||||
struct rule {
|
||||
struct rule *next;
|
||||
|
@ -61,22 +62,30 @@ static int xform_tolower(int c)
|
|||
return tolower(c);
|
||||
}
|
||||
|
||||
/* Do \-substitution. Call with string == NULL to get length only. */
|
||||
static int genmatchstring(char *string, const char *pattern,
|
||||
const char *input, const regmatch_t * pmatch,
|
||||
match_pattern_callback macrosub)
|
||||
/*
|
||||
* Do \-substitution. Call with string == NULL to get length only.
|
||||
* "start" indicates an offset into the input buffer where the pattern
|
||||
* match was started.
|
||||
*/
|
||||
static int do_genmatchstring(char *string, const char *pattern,
|
||||
const char *ibuf,
|
||||
const regmatch_t * pmatch,
|
||||
match_pattern_callback macrosub,
|
||||
int start, int *nextp)
|
||||
{
|
||||
int (*xform) (int) = xform_null;
|
||||
int len = 0;
|
||||
int n, mlen, sublen;
|
||||
int endbytes;
|
||||
const char *input = ibuf + start;
|
||||
|
||||
/* Get section before match; note pmatch[0] is the whole match */
|
||||
endbytes = strlen(input) - pmatch[0].rm_eo;
|
||||
len = pmatch[0].rm_so + endbytes;
|
||||
len = start + pmatch[0].rm_so;
|
||||
if (string) {
|
||||
memcpy(string, input, pmatch[0].rm_so);
|
||||
string += pmatch[0].rm_so;
|
||||
/* Copy the prefix before "start" as well! */
|
||||
memcpy(string, ibuf, start + pmatch[0].rm_so);
|
||||
string += start + pmatch[0].rm_so;
|
||||
}
|
||||
|
||||
/* Transform matched section */
|
||||
|
@ -102,7 +111,7 @@ static int genmatchstring(char *string, const char *pattern,
|
|||
mlen = pmatch[n].rm_eo - pmatch[n].rm_so;
|
||||
len += mlen;
|
||||
if (string) {
|
||||
const char *p = input + pmatch[n].rm_so;
|
||||
const char *p = input + start + pmatch[n].rm_so;
|
||||
while (mlen--)
|
||||
*string++ = xform(*p++);
|
||||
}
|
||||
|
@ -145,7 +154,12 @@ static int genmatchstring(char *string, const char *pattern,
|
|||
}
|
||||
}
|
||||
|
||||
/* Pointer to post-substitution tail */
|
||||
if (nextp)
|
||||
*nextp = len;
|
||||
|
||||
/* Copy section after match */
|
||||
len += endbytes;
|
||||
if (string) {
|
||||
memcpy(string, input + pmatch[0].rm_eo, endbytes);
|
||||
string[endbytes] = '\0';
|
||||
|
@ -154,6 +168,26 @@ static int genmatchstring(char *string, const char *pattern,
|
|||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ditto, but allocate the string in a new buffer
|
||||
*/
|
||||
|
||||
static int genmatchstring(char **string, const char *pattern,
|
||||
const char *ibuf,
|
||||
const regmatch_t * pmatch,
|
||||
match_pattern_callback macrosub,
|
||||
int start, int *nextp)
|
||||
{
|
||||
int len;
|
||||
char *buf;
|
||||
|
||||
len = do_genmatchstring(NULL, pattern, ibuf, pmatch,
|
||||
macrosub, start, NULL);
|
||||
buf = tf_malloc(len + 1);
|
||||
return do_genmatchstring(buf, pattern, ibuf, pmatch,
|
||||
macrosub, start, nextp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a string terminated by non-escaped whitespace; ignoring
|
||||
* leading whitespace. Consider an unescaped # to be a comment marker,
|
||||
|
@ -165,7 +199,7 @@ static int readescstring(char *buf, char **str)
|
|||
int wasbs = 0, len = 0;
|
||||
|
||||
while (*p && isspace(*p))
|
||||
p++;
|
||||
p++;
|
||||
|
||||
if (!*p) {
|
||||
*buf = '\0';
|
||||
|
@ -211,14 +245,17 @@ static int parseline(char *line, struct rule *r, int lineno)
|
|||
r->rule_flags |= RULE_REWRITE;
|
||||
break;
|
||||
case 'g':
|
||||
r->rule_flags |= RULE_GLOBAL;
|
||||
if (r->rule_flags & RULE_GLOBAL)
|
||||
r->rule_flags |= RULE_SEDG;
|
||||
else
|
||||
r->rule_flags |= RULE_GLOBAL;
|
||||
break;
|
||||
case 'e':
|
||||
r->rule_flags |= RULE_EXIT;
|
||||
break;
|
||||
case 'E':
|
||||
r->rule_flags |= RULE_HASFILE;
|
||||
break;
|
||||
case 'E':
|
||||
r->rule_flags |= RULE_HASFILE;
|
||||
break;
|
||||
case 's':
|
||||
r->rule_flags |= RULE_RESTART;
|
||||
break;
|
||||
|
@ -231,18 +268,18 @@ static int parseline(char *line, struct rule *r, int lineno)
|
|||
case '~':
|
||||
r->rule_flags |= RULE_INVERSE;
|
||||
break;
|
||||
case '4':
|
||||
r->rule_flags |= RULE_IPV4;
|
||||
break;
|
||||
case '6':
|
||||
r->rule_flags |= RULE_IPV6;
|
||||
break;
|
||||
case 'G':
|
||||
r->rule_flags |= RULE_RRQ;
|
||||
break;
|
||||
case 'P':
|
||||
r->rule_flags |= RULE_WRQ;
|
||||
break;
|
||||
case '4':
|
||||
r->rule_flags |= RULE_IPV4;
|
||||
break;
|
||||
case '6':
|
||||
r->rule_flags |= RULE_IPV6;
|
||||
break;
|
||||
case 'G':
|
||||
r->rule_flags |= RULE_RRQ;
|
||||
break;
|
||||
case 'P':
|
||||
r->rule_flags |= RULE_WRQ;
|
||||
break;
|
||||
default:
|
||||
syslog(LOG_ERR,
|
||||
"Remap command \"%s\" on line %d contains invalid char \"%c\"",
|
||||
|
@ -253,20 +290,20 @@ static int parseline(char *line, struct rule *r, int lineno)
|
|||
}
|
||||
|
||||
if (r->rule_flags & RULE_REWRITE) {
|
||||
if (r->rule_flags & RULE_INVERSE) {
|
||||
syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n",
|
||||
lineno, line);
|
||||
return -1; /* Error */
|
||||
}
|
||||
if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE))
|
||||
== (RULE_GLOBAL|RULE_HASFILE)) {
|
||||
syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n",
|
||||
lineno, line);
|
||||
return -1; /* Error */
|
||||
}
|
||||
if (r->rule_flags & RULE_INVERSE) {
|
||||
syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n",
|
||||
lineno, line);
|
||||
return -1; /* Error */
|
||||
}
|
||||
if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE))
|
||||
== (RULE_GLOBAL|RULE_HASFILE)) {
|
||||
syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n",
|
||||
lineno, line);
|
||||
return -1; /* Error */
|
||||
}
|
||||
} else {
|
||||
/* RULE_GLOBAL is meaningless without RULE_REWRITE */
|
||||
r->rule_flags &= ~RULE_GLOBAL;
|
||||
/* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */
|
||||
r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG);
|
||||
}
|
||||
|
||||
/* Read and compile the regex */
|
||||
|
@ -348,19 +385,23 @@ void freerules(struct rule *r)
|
|||
|
||||
/* Execute a rule set on a string; returns a malloc'd new string. */
|
||||
char *rewrite_string(const struct formats *pf,
|
||||
const char *input, const struct rule *rules,
|
||||
const char *input, const struct rule *rules,
|
||||
int mode, int af, match_pattern_callback macrosub,
|
||||
const char **errmsg)
|
||||
{
|
||||
char *current = tfstrdup(input);
|
||||
char *newstr;
|
||||
char *newstr, *newerstr;
|
||||
const char *accerr;
|
||||
const struct rule *ruleptr = rules;
|
||||
regmatch_t pmatch[10];
|
||||
int len;
|
||||
int i;
|
||||
int len, newlen;
|
||||
int was_match = 0;
|
||||
int deadman = DEADMAN_MAX_STEPS;
|
||||
int matchsense;
|
||||
int pmatches;
|
||||
unsigned int bad_flags;
|
||||
int ggoffset;
|
||||
|
||||
/* Default error */
|
||||
*errmsg = "Remap table failure";
|
||||
|
@ -376,30 +417,25 @@ char *rewrite_string(const struct formats *pf,
|
|||
if (af != AF_INET6) bad_flags |= RULE_IPV6;
|
||||
|
||||
for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) {
|
||||
if (ruleptr->rule_flags & bad_flags)
|
||||
continue; /* This rule is excluded by flags */
|
||||
if (ruleptr->rule_flags & bad_flags)
|
||||
continue; /* This rule is excluded by flags */
|
||||
|
||||
if (!deadman--) {
|
||||
syslog(LOG_WARNING,
|
||||
"remap: Breaking loop, input = %s, last = %s", input,
|
||||
current);
|
||||
free(current);
|
||||
return NULL; /* Did not terminate! */
|
||||
}
|
||||
matchsense = ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0;
|
||||
pmatches = ruleptr->rule_flags & RULE_INVERSE ? 0 : 10;
|
||||
|
||||
/* Clear the pmatch[] array */
|
||||
for (i = 0; i < 10; i++)
|
||||
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
|
||||
|
||||
do {
|
||||
if (regexec(&ruleptr->rx, current, 10, pmatch, 0) ==
|
||||
(ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) {
|
||||
if (!deadman--)
|
||||
goto dead;
|
||||
|
||||
if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0)
|
||||
== matchsense) {
|
||||
/* Match on this rule */
|
||||
was_match = 1;
|
||||
|
||||
if (ruleptr->rule_flags & RULE_INVERSE) {
|
||||
/* No actual match, so clear out the pmatch array */
|
||||
int i;
|
||||
for (i = 0; i < 10; i++)
|
||||
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
|
||||
}
|
||||
|
||||
if (ruleptr->rule_flags & RULE_ABORT) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: abort: %s",
|
||||
|
@ -407,12 +443,8 @@ char *rewrite_string(const struct formats *pf,
|
|||
}
|
||||
if (ruleptr->pattern[0]) {
|
||||
/* Custom error message */
|
||||
len =
|
||||
genmatchstring(NULL, ruleptr->pattern, current,
|
||||
pmatch, macrosub);
|
||||
newstr = tfmalloc(len + 1);
|
||||
genmatchstring(newstr, ruleptr->pattern, current,
|
||||
pmatch, macrosub);
|
||||
genmatchstring(&newstr, ruleptr->pattern, current,
|
||||
pmatch, macrosub, 0, NULL);
|
||||
*errmsg = newstr;
|
||||
} else {
|
||||
*errmsg = NULL;
|
||||
|
@ -422,58 +454,76 @@ char *rewrite_string(const struct formats *pf,
|
|||
}
|
||||
|
||||
if (ruleptr->rule_flags & RULE_REWRITE) {
|
||||
len = genmatchstring(NULL, ruleptr->pattern, current,
|
||||
pmatch, macrosub);
|
||||
newstr = tfmalloc(len + 1);
|
||||
genmatchstring(newstr, ruleptr->pattern, current,
|
||||
pmatch, macrosub);
|
||||
if ((ruleptr->rule_flags & RULE_HASFILE) &&
|
||||
pf->f_validate(newstr, mode, pf, &accerr)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s",
|
||||
ruleptr->nrule, accerr, newstr);
|
||||
}
|
||||
free(newstr);
|
||||
was_match = 0;
|
||||
break;
|
||||
}
|
||||
free(current);
|
||||
current = newstr;
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: rewrite: %s",
|
||||
ruleptr->nrule, current);
|
||||
}
|
||||
} else if (ruleptr->rule_flags & RULE_HASFILE) {
|
||||
if (pf->f_validate(current, mode, pf, &accerr)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n",
|
||||
ruleptr->nrule, accerr);
|
||||
}
|
||||
was_match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break; /* No match, terminate unconditionally */
|
||||
len = genmatchstring(&newstr, ruleptr->pattern, current,
|
||||
pmatch, macrosub, 0, &ggoffset);
|
||||
|
||||
if (ruleptr->rule_flags & RULE_SEDG) {
|
||||
/* sed-style partial-matching global */
|
||||
while (ggoffset < len &&
|
||||
regexec(ruleptr->rx, newstr + ggoffset,
|
||||
pmatches, pmatch,
|
||||
ggoffset ? REG_NOTBOL : 0)
|
||||
== matchsense) {
|
||||
if (!deadman--) {
|
||||
free(current);
|
||||
current = newstr;
|
||||
goto dead;
|
||||
}
|
||||
len = genmatchstring(&newerstr, ruleptr->pattern,
|
||||
newstr, pmatch, macrosub,
|
||||
ggoffset, &ggoffset);
|
||||
free(newstr);
|
||||
newstr = newerstr;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ruleptr->rule_flags & RULE_HASFILE) &&
|
||||
pf->f_validate(newstr, mode, pf, &accerr)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s",
|
||||
ruleptr->nrule, accerr, newstr);
|
||||
}
|
||||
free(newstr);
|
||||
was_match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(current);
|
||||
current = newstr;
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: rewrite: %s",
|
||||
ruleptr->nrule, current);
|
||||
}
|
||||
} else if (ruleptr->rule_flags & RULE_HASFILE) {
|
||||
if (pf->f_validate(current, mode, pf, &accerr)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n",
|
||||
ruleptr->nrule, accerr);
|
||||
}
|
||||
was_match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If the rule is global, keep going until no match */
|
||||
} while (ruleptr->rule_flags & RULE_GLOBAL);
|
||||
} else {
|
||||
break; /* No match, terminate unconditionally */
|
||||
}
|
||||
/* If the rule is (old-style) global, keep going until no match */
|
||||
} while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL);
|
||||
|
||||
if (was_match) {
|
||||
was_match = 0;
|
||||
if (was_match) {
|
||||
was_match = 0;
|
||||
|
||||
if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: exit",
|
||||
ruleptr->nrule);
|
||||
}
|
||||
return current; /* Exit here, we're done */
|
||||
} else if (ruleptr->rule_flags & RULE_RESTART) {
|
||||
ruleptr = rules; /* Start from the top */
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: restart",
|
||||
ruleptr->nrule);
|
||||
}
|
||||
if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: exit",
|
||||
ruleptr->nrule);
|
||||
}
|
||||
return current; /* Exit here, we're done */
|
||||
} else if (ruleptr->rule_flags & RULE_RESTART) {
|
||||
ruleptr = rules; /* Start from the top */
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: restart",
|
||||
ruleptr->nrule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -482,4 +532,11 @@ char *rewrite_string(const struct formats *pf,
|
|||
syslog(LOG_INFO, "remap: done");
|
||||
}
|
||||
return current;
|
||||
|
||||
dead: /* Deadman expired */
|
||||
syslog(LOG_WARNING,
|
||||
"remap: Breaking loop, input = %s, last = %s", input,
|
||||
current);
|
||||
free(current);
|
||||
return NULL; /* Did not terminate! */
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.\" -*- nroff -*- --------------------------------------------------------- *
|
||||
.\"
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993, 1994
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
.\"
|
||||
|
@ -270,7 +270,18 @@ by the
|
|||
The replacement pattern may contain escape sequences; see below.
|
||||
.TP
|
||||
.B g
|
||||
Repeat this rule until it no longer matches. This is always used with
|
||||
Repeat this rule until it no longer matches. This is always used with
|
||||
.BR r .
|
||||
.TP
|
||||
.B gg
|
||||
Repeat this rule until it no longer matches, but only on the portion
|
||||
of the string that has not yet been matched, similar to how the
|
||||
.B s
|
||||
command with the
|
||||
.B g
|
||||
option works in
|
||||
.BR sed (1).
|
||||
This is always used with
|
||||
.BR r .
|
||||
.TP
|
||||
.B i
|
||||
|
@ -288,7 +299,9 @@ end rule processing after executing the rule. If this is combined with
|
|||
.BR r ,
|
||||
then if the substitution does \fInot\fP result in a valid filename,
|
||||
the substitution is undone. This cannot be combined with
|
||||
.BR g .
|
||||
.BR g ,
|
||||
but \fIcan\fP be combined with
|
||||
.BR gg .
|
||||
.TP
|
||||
.B s
|
||||
If this rule matches, start rule processing over from the very first
|
||||
|
@ -400,7 +413,7 @@ Access to files can, and should, be restricted by invoking
|
|||
.B tftpd
|
||||
with a list of directories by including pathnames as server program
|
||||
arguments on the command line. In this case access is restricted to
|
||||
files whole names are prefixed by one of the given directories. If
|
||||
files whose names are prefixed by one of the given directories. If
|
||||
possible, it is recommended that the
|
||||
.B \-\-secure
|
||||
flag is used to set up a chroot() environment for the server to run in
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue