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>