Annotation of embedaddon/lighttpd/src/mod_secure_download.c, revision 1.1.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>