summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MCONFIG.in3
-rw-r--r--config.h.in1
-rw-r--r--configure.in27
-rw-r--r--tftp/tftpsubs.h7
-rw-r--r--tftpd/Makefile2
-rw-r--r--tftpd/remap.c282
-rw-r--r--tftpd/remap.h28
-rw-r--r--tftpd/tftpd.c3
8 files changed, 344 insertions, 9 deletions
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 <stdlib.h>
+
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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <regex.h>
+
+#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 <stdlib.h>
+#include <stdio.h>
+
+/* 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 <tcpd.h>