Annotation of embedaddon/lighttpd/src/mod_secure_download.c, revision 1.1
1.1 ! misho 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(specific_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:
! 259: li_MD5_Init(&Md5Ctx);
! 260: li_MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1);
! 261: li_MD5_Final(HA1, &Md5Ctx);
! 262:
! 263: buffer_copy_string_hex(p->md5, (char *)HA1, 16);
! 264:
! 265: if (0 != strncasecmp(md5_str, p->md5->ptr, 32)) {
! 266: con->http_status = 403;
! 267:
! 268: log_error_write(srv, __FILE__, __LINE__, "sss",
! 269: "md5 invalid:",
! 270: md5_str, p->md5->ptr);
! 271:
! 272: return HANDLER_FINISHED;
! 273: }
! 274:
! 275: /* starting with the last / we should have relative-path to the docroot
! 276: */
! 277:
! 278: buffer_copy_string_buffer(con->physical.doc_root, p->conf.doc_root);
! 279: buffer_copy_string(con->physical.rel_path, rel_uri);
! 280: buffer_copy_string_buffer(con->physical.path, con->physical.doc_root);
! 281: buffer_append_string_buffer(con->physical.path, con->physical.rel_path);
! 282:
! 283: return HANDLER_GO_ON;
! 284: }
! 285:
! 286: /* this function is called at dlopen() time and inits the callbacks */
! 287:
! 288: int mod_secdownload_plugin_init(plugin *p);
! 289: int mod_secdownload_plugin_init(plugin *p) {
! 290: p->version = LIGHTTPD_VERSION_ID;
! 291: p->name = buffer_init_string("secdownload");
! 292:
! 293: p->init = mod_secdownload_init;
! 294: p->handle_physical = mod_secdownload_uri_handler;
! 295: p->set_defaults = mod_secdownload_set_defaults;
! 296: p->cleanup = mod_secdownload_free;
! 297:
! 298: p->data = NULL;
! 299:
! 300: return 0;
! 301: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>