mirror of
https://kernel.googlesource.com/pub/scm/network/tftp/tftp-hpa
synced 2025-04-26 01:49:52 +03:00
tftpd: allow a rewrite rule to probe for the existence of a file
This adds an "E" flag to the rewrite rules, which exits rule processing if and only if the result is a valid, accessible filename. If combined with "r", the rewrite is cancelled if the rule is not applied. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
parent
1dc6d55811
commit
6f96fcd1b6
5 changed files with 103 additions and 47 deletions
|
@ -1,6 +1,6 @@
|
|||
/* ----------------------------------------------------------------------- *
|
||||
*
|
||||
* Copyright 2001-2014 H. Peter Anvin - All Rights Reserved
|
||||
* Copyright 2001-2024 H. Peter Anvin - All Rights Reserved
|
||||
*
|
||||
* This program is free software available under the same license
|
||||
* as the "OpenBSD" operating system, distributed at
|
||||
|
@ -34,6 +34,8 @@
|
|||
#define RULE_IPV4 0x40 /* IPv4 only */
|
||||
#define RULE_IPV6 0x80 /* IPv6 only */
|
||||
|
||||
#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */
|
||||
|
||||
struct rule {
|
||||
struct rule *next;
|
||||
int nrule;
|
||||
|
@ -213,6 +215,9 @@ static int parseline(char *line, struct rule *r, int lineno)
|
|||
case 'e':
|
||||
r->rule_flags |= RULE_EXIT;
|
||||
break;
|
||||
case 'E':
|
||||
r->rule_flags |= RULE_HASFILE;
|
||||
break;
|
||||
case 's':
|
||||
r->rule_flags |= RULE_RESTART;
|
||||
break;
|
||||
|
@ -244,15 +249,21 @@ static int parseline(char *line, struct rule *r, int lineno)
|
|||
}
|
||||
}
|
||||
|
||||
/* RULE_GLOBAL only applies when RULE_REWRITE specified */
|
||||
if (!(r->rule_flags & RULE_REWRITE))
|
||||
r->rule_flags &= ~RULE_GLOBAL;
|
||||
|
||||
if ((r->rule_flags & (RULE_INVERSE | RULE_REWRITE)) ==
|
||||
(RULE_INVERSE | RULE_REWRITE)) {
|
||||
syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n",
|
||||
lineno, line);
|
||||
return -1; /* Error */
|
||||
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 */
|
||||
}
|
||||
} else {
|
||||
/* RULE_GLOBAL is meaningless without RULE_REWRITE */
|
||||
r->rule_flags &= ~RULE_GLOBAL;
|
||||
}
|
||||
|
||||
/* Read and compile the regex */
|
||||
|
@ -333,12 +344,14 @@ void freerules(struct rule *r)
|
|||
}
|
||||
|
||||
/* Execute a rule set on a string; returns a malloc'd new string. */
|
||||
char *rewrite_string(const char *input, const struct rule *rules,
|
||||
char *rewrite_string(const struct formats *pf,
|
||||
const char *input, const struct rule *rules,
|
||||
char mode, int af, match_pattern_callback macrosub,
|
||||
const char **errmsg)
|
||||
{
|
||||
char *current = tfstrdup(input);
|
||||
char *newstr;
|
||||
const char *accerr;
|
||||
const struct rule *ruleptr = rules;
|
||||
regmatch_t pmatch[10];
|
||||
int len;
|
||||
|
@ -410,13 +423,34 @@ char *rewrite_string(const char *input, const struct rule *rules,
|
|||
newstr = tfmalloc(len + 1);
|
||||
genmatchstring(newstr, ruleptr->pattern, current,
|
||||
pmatch, macrosub);
|
||||
free(current);
|
||||
current = newstr;
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: rewrite: %s",
|
||||
ruleptr->nrule, current);
|
||||
}
|
||||
}
|
||||
if ((ruleptr->rule_flags & RULE_HASFILE) &&
|
||||
pf->f_validate(newstr, mode == 'G' ? RRQ : WRQ,
|
||||
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 == 'G' ? RRQ : WRQ,
|
||||
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 */
|
||||
}
|
||||
|
@ -426,7 +460,7 @@ char *rewrite_string(const char *input, const struct rule *rules,
|
|||
if (was_match) {
|
||||
was_match = 0;
|
||||
|
||||
if (ruleptr->rule_flags & RULE_EXIT) {
|
||||
if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) {
|
||||
if (verbosity >= 3) {
|
||||
syslog(LOG_INFO, "remap: rule %d: exit",
|
||||
ruleptr->nrule);
|
||||
|
|
|
@ -35,7 +35,9 @@ struct rule *parserulefile(FILE *);
|
|||
void freerules(struct rule *);
|
||||
|
||||
/* Execute a rule set on a string; returns a malloc'd new string. */
|
||||
char *rewrite_string(const char *, const struct rule *, char, int,
|
||||
struct formats;
|
||||
char *rewrite_string(const struct formats *, const char *,
|
||||
const struct rule *, char, int,
|
||||
match_pattern_callback, const char **);
|
||||
|
||||
#endif /* WITH_REGEX */
|
||||
|
|
|
@ -281,6 +281,15 @@ case-insensitively. By default it is case sensitive.
|
|||
.B e
|
||||
If this rule matches, end rule processing after executing the rule.
|
||||
.TP
|
||||
.B E
|
||||
If this rule matches,
|
||||
\fIand the result matches a filename that can be transferred\fP,
|
||||
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 .
|
||||
.TP
|
||||
.B s
|
||||
If this rule matches, start rule processing over from the very first
|
||||
rule after executing this rule.
|
||||
|
@ -307,10 +316,10 @@ Inverse the sense of this rule, i.e. execute the
|
|||
only if the
|
||||
.I regex
|
||||
.I doesn't
|
||||
match. Cannot used together with
|
||||
match. Cannot used together with
|
||||
.BR r .
|
||||
.PP
|
||||
The following escape sequences are recognized as part of the
|
||||
The following escape sequences are recognized as part of a
|
||||
.IR "replacement pattern" :
|
||||
.TP
|
||||
\fB\\0\fP
|
||||
|
|
|
@ -106,11 +106,12 @@ int portrange = 0;
|
|||
unsigned int portrange_from, portrange_to;
|
||||
int verbosity = 0;
|
||||
|
||||
struct formats;
|
||||
#ifdef WITH_REGEX
|
||||
static struct rule *rewrite_rules = NULL;
|
||||
#endif
|
||||
|
||||
static FILE *file;
|
||||
|
||||
int tftp(struct tftphdr *, int);
|
||||
static void nak(int, const char *);
|
||||
static void timer(int);
|
||||
|
@ -162,14 +163,14 @@ static void timer(int sig)
|
|||
}
|
||||
|
||||
#ifdef WITH_REGEX
|
||||
static struct rule *read_remap_rules(const char *file)
|
||||
static struct rule *read_remap_rules(const char *rulefile)
|
||||
{
|
||||
FILE *f;
|
||||
struct rule *rulep;
|
||||
|
||||
f = fopen(file, "rt");
|
||||
f = fopen(rulefile, "rt");
|
||||
if (!f) {
|
||||
syslog(LOG_ERR, "Cannot open map file: %s: %m", file);
|
||||
syslog(LOG_ERR, "Cannot open map file: %s: %m", rulefile);
|
||||
exit(EX_NOINPUT);
|
||||
}
|
||||
rulep = parserulefile(f);
|
||||
|
@ -1057,19 +1058,12 @@ int main(int argc, char **argv)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
static char *rewrite_access(char *, int, int, const char **);
|
||||
static char *rewrite_access(const struct formats *,
|
||||
char *, int, int, const char **);
|
||||
static int validate_access(char *, int, const struct formats *, const char **);
|
||||
static void tftp_sendfile(const struct formats *, struct tftphdr *, int);
|
||||
static void tftp_recvfile(const struct formats *, struct tftphdr *, int);
|
||||
|
||||
struct formats {
|
||||
const char *f_mode;
|
||||
char *(*f_rewrite) (char *, int, int, const char **);
|
||||
int (*f_validate) (char *, int, const struct formats *, const char **);
|
||||
void (*f_send) (const struct formats *, struct tftphdr *, int);
|
||||
void (*f_recv) (const struct formats *, struct tftphdr *, int);
|
||||
int f_convert;
|
||||
};
|
||||
static const struct formats formats[] = {
|
||||
{
|
||||
"netascii", rewrite_access, validate_access, tftp_sendfile,
|
||||
|
@ -1126,8 +1120,9 @@ int tftp(struct tftphdr *tp, int size)
|
|||
nak(EBADOP, "Unknown mode");
|
||||
exit(0);
|
||||
}
|
||||
file = NULL;
|
||||
if (!(filename = (*pf->f_rewrite)
|
||||
(origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) {
|
||||
(pf, origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) {
|
||||
nak(EACCESS, errmsgptr); /* File denied by mapping rule */
|
||||
exit(0);
|
||||
}
|
||||
|
@ -1150,12 +1145,18 @@ int tftp(struct tftphdr *tp, int size)
|
|||
tmp_p, origfilename,
|
||||
filename);
|
||||
}
|
||||
ecode =
|
||||
(*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr);
|
||||
if (ecode) {
|
||||
nak(ecode, errmsgptr);
|
||||
exit(0);
|
||||
}
|
||||
/*
|
||||
* If "file" is already set, then a file was already validated
|
||||
* and opened during remap processing.
|
||||
*/
|
||||
if (!file) {
|
||||
ecode =
|
||||
(*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr);
|
||||
if (ecode) {
|
||||
nak(ecode, errmsgptr);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
opt = ++cp;
|
||||
} else if (argn & 1) {
|
||||
val = ++cp;
|
||||
|
@ -1411,12 +1412,12 @@ static int rewrite_macros(char macro, char *output)
|
|||
/*
|
||||
* Modify the filename, if applicable. If it returns NULL, deny the access.
|
||||
*/
|
||||
static char *rewrite_access(char *filename, int mode, int af,
|
||||
const char **msg)
|
||||
static char *rewrite_access(const struct formats *pf, char *filename,
|
||||
int mode, int af, const char **msg)
|
||||
{
|
||||
if (rewrite_rules) {
|
||||
char *newname =
|
||||
rewrite_string(filename, rewrite_rules,
|
||||
rewrite_string(pf, filename, rewrite_rules,
|
||||
mode != RRQ ? 'P' : 'G', af,
|
||||
rewrite_macros, msg);
|
||||
filename = newname;
|
||||
|
@ -1425,8 +1426,10 @@ static char *rewrite_access(char *filename, int mode, int af,
|
|||
}
|
||||
|
||||
#else
|
||||
static char *rewrite_access(char *filename, int mode, int af, const char **msg)
|
||||
static char *rewrite_access(const struct formats *pf, char *filename,
|
||||
int mode, int af, const char **msg)
|
||||
{
|
||||
(void)pf;
|
||||
(void)mode; /* Avoid warning */
|
||||
(void)msg;
|
||||
(void)af;
|
||||
|
@ -1434,7 +1437,6 @@ static char *rewrite_access(char *filename, int mode, int af, const char **msg)
|
|||
}
|
||||
#endif
|
||||
|
||||
static FILE *file;
|
||||
/*
|
||||
* Validate file access. Since we
|
||||
* have no uid or gid, for now require
|
||||
|
|
|
@ -23,4 +23,13 @@ char *tfstrdup(const char *);
|
|||
|
||||
extern int verbosity;
|
||||
|
||||
struct formats {
|
||||
const char *f_mode;
|
||||
char *(*f_rewrite) (const struct formats *, char *, int, int, const char **);
|
||||
int (*f_validate) (char *, int, const struct formats *, const char **);
|
||||
void (*f_send) (const struct formats *, struct tftphdr *, int);
|
||||
void (*f_recv) (const struct formats *, struct tftphdr *, int);
|
||||
int f_convert;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue