From 20271a4c874ab47a08fb96d4de82c53b9ba4e418 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 10 Jul 2001 04:52:06 +0000 Subject: [PATCH] Initial step in integrating regex-based rewrite engine. --- MCONFIG.in | 3 + config.h.in | 1 + configure.in | 27 +++-- tftp/tftpsubs.h | 7 ++ tftpd/Makefile | 2 +- tftpd/remap.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++++ tftpd/remap.h | 28 +++++ tftpd/tftpd.c | 3 + 8 files changed, 344 insertions(+), 9 deletions(-) create mode 100644 tftpd/remap.c create mode 100644 tftpd/remap.h diff --git a/MCONFIG.in b/MCONFIG.in index 2cdbbd7..a3f205c 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -32,6 +32,9 @@ LIBS = @LIBS@ # Additional library we need to build LIBOBJS = @LIBOBJS@ +# Additional tftpd objects we need to build +TFTPDOBJS = @TFTPDOBJS@ + # ar and ranlib (for making libraries) AR = ar cq RANLIB = @RANLIB@ diff --git a/config.h.in b/config.h.in index 64b0cb8..a5492af 100644 --- a/config.h.in +++ b/config.h.in @@ -22,3 +22,4 @@ #undef HAVE_STRUCT_IN_PKTINFO #undef HAVE_SETREUID #undef HAVE_SETREGID +#undef WITH_REGEX diff --git a/configure.in b/configure.in index 4537a59..5a2a486 100644 --- a/configure.in +++ b/configure.in @@ -19,10 +19,20 @@ PA_WITH_BOOL(tcpwrappers, 1, [ AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv]) PA_HAVE_TCPWRAPPERS -], +],:) + +PA_WITH_BOOL(remap, 1, +[ --without-remap Disable regex-based filename remapping], [ - : -]) + AC_CHECK_HEADER(regex.h, + [ + AC_SEARCH_LIBS(regcomp, [regex rx], + [ + AC_DEFINE(WITH_REGEX) + TFTPDOBJS="remap.o $TFTPDOBJS" + ]) + ]) +],:) PA_ADD_CFLAGS(-Wall) PA_ADD_CFLAGS(-W) @@ -39,17 +49,18 @@ PA_ADD_CFLAGS(-pipe) PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) -LIBXTRA=0 +LIBXTRA=false -AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=1 LIBOBJS="$LIBOBJS xmalloc.o") -AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=1 LIBOBJS="$LIBOBJS xstrdup.o") -AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=1 LIBOBJS="$LIBOBJS bsdsignal.o") +AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=true LIBOBJS="$LIBOBJS xmalloc.o") +AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=true LIBOBJS="$LIBOBJS xstrdup.o") +AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=true LIBOBJS="$LIBOBJS bsdsignal.o") -if test "$LIBXTRA" -eq 1; then +if $LIBXTRA; then LIBS="../lib/libxtra.a $LIBS" fi AC_SUBST(LIBOBJS) +AC_SUBST(TFTPDOBJS) AC_PROG_RANLIB AC_PROG_INSTALL diff --git a/tftp/tftpsubs.h b/tftp/tftpsubs.h index a2a3a6c..6d2c183 100644 --- a/tftp/tftpsubs.h +++ b/tftp/tftpsubs.h @@ -42,6 +42,8 @@ * Prototypes for read-ahead/write-behind subroutines for tftp user and * server. */ +#include + struct tftphdr *r_init(void); void read_ahead(FILE *, int); int readit(FILE *, struct tftphdr **, int); @@ -55,3 +57,8 @@ int writeit(FILE *, struct tftphdr **, int, int); extern int segsize; #define MAX_SEGSIZE 65464 +/* + * Prototype for xmalloc/xstrdup + */ +extern void *xmalloc(size_t); +extern char *xstrdup(const char *); diff --git a/tftpd/Makefile b/tftpd/Makefile index ea1170f..4ae0dab 100644 --- a/tftpd/Makefile +++ b/tftpd/Makefile @@ -3,7 +3,7 @@ all: tftpd -include ../MCONFIG -include ../MRULES -OBJS = tftpd.o tftpsubs.o recvfrom.o +OBJS = tftpd.o tftpsubs.o recvfrom.o $(TFTPDOBJS) tftpd: $(OBJS) $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ diff --git a/tftpd/remap.c b/tftpd/remap.c new file mode 100644 index 0000000..fe7a753 --- /dev/null +++ b/tftpd/remap.c @@ -0,0 +1,282 @@ +/* $Id$ */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software available under the same license + * as the "OpenBSD" operating system, distributed at + * http://www.openbsd.org/. + * + * ----------------------------------------------------------------------- */ + +/* + * remap.c + * + * Perform regular-expression based filename remapping. + */ + +#include +#include +#include +#include +#include + +#include "tftpsubs.h" +#include "remap.h" + +#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */ +#define LINE_MAX 65536 /* Truncate a line at this many bytes */ + +#define RULE_REWRITE 0x01 /* This is a rewrite rule */ +#define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */ +#define RULE_EXIT 0x04 /* Exit after matching this rule */ +#define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ +#define RULE_ABORT 0x10 /* Terminate processing with an error */ +#define RULE_GETONLY 0x20 /* Applicable to GET only */ +#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ + +struct rule { + struct rule *next; + int nrule; + int rule_flags; + regex_t rx; + char *pattern; +}; + +/* 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) +{ + int len = 0; + int n, mlen; + int endbytes; + + /* Get section before match; note pmatch[0] is the whole match */ + endbytes = strlen(input) - pmatch[0].rm_eo; + len = pmatch[0].rm_so + endbytes; + if ( string ) { + memcpy(string, input, pmatch[0].rm_so); + string += pmatch[0].rm_so; + } + + /* Transform matched section */ + while ( *pattern ) { + if ( *pattern == '\\' && pattern[1] != '\0' ) { + if ( pattern[1] < '0' || pattern[1] > '9' ) { + len++; + if ( string ) + *string++ = pattern[1]; + } else { + n = pattern[1] - '0'; + + if ( pmatch[n].rm_so != -1 ) { + mlen = pmatch[n].rm_eo - pmatch[n].rm_so; + len += mlen; + if ( string ) { + memcpy(string, input+pmatch[n].rm_so, mlen); + string += mlen; + } + } + } + pattern += 2; + } else { + len++; + if ( string ) + *string++ = *pattern; + pattern++; + } + } + + /* Copy section after match */ + if ( string ) { + memcpy(string, input+pmatch[0].rm_eo, endbytes); + string[endbytes] = '\0'; + } + + return len; +} + +/* Extract a string terminated by non-escaped whitespace; ignore leading whitespace */ +/* Consider an unescaped # to be a comment marker, functionally \n */ +static int readescstring(char *buf, char **str) +{ + char *p = *str; + int wasbs = 0, len = 0; + + while ( *p && isspace(*p) ) + p++; + + if ( ! *p ) { + *buf = '\0'; + *str = p; + return 0; + } + + while ( *p ) { + if ( !wasbs && (isspace(*p) || *p == '#') ) { + *buf = '\0'; + *str = p; + return len; + } + /* Important: two backslashes leave us in the !wasbs state! */ + wasbs = !wasbs && ( *p == '\\' ); + *buf++ = *p++; + len++; + } + + *buf = '\0'; + *str = p; + return len; +} + +/* Parse a line into a set of instructions */ +static int parseline(char *line, struct rule *r) +{ + char buffer[LINE_MAX]; + char *p; + int rv; + int rxflags = REG_EXTENDED; + static int nrule; + + memset(r, 0, sizeof r); + r->nrule = nrule; + + if ( !readescstring(buffer, &line) ) + return 0; /* No rule found */ + + for ( p = buffer ; *p ; p++ ) { + switch(*p) { + case 'r': + r->rule_flags |= RULE_REWRITE; + break; + case 'g': + r->rule_flags |= RULE_GLOBAL; + break; + case 'e': + r->rule_flags |= RULE_EXIT; + break; + case 's': + r->rule_flags |= RULE_RESTART; + break; + case 'a': + r->rule_flags |= RULE_ABORT; + break; + case 'i': + rxflags |= REG_ICASE; + break; + case 'G': + r->rule_flags |= RULE_GETONLY; + break; + case 'P': + r->rule_flags |= RULE_PUTONLY; + break; + default: + /* boo hoo */ + break; + } + } + + /* RULE_GLOBAL only applies when RULE_REWRITE specified */ + if ( !(r->rule_flags & RULE_REWRITE) ) + r->rule_flags &= ~RULE_GLOBAL; + + /* Read and compile the regex */ + if ( !readescstring(buffer, &line) ) { + /* boo hoo */ + return 0; /* No rule found */ + } + + if ( (rv = regcomp(&r->rx, buffer, rxflags)) != 0 ) { + /* boo hoo */ + return 0; /* No rule found */ + } + + /* Read the rewrite pattern, if any */ + if ( readescstring(buffer, &line) ) { + r->pattern = xstrdup(buffer); + } else { + r->pattern = ""; + } + + nrule++; + return 1; /* Rule found */ +} + +/* Read a rule file */ +struct rule *parserulefile(FILE *f) +{ + char line[LINE_MAX]; + struct rule *first_rule = NULL; + struct rule **last_rule = &first_rule; + struct rule *this_rule = xmalloc(sizeof(struct rule)); + + while ( fgets(line, LINE_MAX, f) ) { + if ( parseline(line, this_rule) ) { + *last_rule = this_rule; + last_rule = &this_rule->next; + this_rule = xmalloc(sizeof(struct rule)); + } + } + + free(this_rule); /* Last one is always unused */ + + return first_rule; +} + +/* Execute a rule set on a string; returns a malloc'd new string. */ +char *rewrite_string(const char *input, const struct rule *rules, int is_put) +{ + char *current = xstrdup(input); + char *newstr; + const struct rule *ruleptr = rules; + regmatch_t pmatch[10]; + int len; + int was_match = 0; + int deadman = DEADMAN_MAX_STEPS; + + for ( ruleptr = rules ; ruleptr ; ruleptr = ruleptr->next ) { + if ( ((ruleptr->rule_flags & RULE_GETONLY) && is_put) || + ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put) ) { + continue; /* Rule not applicable, try next */ + } + + if ( ! deadman-- ) { + free(current); + return NULL; /* Did not terminate! */ + } + + do { + if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == 0 ) { + /* Match on this rule */ + was_match = 1; + + if ( ruleptr->rule_flags & RULE_ABORT ) { + free(current); + return(NULL); + } + + if ( ruleptr->rule_flags & RULE_REWRITE ) { + len = genmatchstring(NULL, ruleptr->pattern, current, pmatch); + newstr = xmalloc(len+1); + genmatchstring(newstr, ruleptr->pattern, current, pmatch); + free(current); + current = newstr; + } + } else { + break; /* No match, terminate unconditionally */ + } + /* If the rule is global, keep going until no match */ + } while ( ruleptr->rule_flags & RULE_GLOBAL ); + + if ( was_match ) { + was_match = 0; + + if ( ruleptr->rule_flags & RULE_EXIT ) { + break; /* Exit here, we're done */ + } else if ( ruleptr->rule_flags & RULE_RESTART ) { + ruleptr = rules; /* Start from the top */ + } + } + } + + return current; +} diff --git a/tftpd/remap.h b/tftpd/remap.h new file mode 100644 index 0000000..c14ba75 --- /dev/null +++ b/tftpd/remap.h @@ -0,0 +1,28 @@ +/* $Id$ */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software available under the same license + * as the "OpenBSD" operating system, distributed at + * http://www.openbsd.org/. + * + * ----------------------------------------------------------------------- */ + +/* + * remap.h + * + * Prototypes for regular-expression based filename remapping. + */ + +#include +#include + +/* Opaque type */ +struct rule; + +/* Read a rule file */ +struct rule *parserulefile(FILE *); + +/* Execute a rule set on a string; returns a malloc'd new string. */ +char *rewrite_string(const char *input, const struct rule *rules, int is_put); diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 9d878ab..2084b98 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -77,6 +77,9 @@ static const char *rcsid = "tftp-hpa $Id$"; #include "../config.h" #include "tftpsubs.h" #include "recvfrom.h" +#ifdef WITH_REGEX +#include "remap.h" +#endif #ifdef HAVE_TCPWRAPPERS #include