tftpd: handle rule filter flags more cleanly

Instead of a bunch of ad hoc tests, keep a bitmask of flags that would
keep this rule from being executed. This also removes the ugly hack of
converting the request mode between opcode and character encodings for
really no good reason.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin 2024-05-29 17:28:53 -07:00
parent 6f96fcd1b6
commit 351907e3f0
3 changed files with 22 additions and 21 deletions

View file

@ -35,12 +35,13 @@
#define RULE_IPV6 0x80 /* IPv6 only */ #define RULE_IPV6 0x80 /* IPv6 only */
#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ #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 */
struct rule { struct rule {
struct rule *next; struct rule *next;
int nrule; int nrule;
int rule_flags; unsigned int rule_flags;
char rule_mode;
regex_t rx; regex_t rx;
const char *pattern; const char *pattern;
}; };
@ -237,8 +238,10 @@ static int parseline(char *line, struct rule *r, int lineno)
r->rule_flags |= RULE_IPV6; r->rule_flags |= RULE_IPV6;
break; break;
case 'G': case 'G':
r->rule_flags |= RULE_RRQ;
break;
case 'P': case 'P':
r->rule_mode = *p; r->rule_flags |= RULE_WRQ;
break; break;
default: default:
syslog(LOG_ERR, syslog(LOG_ERR,
@ -346,7 +349,7 @@ void freerules(struct rule *r)
/* Execute a rule set on a string; returns a malloc'd new string. */ /* Execute a rule set on a string; returns a malloc'd new string. */
char *rewrite_string(const struct formats *pf, char *rewrite_string(const struct formats *pf,
const char *input, const struct rule *rules, const char *input, const struct rule *rules,
char mode, int af, match_pattern_callback macrosub, int mode, int af, match_pattern_callback macrosub,
const char **errmsg) const char **errmsg)
{ {
char *current = tfstrdup(input); char *current = tfstrdup(input);
@ -357,6 +360,7 @@ char *rewrite_string(const struct formats *pf,
int len; int len;
int was_match = 0; int was_match = 0;
int deadman = DEADMAN_MAX_STEPS; int deadman = DEADMAN_MAX_STEPS;
unsigned int bad_flags;
/* Default error */ /* Default error */
*errmsg = "Remap table failure"; *errmsg = "Remap table failure";
@ -365,15 +369,15 @@ char *rewrite_string(const struct formats *pf,
syslog(LOG_INFO, "remap: input: %s", current); syslog(LOG_INFO, "remap: input: %s", current);
} }
bad_flags = 0;
if (mode != RRQ) bad_flags |= RULE_RRQ;
if (mode != WRQ) bad_flags |= RULE_WRQ;
if (af != AF_INET) bad_flags |= RULE_IPV4;
if (af != AF_INET6) bad_flags |= RULE_IPV6;
for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) {
if (ruleptr->rule_mode && ruleptr->rule_mode != mode) if (ruleptr->rule_flags & bad_flags)
continue; /* Rule not applicable, try next */ continue; /* This rule is excluded by flags */
if ((ruleptr->rule_flags & RULE_IPV4) && (af != AF_INET))
continue; /* Rule not applicable, try next */
if ((ruleptr->rule_flags & RULE_IPV6) && (af != AF_INET6))
continue; /* Rule not applicable, try next */
if (!deadman--) { if (!deadman--) {
syslog(LOG_WARNING, syslog(LOG_WARNING,
@ -424,8 +428,7 @@ char *rewrite_string(const struct formats *pf,
genmatchstring(newstr, ruleptr->pattern, current, genmatchstring(newstr, ruleptr->pattern, current,
pmatch, macrosub); pmatch, macrosub);
if ((ruleptr->rule_flags & RULE_HASFILE) && if ((ruleptr->rule_flags & RULE_HASFILE) &&
pf->f_validate(newstr, mode == 'G' ? RRQ : WRQ, pf->f_validate(newstr, mode, pf, &accerr)) {
pf, &accerr)) {
if (verbosity >= 3) { if (verbosity >= 3) {
syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s",
ruleptr->nrule, accerr, newstr); ruleptr->nrule, accerr, newstr);
@ -441,8 +444,7 @@ char *rewrite_string(const struct formats *pf,
ruleptr->nrule, current); ruleptr->nrule, current);
} }
} else if (ruleptr->rule_flags & RULE_HASFILE) { } else if (ruleptr->rule_flags & RULE_HASFILE) {
if (pf->f_validate(current, mode == 'G' ? RRQ : WRQ, if (pf->f_validate(current, mode, pf, &accerr)) {
pf, &accerr)) {
if (verbosity >= 3) { if (verbosity >= 3) {
syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n",
ruleptr->nrule, accerr); ruleptr->nrule, accerr);

View file

@ -37,7 +37,7 @@ void freerules(struct rule *);
/* Execute a rule set on a string; returns a malloc'd new string. */ /* Execute a rule set on a string; returns a malloc'd new string. */
struct formats; struct formats;
char *rewrite_string(const struct formats *, const char *, char *rewrite_string(const struct formats *, const char *,
const struct rule *, char, int, const struct rule *, int, int,
match_pattern_callback, const char **); match_pattern_callback, const char **);
#endif /* WITH_REGEX */ #endif /* WITH_REGEX */

View file

@ -1417,8 +1417,7 @@ static char *rewrite_access(const struct formats *pf, char *filename,
{ {
if (rewrite_rules) { if (rewrite_rules) {
char *newname = char *newname =
rewrite_string(pf, filename, rewrite_rules, rewrite_string(pf, filename, rewrite_rules, mode, af,
mode != RRQ ? 'P' : 'G', af,
rewrite_macros, msg); rewrite_macros, msg);
filename = newname; filename = newname;
} }