Annotation of embedaddon/lighttpd/src/mod_secure_download.c, revision 1.1.1.2

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: 
1.1.1.2 ! misho     100:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     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);
1.1.1.2 ! misho     258:        force_assert(p->md5->used > 0);
1.1       misho     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);
1.1.1.2 ! misho     280:        buffer_copy_string_buffer(con->physical.basedir, p->conf.doc_root);
1.1       misho     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>