Browse Source

[mod_ssi] more flexible quoting (fixes #1768)

allow double-quotes, single-quotes or no quote on SSI param values

remove use of PCRE from mod_ssi

fix misspelling of 'unknow' to be 'unknown'

x-ref:
  "mod_ssi doesn't accept single quotes"
  https://redmine.lighttpd.net/issues/1768
personal/stbuehler/mod-csrf-old
Glenn Strauss 5 years ago
parent
commit
a5fcfee6fc
  1. 4
      configure.ac
  2. 2
      src/CMakeLists.txt
  3. 2
      src/Makefile.am
  4. 2
      src/SConscript
  5. 179
      src/mod_ssi.c
  6. 7
      src/mod_ssi.h
  7. 3
      tests/docroot/www/ssi-include.shtml
  8. 2
      tests/mod-ssi.t

4
configure.ac

@ -841,9 +841,9 @@ AC_OUTPUT
do_build="mod_cgi mod_fastcgi mod_extforward mod_proxy mod_evhost mod_simple_vhost mod_access mod_alias mod_setenv mod_usertrack mod_auth mod_status mod_accesslog"
do_build="$do_build mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfile mod_userdir mod_webdav mod_staticfile mod_scgi mod_flv_streaming"
do_build="$do_build mod_rrdtool mod_secdownload mod_expire mod_compress mod_dirlisting mod_indexfile mod_userdir mod_webdav mod_staticfile mod_scgi mod_flv_streaming mod_ssi"
plugins="mod_rewrite mod_redirect mod_ssi mod_trigger_b4_dl"
plugins="mod_rewrite mod_redirect mod_trigger_b4_dl"
features="regex-conditionals"
if test ! "x$PCRE_LIB" = x; then
do_build="$do_build $plugins"

2
src/CMakeLists.txt

@ -597,8 +597,6 @@ if(HAVE_PCRE_H)
add_target_properties(mod_dirlisting COMPILE_FLAGS ${PCRE_CFLAGS})
target_link_libraries(mod_redirect ${PCRE_LDFLAGS})
add_target_properties(mod_redirect COMPILE_FLAGS ${PCRE_CFLAGS})
target_link_libraries(mod_ssi ${PCRE_LDFLAGS})
add_target_properties(mod_ssi COMPILE_FLAGS ${PCRE_CFLAGS})
target_link_libraries(mod_trigger_b4_dl ${PCRE_LDFLAGS})
add_target_properties(mod_trigger_b4_dl COMPILE_FLAGS ${PCRE_CFLAGS})
endif()

2
src/Makefile.am

@ -204,7 +204,7 @@ mod_proxy_la_LIBADD = $(common_libadd)
lib_LTLIBRARIES += mod_ssi.la
mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c
mod_ssi_la_LDFLAGS = $(common_module_ldflags)
mod_ssi_la_LIBADD = $(common_libadd) $(PCRE_LIB)
mod_ssi_la_LIBADD = $(common_libadd)
lib_LTLIBRARIES += mod_secdownload.la
mod_secdownload_la_SOURCES = mod_secdownload.c

2
src/SConscript

@ -106,7 +106,7 @@ modules = {
'mod_mysql_vhost' : { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] },
# 'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] },
'mod_evasive' : { 'src' : [ 'mod_evasive.c' ] },
'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ], 'lib' : [ env['LIBPCRE'] ] },
'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ] },
'mod_flv_streaming' : { 'src' : [ 'mod_flv_streaming.c' ] },
'mod_cml': {
'src' : [ 'mod_cml_lua.c', 'mod_cml.c', 'mod_cml_funcs.c' ],

179
src/mod_ssi.c

@ -84,9 +84,6 @@ FREE_FUNC(mod_ssi_free) {
array_free(p->ssi_vars);
array_free(p->ssi_cgi_env);
#ifdef HAVE_PCRE_H
pcre_free(p->ssi_regex);
#endif
buffer_free(p->timefmt);
buffer_free(p->stat_fn);
@ -100,10 +97,6 @@ FREE_FUNC(mod_ssi_free) {
SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
plugin_data *p = p_d;
size_t i = 0;
#ifdef HAVE_PCRE_H
const char *errptr;
int erroff;
#endif
config_values_t cv[] = {
{ "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
@ -139,26 +132,10 @@ SETDEFAULTS_FUNC(mod_ssi_set_defaults) {
}
}
#ifdef HAVE_PCRE_H
/* allow 2 params */
if (NULL == (p->ssi_regex = pcre_compile("^<!--#([a-z]+)\\s+(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?-->$", PCRE_ANCHORED|PCRE_DOLLAR_ENDONLY|PCRE_DOTALL|PCRE_UTF8, &errptr, &erroff, NULL))) {
log_error_write(srv, __FILE__, __LINE__, "sds",
"ssi: pcre ",
erroff, errptr);
return HANDLER_ERROR;
}
#else
log_error_write(srv, __FILE__, __LINE__, "s",
"mod_ssi: pcre support is missing, please recompile with pcre support or remove mod_ssi from the list of modules");
return HANDLER_ERROR;
#endif
return HANDLER_GO_ON;
}
#ifdef HAVE_PCRE_H
static int ssi_env_add(array *env, const char *key, const char *val) {
data_string *ds;
@ -471,7 +448,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
*/
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -598,7 +575,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
virt_path = l[i+1];
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -747,7 +724,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
val = l[i+1];
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -764,10 +741,12 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
buffer_copy_string(ds->value, val);
array_insert_unique(p->ssi_vars, (data_unset *)ds);
} else if (key || val) {
log_error_write(srv, __FILE__, __LINE__, "sSSss",
"ssi: var and value have to be set in <!--#set", l[1], "=", l[2], "-->");
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: var and value have to be set in",
l[0], l[1]);
log_error_write(srv, __FILE__, __LINE__, "s",
"ssi: var and value have to be set in <!--#set var=... value=... -->");
}
break;
}
@ -784,14 +763,14 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
p->sizefmt = 0;
} else {
log_error_write(srv, __FILE__, __LINE__, "sssss",
"ssi: unknow value for attribute '",
"ssi: unknown value for attribute '",
l[i],
"' for ",
l[1], l[i+1]);
}
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -834,7 +813,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
cmd = l[i+1];
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -951,7 +930,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
expr = l[i+1];
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -1005,7 +984,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
expr = l[i+1];
} else {
log_error_write(srv, __FILE__, __LINE__, "sss",
"ssi: unknow attribute for ",
"ssi: unknown attribute for ",
l[1], l[i]);
}
}
@ -1054,7 +1033,7 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
break;
default:
log_error_write(srv, __FILE__, __LINE__, "ss",
"ssi: unknow ssi-command:",
"ssi: unknown ssi-command:",
l[1]);
break;
}
@ -1063,27 +1042,125 @@ static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const
}
static int mod_ssi_parse_ssi_stmt_value(const char * const s, const int len) {
int n;
const int c = (s[0] == '"' ? '"' : s[0] == '\'' ? '\'' : 0);
if (0 != c) {
for (n = 1; n < len; ++n) {
if (s[n] == c) return n+1;
if (s[n] == '\\') {
if (n+1 == len) return 0; /* invalid */
++n;
}
}
return 0; /* invalid */
} else {
for (n = 0; n < len; ++n) {
if (isspace(s[n])) return n;
if (s[n] == '\\') {
if (n+1 == len) return 0; /* invalid */
++n;
}
}
return n;
}
}
static int mod_ssi_parse_ssi_stmt_offlen(int o[10], const char * const s, const int len) {
/**
* <!--#element attribute=value attribute=value ... -->
*/
/* s must begin "<!--#" and must end with "-->" */
int n = 5;
o[0] = n;
for (; light_isalpha(s[n]); ++n) ; /*(n = 5 to begin after "<!--#")*/
o[1] = n - o[0];
if (0 == o[1]) return -1; /* empty token */
if (n+3 == len) return 2; /* token only; no params */
if (!isspace(s[n])) return -1;
do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */
if (n+3 == len) return 2; /* token only; no params */
o[2] = n;
for (; light_isalpha(s[n]); ++n) ;
o[3] = n - o[2];
if (0 == o[3] || s[n++] != '=') return -1;
o[4] = n;
o[5] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3);
if (0 == o[5]) return -1; /* empty or invalid token */
n += o[5];
if (n+3 == len) return 6; /* token and one param */
if (!isspace(s[n])) return -1;
do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */
if (n+3 == len) return 6; /* token and one param */
o[6] = n;
for (; light_isalpha(s[n]); ++n) ;
o[7] = n - o[6];
if (0 == o[7] || s[n++] != '=') return -1;
o[8] = n;
o[9] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3);
if (0 == o[9]) return -1; /* empty or invalid token */
n += o[9];
if (n+3 == len) return 10; /* token and two params */
if (!isspace(s[n])) return -1;
do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */
if (n+3 == len) return 10; /* token and two params */
return -1;
}
static void mod_ssi_parse_ssi_stmt(server *srv, connection *con, plugin_data *p, char *s, int len, struct stat *st) {
/**
* <!--#element attribute=value attribute=value ... -->
*/
#define N 10
int ovec[N * 3];
int n = pcre_exec(p->ssi_regex, NULL, s, len, 0, PCRE_ANCHORED, ovec, sizeof(ovec)/sizeof(*ovec));
if (n > 0) {
const char **l;
pcre_get_substring_list(s, ovec, n, &l);
process_ssi_stmt(srv, con, p, l, n, st);
pcre_free_substring_list(l);
} else {
if (n != PCRE_ERROR_NOMATCH) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"execution error while matching: ", n);
}
int o[10];
int m;
const int n = mod_ssi_parse_ssi_stmt_offlen(o, s, len);
char *l[6] = { s, NULL, NULL, NULL, NULL, NULL };
if (-1 == n) {
/* XXX: perhaps emit error comment instead of invalid <!--#...--> code to client */
chunkqueue_append_mem(con->write_queue, s, len); /* append stmt as-is */
return;
}
#if 0
/* dup s and then modify s */
/*(l[0] is no longer used; was previously used in only one place for error reporting)*/
l[0] = malloc((size_t)(len+1));
memcpy(l[0], s, (size_t)len);
(l[0])[len] = '\0';
#endif
/* modify s in-place to split string into arg tokens */
for (m = 0; m < n; m += 2) {
char *ptr = s+o[m];
switch (*ptr) {
case '"':
case '\'': (++ptr)[o[m+1]-2] = '\0'; break;
default: ptr[o[m+1]] = '\0'; break;
}
l[1+(m>>1)] = ptr;
if (m == 4 || m == 8) {
/* XXX: removing '\\' escapes from param value would be
* the right thing to do, but would potentially change
* current behavior, e.g. <!--#exec cmd=... --> */
}
}
process_ssi_stmt(srv, con, p, (const char **)l, 1+(n>>1), st);
#if 0
free(l[0]);
#endif
}
static int mod_ssi_stmt_len(const char *s, const int len) {
@ -1184,8 +1261,6 @@ static void mod_ssi_read_fd(server *srv, connection *con, plugin_data *p, int fd
}
}
#endif /* HAVE_PCRE_H */
/* don't want to block when open()ing a fifo */
#if defined(O_NONBLOCK)
@ -1222,11 +1297,7 @@ static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p)
return -1;
}
#ifdef HAVE_PCRE_H
mod_ssi_read_fd(srv, con, p, fd, &st);
#else
chunkqueue_append_file(con->write_queue, con->physical.path, 0, st.st_size);
#endif
close(fd);
con->file_started = 1;

