2
#include <ngx_string.h>
3
#include <ngx_config.h>
10
#include <openssl/md5.h>
17
const char *timestamp;
22
} ngx_http_secure_download_split_uri_t;
24
static ngx_int_t ngx_http_secure_download_split_uri (ngx_http_request_t*, ngx_http_secure_download_split_uri_t*);
25
static ngx_int_t ngx_http_secure_download_check_hash(ngx_http_request_t*, ngx_http_secure_download_split_uri_t*, ngx_str_t*);
26
static void * ngx_http_secure_download_create_loc_conf(ngx_conf_t*);
27
static char * ngx_http_secure_download_merge_loc_conf (ngx_conf_t*, void*, void*);
28
static ngx_int_t ngx_http_secure_download_add_variables(ngx_conf_t *cf);
29
static ngx_int_t ngx_http_secure_download_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
30
static char * ngx_conf_set_path_mode(ngx_conf_t*, ngx_command_t*, void*);
32
static char *ngx_http_secure_download_secret(ngx_conf_t *cf, void *post, void *data);
33
static ngx_conf_post_handler_pt ngx_http_secure_download_secret_p =
34
ngx_http_secure_download_secret;
40
ngx_array_t *secret_lengths;
41
ngx_array_t *secret_values;
42
} ngx_http_secure_download_loc_conf_t;
44
static ngx_command_t ngx_http_secure_download_commands[] = {
46
ngx_string("secure_download"),
47
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
48
ngx_conf_set_flag_slot,
49
NGX_HTTP_LOC_CONF_OFFSET,
50
offsetof(ngx_http_secure_download_loc_conf_t, enable),
54
ngx_string("secure_download_path_mode"),
55
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
56
ngx_conf_set_path_mode,
57
NGX_HTTP_LOC_CONF_OFFSET,
58
offsetof(ngx_http_secure_download_loc_conf_t, path_mode),
62
ngx_string("secure_download_secret"),
63
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
64
ngx_conf_set_str_slot,
65
NGX_HTTP_LOC_CONF_OFFSET,
66
offsetof(ngx_http_secure_download_loc_conf_t, secret),
67
&ngx_http_secure_download_secret_p
71
static ngx_http_module_t ngx_http_secure_download_module_ctx = {
72
ngx_http_secure_download_add_variables,
81
ngx_http_secure_download_create_loc_conf,
82
ngx_http_secure_download_merge_loc_conf
85
ngx_module_t ngx_http_secure_download_module = {
87
&ngx_http_secure_download_module_ctx,
88
ngx_http_secure_download_commands,
100
static ngx_str_t ngx_http_secure_download = ngx_string("secure_download");
102
static char * ngx_conf_set_path_mode(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
104
ngx_str_t *d = cf->args->elts;
105
ngx_http_secure_download_loc_conf_t *sdlc = conf;
106
if ((d[1].len == 6) && (strncmp((char*)d[1].data, "folder", 6) == 0))
108
sdlc->path_mode = FOLDER_MODE;
110
else if((d[1].len == 4) && (strncmp((char*)d[1].data, "file", 4) == 0))
112
sdlc->path_mode = FILE_MODE;
116
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "secure_download_path_mode should be folder or file", 0);
117
return NGX_CONF_ERROR;
122
static void * ngx_http_secure_download_create_loc_conf(ngx_conf_t *cf)
124
ngx_http_secure_download_loc_conf_t *conf;
126
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_download_loc_conf_t));
128
return NGX_CONF_ERROR;
130
conf->enable = NGX_CONF_UNSET;
131
conf->path_mode = NGX_CONF_UNSET;
132
conf->secret.data = NULL;
133
conf->secret.len = 0;
137
static char * ngx_http_secure_download_merge_loc_conf (ngx_conf_t *cf, void *parent, void *child)
139
ngx_http_secure_download_loc_conf_t *prev = parent;
140
ngx_http_secure_download_loc_conf_t *conf = child;
142
ngx_conf_merge_value(conf->enable, prev->enable, 0);
143
ngx_conf_merge_value(conf->path_mode, prev->path_mode, FOLDER_MODE);
144
ngx_conf_merge_str_value(conf->secret, prev->secret, "");
146
if (conf->enable == 1) {
147
if (conf->secret.len == 0) {
148
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
149
"no secure_download_secret specified");
150
return NGX_CONF_ERROR;
157
static ngx_int_t ngx_http_secure_download_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) {
159
unsigned remaining_time = 0;
160
ngx_http_secure_download_loc_conf_t *sdc;
161
ngx_http_secure_download_split_uri_t sdsu;
166
sdc = ngx_http_get_module_loc_conf(r, ngx_http_secure_download_module);
167
if (sdc->enable != 1)
169
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
170
"securedownload: module not enabled");
175
if (!sdc->secret_lengths || !sdc->secret_values) {
176
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
177
"securedownload: module enabled, but secret key not configured!");
182
if (ngx_http_secure_download_split_uri(r, &sdsu) == NGX_ERROR)
184
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "received an error from ngx_http_secure_download_split_uri", 0);
189
if (sscanf(sdsu.timestamp, "%08X", ×tamp) != 1)
191
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "error in timestamp hex-dec conversion", 0);
196
remaining_time = timestamp - (unsigned) time(NULL);
197
if ((int)remaining_time <= 0)
199
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "expired timestamp", 0);
204
if (ngx_http_script_run(r, &secret, sdc->secret_lengths->elts, 0, sdc->secret_values->elts) == NULL) {
205
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
206
"securedownload: evaluation failed");
211
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
212
"securedownload: evaluated value of secret: \"%V\"", &secret);
214
if (ngx_http_secure_download_check_hash(r, &sdsu, &secret) != NGX_OK)
216
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "bad hash", 0);
221
rel_path.data = r->uri.data;
222
rel_path.len = sdsu.path_len;
232
v->data = ngx_pcalloc(r->pool, sizeof(char) * 12);
233
if (v->data == NULL) {
236
v->len = (int) sprintf((char *)v->data, "%i", remaining_time);
237
//printf("valid, %i\n", remaining_time);
239
v->data = ngx_pcalloc(r->pool, sizeof(char) * 3);
240
if (v->data == NULL) {
243
v->len = (int) sprintf((char*)v->data, "%i", value);
244
//printf("problem %i\n", value);
250
//////////////////////
252
ngx_http_secure_download_compile_secret(ngx_conf_t *cf, ngx_http_secure_download_loc_conf_t *sdc)
255
ngx_http_script_compile_t sc;
256
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
259
sc.source = &sdc->secret;
260
sc.lengths = &sdc->secret_lengths;
261
sc.values = &sdc->secret_values;
262
sc.variables = ngx_http_script_variables_count(&sdc->secret);
263
sc.complete_lengths = 1;
264
sc.complete_values = 1;
266
if (ngx_http_script_compile(&sc) != NGX_OK) {
267
return NGX_CONF_ERROR;
274
ngx_http_secure_download_secret(ngx_conf_t *cf, void *post, void *data)
276
ngx_http_secure_download_loc_conf_t *sdc =
277
ngx_http_conf_get_module_loc_conf(cf, ngx_http_secure_download_module);
279
return ngx_http_secure_download_compile_secret(cf, sdc);
281
////////////////////////
283
static ngx_int_t ngx_http_secure_download_check_hash(ngx_http_request_t *r, ngx_http_secure_download_split_uri_t *sdsu, ngx_str_t *secret)
286
unsigned char generated_hash[16];
289
char *hash_data, *str;
292
static const char xtoc[] = "0123456789abcdef";
294
/* rel_path_to_hash/secret/timestamp\0 */
296
data_len = sdsu->path_to_hash_len + secret->len + 10;
298
hash_data = malloc(data_len + 1);
299
if (hash_data == NULL)
301
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "error in allocating memory for string_to_hash.data", 0);
306
memcpy(str, sdsu->path, sdsu->path_to_hash_len);
307
str += sdsu->path_to_hash_len;
309
memcpy(str, secret->data, secret->len);
312
memcpy(str, sdsu->timestamp, 8);
315
td = mhash_init(MHASH_MD5);
317
if (td == MHASH_FAILED)
322
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "hashing string \"%s\" with len %i", hash_data, data_len);
323
mhash(td, hash_data, data_len);
324
mhash_deinit(td, generated_hash);
328
for (i = 0; i < 16; ++i) {
329
hash[2 * i + 0] = xtoc[generated_hash[i] >> 4];
330
hash[2 * i + 1] = xtoc[generated_hash[i] & 0xf];
333
hash[32] = 0; //because %.32 doesn't work
334
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "computed hash: %32s", hash);
335
// ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "hash from uri: %.32s", sdsu->md5);
337
if(memcmp(hash, sdsu->md5, 32) != 0) {
338
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "hash mismatch", 0);
345
static ngx_int_t ngx_http_secure_download_split_uri(ngx_http_request_t *r, ngx_http_secure_download_split_uri_t *sdsu)
349
int len = r->uri.len;
350
const char *uri = (char*)r->uri.data;
352
ngx_http_secure_download_loc_conf_t *sdc = ngx_http_get_module_loc_conf(r, ngx_http_secure_download_module);
354
while(len && uri[--len] != '/')
356
if(tstamp_len != 8) {
357
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "timestamp size mismatch: %d", tstamp_len);
360
sdsu->timestamp = uri + len + 1;
362
while(len && uri[--len] != '/')
365
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "md5 size mismatch: %d", md5_len);
368
sdsu->md5 = uri + len + 1;
371
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "bad path", 0);
376
sdsu->path_len = len;
378
if(sdc->path_mode == FOLDER_MODE) {
379
while(len && uri[--len] != '/');
381
sdsu->path_to_hash_len = len;
387
ngx_http_secure_download_add_variables(ngx_conf_t *cf)
389
ngx_http_variable_t *var;
391
var = ngx_http_add_variable(cf, &ngx_http_secure_download, NGX_HTTP_VAR_NOHASH);
396
var->get_handler = ngx_http_secure_download_variable;