A security issue in the handling of the Includes and IncludesNoExec directives was reported recently, and I'm after some help.
The security issues are as follows: a) If "AllowOverride Options=IncludesNoEXEC" is configured in httpd.conf, a user can put "Options Includes" in an .htaccess file and SSI will be enabled *with* exec= permitted b) If "AllowOverride Options=IncludesNoEXEC" is configured in httpd.conf, and "Options IncludesNoExec" is enabled in the same <Directory> context, then merely placing "Options +IncludesNoExec" in an .htaccess file also results in SSI enabled with exec= permitted These are fixable but one question is left on how a particular combination of Includes and IncludesNoExec is interpreted: - if httpd.conf has "Options Includes", and an .htaccess file has "Options +IncludesNoExec" - should exec= be permitted in an SSI? I can argue this either way but am tending towards "no"; I'd very much welcome further opinions on this. I've attached the patch I'm using for testing; results are up here: http://people.apache.org/~jorton/ssi-exec/ vanilla-2.2.x.txt and jorton-v1.txt each have four columns (1) httpd.conf Options used (2) httpd.conf AllowOverride used (3) .htaccess Options used (4) result of interpreting an SSI with an exec= statement in this context; readme.txt describes vanilla-2.2.x.txt is results with vanilla 2.2.x, jorton-v1.txt is with the attached patch applied. Thanks to Jonathan for reporting the original security issue, and to Vincent for his very thorough analysis which found the additional problems. Regards, Joe
Index: server/config.c =================================================================== --- server/config.c (revision 766698) +++ server/config.c (working copy) @@ -1510,7 +1510,7 @@ parms.temp_pool = ptemp; parms.server = s; parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); - parms.override_opts = OPT_ALL | OPT_INCNOEXEC | OPT_SYM_OWNER | OPT_MULTI; + parms.override_opts = OPT_ALL | OPT_INCLUDES | OPT_SYM_OWNER | OPT_MULTI; parms.config_file = ap_pcfg_open_custom(p, "-c/-C directives", &arr_parms, NULL, @@ -1617,7 +1617,7 @@ parms.temp_pool = ptemp; parms.server = s; parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); - parms.override_opts = OPT_ALL | OPT_INCNOEXEC | OPT_SYM_OWNER | OPT_MULTI; + parms.override_opts = OPT_ALL | OPT_INCLUDES | OPT_SYM_OWNER | OPT_MULTI; rv = ap_pcfg_openfile(&cfp, p, fname); if (rv != APR_SUCCESS) { @@ -1755,7 +1755,7 @@ parms.temp_pool = ptemp; parms.server = s; parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); - parms.override_opts = OPT_ALL | OPT_INCNOEXEC | OPT_SYM_OWNER | OPT_MULTI; + parms.override_opts = OPT_ALL | OPT_INCLUDES | OPT_SYM_OWNER | OPT_MULTI; parms.limited = -1; errmsg = ap_walk_config(conftree, &parms, s->lookup_defaults); Index: server/core.c =================================================================== --- server/core.c (revision 766698) +++ server/core.c (working copy) @@ -108,7 +108,7 @@ conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL; conf->opts_add = conf->opts_remove = OPT_NONE; conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL; - conf->override_opts = OPT_UNSET | OPT_ALL | OPT_INCNOEXEC | OPT_SYM_OWNER + conf->override_opts = OPT_UNSET | OPT_ALL | OPT_INCLUDES | OPT_SYM_OWNER | OPT_MULTI; conf->content_md5 = 2; @@ -242,9 +242,6 @@ conf->opts_remove = (conf->opts_remove & ~new->opts_add) | new->opts_remove; conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add; - if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) { - conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES; - } } else { /* otherwise we just copy, because an explicit opts setting @@ -1304,10 +1301,10 @@ opt = OPT_INDEXES; } else if (!strcasecmp(w, "Includes")) { - opt = OPT_INCLUDES; + opt = (OPT_INCLUDES | OPT_INC_WITH_EXEC); } else if (!strcasecmp(w, "IncludesNOEXEC")) { - opt = (OPT_INCLUDES | OPT_INCNOEXEC); + opt = OPT_INCLUDES; } else if (!strcasecmp(w, "FollowSymLinks")) { opt = OPT_SYM_LINKS; @@ -1428,10 +1425,10 @@ opt = OPT_INDEXES; } else if (!strcasecmp(w, "Includes")) { - opt = OPT_INCLUDES; + opt = (OPT_INCLUDES | OPT_INC_WITH_EXEC); } else if (!strcasecmp(w, "IncludesNOEXEC")) { - opt = (OPT_INCLUDES | OPT_INCNOEXEC); + opt = OPT_INCLUDES; } else if (!strcasecmp(w, "FollowSymLinks")) { opt = OPT_SYM_LINKS; Index: include/http_core.h =================================================================== --- include/http_core.h (revision 766698) +++ include/http_core.h (working copy) @@ -73,14 +73,14 @@ #define OPT_EXECCGI 8 /** directive unset */ #define OPT_UNSET 16 -/** IncludesNOEXEC directive */ -#define OPT_INCNOEXEC 32 +/** exec= permission is permitted, iff OPT_INCLUDES is also set */ +#define OPT_INC_WITH_EXEC 32 /** SymLinksIfOwnerMatch directive */ #define OPT_SYM_OWNER 64 /** MultiViews directive */ #define OPT_MULTI 128 /** All directives */ -#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS|OPT_EXECCGI) +#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_INC_WITH_EXEC|OPT_SYM_LINKS|OPT_EXECCGI) /** @} */ /** Index: modules/filters/mod_include.c =================================================================== --- modules/filters/mod_include.c (revision 766698) +++ modules/filters/mod_include.c (working copy) @@ -3574,7 +3574,7 @@ intern->seen_eos = 0; intern->state = PARSE_PRE_HEAD; ctx->flags = (SSI_FLAG_PRINTING | SSI_FLAG_COND_TRUE); - if (ap_allow_options(r) & OPT_INCNOEXEC) { + if ((ap_allow_options(r) & OPT_INC_WITH_EXEC) == 0) { ctx->flags |= SSI_FLAG_NO_EXEC; } intern->accessenable = conf->accessenable;