7
src/mod_ssi.h

@ -8,10 +8,6 @@
#include "plugin.h"
#ifdef HAVE_PCRE_H
#include <pcre.h>
#endif
/* plugin config for all request/connections */
typedef struct {
@ -24,9 +20,6 @@ typedef struct {
typedef struct {
PLUGIN_DATA;
#ifdef HAVE_PCRE_H
pcre *ssi_regex;
#endif
buffer *timefmt;
int sizefmt;

3
tests/docroot/www/ssi-include.shtml

@ -1,2 +1,5 @@
<!--#echo var=SCRIPT_NAME-->
<!--#echo var='SCRIPT_NAME'-->
<!--#echo var="SCRIPT_NAME"-->
<!--#include virtual="ssi-include.txt" -->
<!--#include file="ssi-include.txt" -->

2
tests/mod-ssi.t

@ -37,7 +37,7 @@ $t->{REQUEST} = ( <<EOF
GET /ssi-include.shtml HTTP/1.0
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "ssi-include\n\nssi-include\n\n" } ];
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "/ssi-include.shtml\n/ssi-include.shtml\n/ssi-include.shtml\nssi-include\n\nssi-include\n\n" } ];
ok($tf->handle_http($t) == 0, 'ssi - include');

Loading…
Cancel
Save