File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_secure_download.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    1: #include "base.h"
    2: #include "log.h"
    3: #include "buffer.h"
    4: 
    5: #include "plugin.h"
    6: 
    7: #include <ctype.h>
    8: #include <stdlib.h>
    9: #include <string.h>
   10: 
   11: #include "md5.h"
   12: 
   13: #define HASHLEN 16
   14: typedef unsigned char HASH[HASHLEN];
   15: #define HASHHEXLEN 32
   16: typedef char HASHHEX[HASHHEXLEN+1];
   17: #ifdef USE_OPENSSL
   18: #define IN const
   19: #else
   20: #define IN
   21: #endif
   22: #define OUT
   23: 
   24: 
   25: /* plugin config for all request/connections */
   26: 
   27: typedef struct {
   28: 	buffer *doc_root;
   29: 	buffer *secret;
   30: 	buffer *uri_prefix;
   31: 
   32: 	unsigned int timeout;
   33: } plugin_config;
   34: 
   35: typedef struct {
   36: 	PLUGIN_DATA;
   37: 
   38: 	buffer *md5;
   39: 
   40: 	plugin_config **config_storage;
   41: 
   42: 	plugin_config conf;
   43: } plugin_data;
   44: 
   45: /* init the plugin data */
   46: INIT_FUNC(mod_secdownload_init) {
   47: 	plugin_data *p;
   48: 
   49: 	p = calloc(1, sizeof(*p));
   50: 
   51: 	p->md5 = buffer_init();
   52: 
   53: 	return p;
   54: }
   55: 
   56: /* detroy the plugin data */
   57: FREE_FUNC(mod_secdownload_free) {
   58: 	plugin_data *p = p_d;
   59: 	UNUSED(srv);
   60: 
   61: 	if (!p) return HANDLER_GO_ON;
   62: 
   63: 	if (p->config_storage) {
   64: 		size_t i;
   65: 		for (i = 0; i < srv->config_context->used; i++) {
   66: 			plugin_config *s = p->config_storage[i];
   67: 
   68: 			buffer_free(s->secret);
   69: 			buffer_free(s->doc_root);
   70: 			buffer_free(s->uri_prefix);
   71: 
   72: 			free(s);
   73: 		}
   74: 		free(p->config_storage);
   75: 	}
   76: 
   77: 	buffer_free(p->md5);
   78: 
   79: 	free(p);
   80: 
   81: 	return HANDLER_GO_ON;
   82: }
   83: 
   84: /* handle plugin config and check values */
   85: 
   86: SETDEFAULTS_FUNC(mod_secdownload_set_defaults) {
   87: 	plugin_data *p = p_d;
   88: 	size_t i = 0;
   89: 
   90: 	config_values_t cv[] = {
   91: 		{ "secdownload.secret",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
   92: 		{ "secdownload.document-root",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
   93: 		{ "secdownload.uri-prefix",        NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
   94: 		{ "secdownload.timeout",           NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION },        /* 3 */
   95: 		{ NULL,                            NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
   96: 	};
   97: 
   98: 	if (!p) return HANDLER_ERROR;
   99: 
  100: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
  101: 
  102: 	for (i = 0; i < srv->config_context->used; i++) {
  103: 		plugin_config *s;
  104: 
  105: 		s = calloc(1, sizeof(plugin_config));
  106: 		s->secret        = buffer_init();
  107: 		s->doc_root      = buffer_init();
  108: 		s->uri_prefix    = buffer_init();
  109: 		s->timeout       = 60;
  110: 
  111: 		cv[0].destination = s->secret;
  112: 		cv[1].destination = s->doc_root;
  113: 		cv[2].destination = s->uri_prefix;
  114: 		cv[3].destination = &(s->timeout);
  115: 
  116: 		p->config_storage[i] = s;
  117: 
  118: 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
  119: 			return HANDLER_ERROR;
  120: 		}
  121: 	}
  122: 
  123: 	return HANDLER_GO_ON;
  124: }
  125: 
  126: /**
  127:  * checks if the supplied string is a MD5 string
  128:  *
  129:  * @param str a possible MD5 string
  130:  * @return if the supplied string is a valid MD5 string 1 is returned otherwise 0
  131:  */
  132: 
  133: static int is_hex_len(const char *str, size_t len) {
  134: 	size_t i;
  135: 
  136: 	if (NULL == str) return 0;
  137: 
  138: 	for (i = 0; i < len && *str; i++, str++) {
  139: 		/* illegal characters */
  140: 		if (!((*str >= '0' && *str <= '9') ||
  141: 		      (*str >= 'a' && *str <= 'f') ||
  142: 		      (*str >= 'A' && *str <= 'F'))
  143: 		    ) {
  144: 			return 0;
  145: 		}
  146: 	}
  147: 
  148: 	return i == len;
  149: }
  150: 
  151: #define PATCH(x) \
  152: 	p->conf.x = s->x;
  153: static int mod_secdownload_patch_connection(server *srv, connection *con, plugin_data *p) {
  154: 	size_t i, j;
  155: 	plugin_config *s = p->config_storage[0];
  156: 
  157: 	PATCH(secret);
  158: 	PATCH(doc_root);
  159: 	PATCH(uri_prefix);
  160: 	PATCH(timeout);
  161: 
  162: 	/* skip the first, the global context */
  163: 	for (i = 1; i < srv->config_context->used; i++) {
  164: 		data_config *dc = (data_config *)srv->config_context->data[i];
  165: 		s = p->config_storage[i];
  166: 
  167: 		/* condition didn't match */
  168: 		if (!config_check_cond(srv, con, dc)) continue;
  169: 
  170: 		/* merge config */
  171: 		for (j = 0; j < dc->value->used; j++) {
  172: 			data_unset *du = dc->value->data[j];
  173: 
  174: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.secret"))) {
  175: 				PATCH(secret);
  176: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.document-root"))) {
  177: 				PATCH(doc_root);
  178: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.uri-prefix"))) {
  179: 				PATCH(uri_prefix);
  180: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.timeout"))) {
  181: 				PATCH(timeout);
  182: 			}
  183: 		}
  184: 	}
  185: 
  186: 	return 0;
  187: }
  188: #undef PATCH
  189: 
  190: 
  191: URIHANDLER_FUNC(mod_secdownload_uri_handler) {
  192: 	plugin_data *p = p_d;
  193: 	li_MD5_CTX Md5Ctx;
  194: 	HASH HA1;
  195: 	const char *rel_uri, *ts_str, *md5_str;
  196: 	time_t ts = 0;
  197: 	size_t i;
  198: 
  199: 	if (con->mode != DIRECT) return HANDLER_GO_ON;
  200: 
  201: 	if (con->uri.path->used == 0) return HANDLER_GO_ON;
  202: 
  203: 	mod_secdownload_patch_connection(srv, con, p);
  204: 
  205: 	if (buffer_is_empty(p->conf.uri_prefix)) return HANDLER_GO_ON;
  206: 
  207: 	if (buffer_is_empty(p->conf.secret)) {
  208: 		log_error_write(srv, __FILE__, __LINE__, "s",
  209: 				"secdownload.secret has to be set");
  210: 		return HANDLER_ERROR;
  211: 	}
  212: 
  213: 	if (buffer_is_empty(p->conf.doc_root)) {
  214: 		log_error_write(srv, __FILE__, __LINE__, "s",
  215: 				"secdownload.document-root has to be set");
  216: 		return HANDLER_ERROR;
  217: 	}
  218: 
  219: 	/*
  220: 	 *  /<uri-prefix>[a-f0-9]{32}/[a-f0-9]{8}/<rel-path>
  221: 	 */
  222: 
  223: 	if (0 != strncmp(con->uri.path->ptr, p->conf.uri_prefix->ptr, p->conf.uri_prefix->used - 1)) return HANDLER_GO_ON;
  224: 
  225: 	md5_str = con->uri.path->ptr + p->conf.uri_prefix->used - 1;
  226: 
  227: 	if (!is_hex_len(md5_str, 32)) return HANDLER_GO_ON;
  228: 	if (*(md5_str + 32) != '/') return HANDLER_GO_ON;
  229: 
  230: 	ts_str = md5_str + 32 + 1;
  231: 
  232: 	if (!is_hex_len(ts_str, 8)) return HANDLER_GO_ON;
  233: 	if (*(ts_str + 8) != '/') return HANDLER_GO_ON;
  234: 
  235: 	for (i = 0; i < 8; i++) {
  236: 		ts = (ts << 4) + hex2int(*(ts_str + i));
  237: 	}
  238: 
  239: 	/* timed-out */
  240: 	if ( (srv->cur_ts > ts && (unsigned int) (srv->cur_ts - ts) > p->conf.timeout) ||
  241: 	     (srv->cur_ts < ts && (unsigned int) (ts - srv->cur_ts) > p->conf.timeout) ) {
  242: 		/* "Gone" as the url will never be valid again instead of "408 - Timeout" where the request may be repeated */
  243: 		con->http_status = 410;
  244: 
  245: 		return HANDLER_FINISHED;
  246: 	}
  247: 
  248: 	rel_uri = ts_str + 8;
  249: 
  250: 	/* checking MD5
  251: 	 *
  252: 	 * <secret><rel-path><timestamp-hex>
  253: 	 */
  254: 
  255: 	buffer_copy_string_buffer(p->md5, p->conf.secret);
  256: 	buffer_append_string(p->md5, rel_uri);
  257: 	buffer_append_string_len(p->md5, ts_str, 8);
  258: 	force_assert(p->md5->used > 0);
  259: 
  260: 	li_MD5_Init(&Md5Ctx);
  261: 	li_MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1);
  262: 	li_MD5_Final(HA1, &Md5Ctx);
  263: 
  264: 	buffer_copy_string_hex(p->md5, (char *)HA1, 16);
  265: 
  266: 	if (0 != strncasecmp(md5_str, p->md5->ptr, 32)) {
  267: 		con->http_status = 403;
  268: 
  269: 		log_error_write(srv, __FILE__, __LINE__, "sss",
  270: 				"md5 invalid:",
  271: 				md5_str, p->md5->ptr);
  272: 
  273: 		return HANDLER_FINISHED;
  274: 	}
  275: 
  276: 	/* starting with the last / we should have relative-path to the docroot
  277: 	 */
  278: 
  279: 	buffer_copy_string_buffer(con->physical.doc_root, p->conf.doc_root);
  280: 	buffer_copy_string_buffer(con->physical.basedir, p->conf.doc_root);
  281: 	buffer_copy_string(con->physical.rel_path, rel_uri);
  282: 	buffer_copy_string_buffer(con->physical.path, con->physical.doc_root);
  283: 	buffer_append_string_buffer(con->physical.path, con->physical.rel_path);
  284: 
  285: 	return HANDLER_GO_ON;
  286: }
  287: 
  288: /* this function is called at dlopen() time and inits the callbacks */
  289: 
  290: int mod_secdownload_plugin_init(plugin *p);
  291: int mod_secdownload_plugin_init(plugin *p) {
  292: 	p->version     = LIGHTTPD_VERSION_ID;
  293: 	p->name        = buffer_init_string("secdownload");
  294: 
  295: 	p->init        = mod_secdownload_init;
  296: 	p->handle_physical  = mod_secdownload_uri_handler;
  297: 	p->set_defaults  = mod_secdownload_set_defaults;
  298: 	p->cleanup     = mod_secdownload_free;
  299: 
  300: 	p->data        = NULL;
  301: 
  302: 	return 0;
  303: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>