~ubuntu-branches/ubuntu/oneiric/nginx/oneiric-security

« back to all changes in this revision

Viewing changes to debian/modules/nginx-secure-download/ngx_http_secure_download_module.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Lustfield
  • Date: 2011-05-09 00:36:54 UTC
  • mfrom: (4.2.32 sid)
  • Revision ID: james.westby@ubuntu.com-20110509003654-ovgx2o0puujktwu6
Tags: 1.0.1-1
* New upstream release
* debian/rules:
  + Removed if surrounding copy of man/ as it is required for builds.
  + Added nginx-upload-progress to nginx-extras. (Closes: #618306)
  + Added nginx-secure-downloads to nginx-extras. (Closes: #622268)
  + Added --prefix to configure command. (Closes: #619482)
* debian/modules:
  + Added nginx-upload-progress/*.
  + Added nginx-secure-download/*.
  + Updated nginx-lua/*.
  + Updated versions.
* debian/control:
  + Added libmhash-dev build dependency.
  + Updated Standards-Version to 3.9.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <ngx_core.h>
 
2
#include <ngx_string.h>
 
3
#include <ngx_config.h>
 
4
#include <ngx_core.h>
 
5
#include <ngx_http.h>
 
6
#include <stdio.h>
 
7
#include <string.h>
 
8
#include <stdlib.h>
 
9
#include <mhash.h>
 
10
#include <openssl/md5.h>
 
11
#include <ctype.h>
 
12
 
 
13
#define FOLDER_MODE 0
 
14
#define FILE_MODE 1
 
15
 
 
16
typedef struct {
 
17
  const char *timestamp;
 
18
  const char *md5;
 
19
  const char *path;
 
20
  int path_len;
 
21
  int path_to_hash_len;
 
22
} ngx_http_secure_download_split_uri_t;
 
23
 
 
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*);
 
31
 
 
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;
 
35
 
 
36
typedef struct {
 
37
  ngx_flag_t enable;
 
38
  ngx_flag_t path_mode;
 
39
  ngx_str_t secret;
 
40
  ngx_array_t  *secret_lengths;
 
41
  ngx_array_t  *secret_values;
 
42
} ngx_http_secure_download_loc_conf_t;
 
43
 
 
44
static ngx_command_t ngx_http_secure_download_commands[] = {
 
45
  {
 
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),
 
51
    NULL
 
52
  },
 
53
  {
 
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),
 
59
    NULL
 
60
  },
 
61
  {
 
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
 
68
  }
 
69
};
 
70
 
 
71
static ngx_http_module_t ngx_http_secure_download_module_ctx = {
 
72
  ngx_http_secure_download_add_variables,
 
73
  NULL,
 
74
 
 
75
  NULL,
 
76
  NULL,
 
77
 
 
78
  NULL,
 
79
  NULL,
 
80
 
 
81
  ngx_http_secure_download_create_loc_conf,
 
82
  ngx_http_secure_download_merge_loc_conf
 
83
};
 
84
 
 
85
ngx_module_t ngx_http_secure_download_module = {
 
86
  NGX_MODULE_V1,
 
87
  &ngx_http_secure_download_module_ctx,
 
88
  ngx_http_secure_download_commands,
 
89
  NGX_HTTP_MODULE,
 
90
  NULL,
 
91
  NULL,
 
92
  NULL,
 
93
  NULL,
 
94
  NULL,
 
95
  NULL,
 
96
  NULL,
 
97
  NGX_MODULE_V1_PADDING
 
98
};
 
99
 
 
100
static ngx_str_t  ngx_http_secure_download = ngx_string("secure_download");
 
101
 
 
102
static char * ngx_conf_set_path_mode(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
103
{
 
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))
 
107
  {
 
108
    sdlc->path_mode = FOLDER_MODE;
 
109
  }
 
110
  else if((d[1].len == 4) && (strncmp((char*)d[1].data, "file", 4) == 0))
 
111
  {
 
112
    sdlc->path_mode = FILE_MODE;
 
113
  }
 
114
  else
 
115
  {
 
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;
 
118
  }
 
119
  return NGX_CONF_OK;
 
120
}
 
121
 
 
122
static void * ngx_http_secure_download_create_loc_conf(ngx_conf_t *cf)
 
123
{
 
124
  ngx_http_secure_download_loc_conf_t *conf;
 
125
 
 
126
  conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_download_loc_conf_t));
 
127
  if (conf == NULL) {
 
128
    return NGX_CONF_ERROR;
 
129
  }
 
130
  conf->enable = NGX_CONF_UNSET;
 
131
  conf->path_mode = NGX_CONF_UNSET;
 
132
  conf->secret.data = NULL;
 
133
  conf->secret.len = 0;
 
134
  return conf;
 
135
}
 
136
 
 
137
static char * ngx_http_secure_download_merge_loc_conf (ngx_conf_t *cf, void *parent, void *child)
 
138
{
 
139
  ngx_http_secure_download_loc_conf_t *prev = parent;
 
140
  ngx_http_secure_download_loc_conf_t *conf = child;
 
141
 
 
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, "");
 
145
  
 
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;
 
151
      }
 
152
  }
 
153
  
 
154
  return NGX_CONF_OK;
 
155
}
 
156
 
 
157
static ngx_int_t ngx_http_secure_download_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) {
 
158
  unsigned timestamp;
 
159
  unsigned remaining_time = 0;
 
160
  ngx_http_secure_download_loc_conf_t *sdc;
 
161
  ngx_http_secure_download_split_uri_t sdsu;
 
162
  ngx_str_t rel_path;
 
163
  ngx_str_t secret;
 
164
  int value = 0;
 
165
  
 
166
  sdc = ngx_http_get_module_loc_conf(r, ngx_http_secure_download_module);
 
167
  if (sdc->enable != 1)
 
168
  {
 
169
      ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
 
170
          "securedownload: module not enabled");
 
171
      value = -3;
 
172
      goto finish;
 
173
  }
 
174
  
 
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!");
 
178
      value = -3;
 
179
      goto finish;
 
180
  }
 
181
 
 
182
  if (ngx_http_secure_download_split_uri(r, &sdsu) == NGX_ERROR)
 
183
  {
 
184
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "received an error from ngx_http_secure_download_split_uri", 0);
 
185
    value = -3;
 
186
    goto finish;
 
187
  }
 
188
 
 
189
  if (sscanf(sdsu.timestamp, "%08X", &timestamp) != 1)
 
190
  {
 
191
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "error in timestamp hex-dec conversion", 0);
 
192
    value = -3;
 
193
    goto finish;
 
194
  }
 
195
  
 
196
  remaining_time = timestamp - (unsigned) time(NULL);
 
197
  if ((int)remaining_time <= 0)
 
198
  {
 
199
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "expired timestamp", 0);
 
200
    value = -1;
 
201
    goto finish;
 
202
  }
 
203
  
 
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");
 
207
      value = -3;
 
208
      goto finish;
 
209
  }
 
210
  
 
211
  ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
 
212
    "securedownload: evaluated value of secret: \"%V\"", &secret);
 
213
    
 
214
  if (ngx_http_secure_download_check_hash(r, &sdsu, &secret) != NGX_OK)
 
215
  {
 
216
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "bad hash", 0);
 
217
    value = -2;
 
218
    goto finish;
 
219
  }
 
220
  
 
221
  rel_path.data = r->uri.data;
 
222
  rel_path.len = sdsu.path_len;
 
223
  
 
224
  finish: 
 
225
  
 
226
  v->not_found = 0;
 
227
  v->valid = 1;
 
228
  v->no_cacheable = 0;
 
229
  v->not_found = 0;
 
230
  if (value == 0)
 
231
  {
 
232
    v->data = ngx_pcalloc(r->pool, sizeof(char) * 12);
 
233
    if (v->data == NULL) {
 
234
        return NGX_ERROR;
 
235
    }
 
236
    v->len = (int) sprintf((char *)v->data, "%i", remaining_time);
 
237
    //printf("valid, %i\n", remaining_time);
 
238
  } else {
 
239
    v->data = ngx_pcalloc(r->pool, sizeof(char) * 3);
 
240
    if (v->data == NULL) {
 
241
        return NGX_ERROR;
 
242
    }
 
243
    v->len = (int) sprintf((char*)v->data, "%i", value);
 
244
    //printf("problem %i\n", value);
 
245
  }
 
246
  
 
247
  return NGX_OK;
 
248
}
 
249
 
 
250
//////////////////////
 
251
static char *
 
252
ngx_http_secure_download_compile_secret(ngx_conf_t *cf, ngx_http_secure_download_loc_conf_t *sdc)
 
253
{
 
254
 
 
255
    ngx_http_script_compile_t   sc;
 
256
    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
 
257
 
 
258
    sc.cf = cf;
 
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;
 
265
 
 
266
    if (ngx_http_script_compile(&sc) != NGX_OK) {
 
267
        return NGX_CONF_ERROR;
 
268
    }
 
269
 
 
270
    return NGX_CONF_OK;
 
271
}
 
272
 
 
273
static char *
 
274
ngx_http_secure_download_secret(ngx_conf_t *cf, void *post, void *data)
 
275
{
 
276
    ngx_http_secure_download_loc_conf_t *sdc =
 
277
            ngx_http_conf_get_module_loc_conf(cf, ngx_http_secure_download_module);
 
278
 
 
279
    return ngx_http_secure_download_compile_secret(cf, sdc);
 
280
}
 
281
////////////////////////
 
282
 
 
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)
 
284
{
 
285
  int i;
 
286
  unsigned char generated_hash[16];
 
287
  char hash[33];
 
288
  MHASH td;
 
289
  char *hash_data, *str;
 
290
  int data_len;
 
291
 
 
292
  static const char xtoc[] = "0123456789abcdef";
 
293
 
 
294
  /* rel_path_to_hash/secret/timestamp\0 */
 
295
 
 
296
  data_len = sdsu->path_to_hash_len + secret->len + 10;
 
297
 
 
298
  hash_data = malloc(data_len + 1);
 
299
  if (hash_data == NULL)
 
300
  {
 
301
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "error in allocating memory for string_to_hash.data", 0);
 
302
    return NGX_ERROR;
 
303
  }
 
304
 
 
305
  str = hash_data;
 
306
  memcpy(str, sdsu->path, sdsu->path_to_hash_len);
 
307
  str += sdsu->path_to_hash_len;
 
308
  *str++ = '/';
 
309
  memcpy(str, secret->data, secret->len);
 
310
  str += secret->len;
 
311
  *str++ = '/';
 
312
  memcpy(str, sdsu->timestamp, 8);
 
313
  str[8] = 0;
 
314
 
 
315
  td = mhash_init(MHASH_MD5);
 
316
 
 
317
  if (td == MHASH_FAILED)
 
318
  {
 
319
        free(hash_data);
 
320
    return NGX_ERROR;
 
321
  }
 
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);
 
325
 
 
326
  free(hash_data);
 
327
 
 
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];
 
331
  }
 
332
  
 
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);
 
336
 
 
337
  if(memcmp(hash, sdsu->md5, 32) != 0) {
 
338
          ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "hash mismatch", 0);           
 
339
          return NGX_ERROR;
 
340
  }
 
341
 
 
342
  return NGX_OK;
 
343
}
 
344
 
 
345
static ngx_int_t ngx_http_secure_download_split_uri(ngx_http_request_t *r, ngx_http_secure_download_split_uri_t *sdsu)
 
346
{
 
347
  int md5_len = 0;
 
348
  int tstamp_len = 0;
 
349
  int len = r->uri.len;
 
350
  const char *uri = (char*)r->uri.data;
 
351
 
 
352
  ngx_http_secure_download_loc_conf_t *sdc = ngx_http_get_module_loc_conf(r, ngx_http_secure_download_module);
 
353
 
 
354
  while(len && uri[--len] != '/')
 
355
          ++tstamp_len;
 
356
  if(tstamp_len != 8) {
 
357
          ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "timestamp size mismatch: %d", tstamp_len);
 
358
          return NGX_ERROR;
 
359
  }
 
360
  sdsu->timestamp = uri + len + 1;
 
361
 
 
362
  while(len && uri[--len] != '/')
 
363
          ++md5_len;
 
364
  if(md5_len != 32) {
 
365
          ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "md5 size mismatch: %d", md5_len);
 
366
          return NGX_ERROR;
 
367
  }
 
368
  sdsu->md5 = uri + len + 1;
 
369
 
 
370
  if(len == 0) {
 
371
          ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "bad path", 0);
 
372
          return NGX_ERROR;
 
373
  }
 
374
 
 
375
  sdsu->path = uri;
 
376
  sdsu->path_len = len;
 
377
 
 
378
  if(sdc->path_mode == FOLDER_MODE) {
 
379
          while(len && uri[--len] != '/');
 
380
  }
 
381
  sdsu->path_to_hash_len = len;
 
382
 
 
383
  return NGX_OK;
 
384
}
 
385
 
 
386
static ngx_int_t
 
387
ngx_http_secure_download_add_variables(ngx_conf_t *cf)
 
388
{
 
389
    ngx_http_variable_t  *var;
 
390
 
 
391
    var = ngx_http_add_variable(cf, &ngx_http_secure_download, NGX_HTTP_VAR_NOHASH);
 
392
    if (var == NULL) {
 
393
        return NGX_ERROR;
 
394
    }
 
395
 
 
396
    var->get_handler = ngx_http_secure_download_variable;
 
397
 
 
398
    return NGX_OK;
 
399
}
 
400