new ngx_http_secure_link_module with secure_link, secure_link_md5, and
secure_link_expires
Igor Sysoev
11 years ago
9 | 9 | |
10 | 10 | static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, |
11 | 11 | u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); |
12 | static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, | |
13 | const u_char *basis); | |
12 | 14 | |
13 | 15 | |
14 | 16 | void |
1094 | 1096 | ngx_int_t |
1095 | 1097 | ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src) |
1096 | 1098 | { |
1097 | size_t len; | |
1098 | u_char *d, *s; | |
1099 | 1099 | static u_char basis64[] = { |
1100 | 1100 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, |
1101 | 1101 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, |
1116 | 1116 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 |
1117 | 1117 | }; |
1118 | 1118 | |
1119 | return ngx_decode_base64_internal(dst, src, basis64); | |
1120 | } | |
1121 | ||
1122 | ||
1123 | ngx_int_t | |
1124 | ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src) | |
1125 | { | |
1126 | static u_char basis64[] = { | |
1127 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1128 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1129 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, | |
1130 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, | |
1131 | 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
1132 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63, | |
1133 | 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
1134 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, | |
1135 | ||
1136 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1137 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1138 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1139 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1140 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1141 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1142 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, | |
1143 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 | |
1144 | }; | |
1145 | ||
1146 | return ngx_decode_base64_internal(dst, src, basis64); | |
1147 | } | |
1148 | ||
1149 | ||
1150 | static ngx_int_t | |
1151 | ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis) | |
1152 | { | |
1153 | size_t len; | |
1154 | u_char *d, *s; | |
1155 | ||
1119 | 1156 | for (len = 0; len < src->len; len++) { |
1120 | 1157 | if (src->data[len] == '=') { |
1121 | 1158 | break; |
1122 | 1159 | } |
1123 | 1160 | |
1124 | if (basis64[src->data[len]] == 77) { | |
1161 | if (basis[src->data[len]] == 77) { | |
1125 | 1162 | return NGX_ERROR; |
1126 | 1163 | } |
1127 | 1164 | } |
1134 | 1171 | d = dst->data; |
1135 | 1172 | |
1136 | 1173 | while (len > 3) { |
1137 | *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4); | |
1138 | *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2); | |
1139 | *d++ = (u_char) (basis64[s[2]] << 6 | basis64[s[3]]); | |
1174 | *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); | |
1175 | *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); | |
1176 | *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]); | |
1140 | 1177 | |
1141 | 1178 | s += 4; |
1142 | 1179 | len -= 4; |
1143 | 1180 | } |
1144 | 1181 | |
1145 | 1182 | if (len > 1) { |
1146 | *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4); | |
1183 | *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); | |
1147 | 1184 | } |
1148 | 1185 | |
1149 | 1186 | if (len > 2) { |
1150 | *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2); | |
1187 | *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); | |
1151 | 1188 | } |
1152 | 1189 | |
1153 | 1190 | dst->len = d - dst->data; |
177 | 177 | |
178 | 178 | void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); |
179 | 179 | ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); |
180 | ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src); | |
180 | 181 | |
181 | 182 | uint32_t ngx_utf8_decode(u_char **p, size_t n); |
182 | 183 | size_t ngx_utf8_length(u_char *p, size_t n); |
10 | 10 | |
11 | 11 | |
12 | 12 | typedef struct { |
13 | ngx_str_t secret; | |
13 | ngx_http_complex_value_t *variable; | |
14 | ngx_http_complex_value_t *md5; | |
15 | ngx_str_t secret; | |
16 | ngx_flag_t expires; | |
14 | 17 | } ngx_http_secure_link_conf_t; |
15 | 18 | |
16 | 19 | |
20 | typedef struct { | |
21 | ngx_str_t expires; | |
22 | } ngx_http_secure_link_ctx_t; | |
23 | ||
24 | ||
25 | static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r, | |
26 | ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v, | |
27 | uintptr_t data); | |
28 | static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r, | |
29 | ngx_http_variable_value_t *v, uintptr_t data); | |
17 | 30 | static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf); |
18 | 31 | static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, |
19 | 32 | void *child); |
21 | 34 | |
22 | 35 | |
23 | 36 | static ngx_command_t ngx_http_secure_link_commands[] = { |
37 | ||
38 | { ngx_string("secure_link"), | |
39 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
40 | ngx_http_set_comlex_value_slot, | |
41 | NGX_HTTP_LOC_CONF_OFFSET, | |
42 | offsetof(ngx_http_secure_link_conf_t, variable), | |
43 | NULL }, | |
44 | ||
45 | { ngx_string("secure_link_md5"), | |
46 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
47 | ngx_http_set_comlex_value_slot, | |
48 | NGX_HTTP_LOC_CONF_OFFSET, | |
49 | offsetof(ngx_http_secure_link_conf_t, md5), | |
50 | NULL }, | |
51 | ||
52 | { ngx_string("secure_link_expires"), | |
53 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
54 | ngx_conf_set_flag_slot, | |
55 | NGX_HTTP_LOC_CONF_OFFSET, | |
56 | offsetof(ngx_http_secure_link_conf_t, expires), | |
57 | NULL }, | |
24 | 58 | |
25 | 59 | { ngx_string("secure_link_secret"), |
26 | 60 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
64 | 98 | }; |
65 | 99 | |
66 | 100 | |
67 | static ngx_str_t ngx_http_secure_link = ngx_string("secure_link"); | |
101 | static ngx_str_t ngx_http_secure_link_name = ngx_string("secure_link"); | |
102 | static ngx_str_t ngx_http_secure_link_expires_name = | |
103 | ngx_string("secure_link_expires"); | |
68 | 104 | |
69 | 105 | |
70 | 106 | static ngx_int_t |
71 | 107 | ngx_http_secure_link_variable(ngx_http_request_t *r, |
72 | ngx_http_variable_value_t *v, uintptr_t data) | |
73 | { | |
74 | u_char *p, *start, *end, *last; | |
75 | size_t len; | |
76 | ngx_int_t n; | |
77 | ngx_uint_t i; | |
78 | ngx_md5_t md5; | |
108 | ngx_http_variable_value_t *v, uintptr_t data) | |
109 | { | |
110 | u_char *p, *last; | |
111 | ngx_str_t val, hash; | |
112 | time_t expires; | |
113 | ngx_md5_t md5; | |
114 | ngx_http_secure_link_ctx_t *ctx; | |
79 | 115 | ngx_http_secure_link_conf_t *conf; |
80 | u_char hash[16]; | |
116 | u_char hash_buf[16], md5_buf[16]; | |
81 | 117 | |
82 | 118 | conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module); |
83 | 119 | |
84 | if (conf->secret.len == 0) { | |
85 | goto not_found; | |
86 | } | |
120 | if (conf->secret.len) { | |
121 | return ngx_http_secure_link_old_variable(r, conf, v, data); | |
122 | } | |
123 | ||
124 | if (conf->variable == NULL || conf->md5 == NULL) { | |
125 | goto not_found; | |
126 | } | |
127 | ||
128 | if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) { | |
129 | return NGX_ERROR; | |
130 | } | |
131 | ||
132 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
133 | "secure link: \"%V\"", &val); | |
134 | ||
135 | last = val.data + val.len; | |
136 | ||
137 | p = ngx_strlchr(val.data, last, ','); | |
138 | expires = 0; | |
139 | ||
140 | if (p) { | |
141 | val.len = p++ - val.data; | |
142 | ||
143 | if (conf->expires) { | |
144 | expires = ngx_atotm(p, last - p); | |
145 | if (expires <= 0) { | |
146 | goto not_found; | |
147 | } | |
148 | ||
149 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t)); | |
150 | if (ctx == NULL) { | |
151 | return NGX_ERROR; | |
152 | } | |
153 | ||
154 | ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module); | |
155 | ||
156 | ctx->expires.len = last - p; | |
157 | ctx->expires.data = p; | |
158 | } | |
159 | } | |
160 | ||
161 | if (val.len > 24) { | |
162 | goto not_found; | |
163 | } | |
164 | ||
165 | hash.len = 16; | |
166 | hash.data = hash_buf; | |
167 | ||
168 | if (ngx_decode_base64url(&hash, &val) != NGX_OK) { | |
169 | goto not_found; | |
170 | } | |
171 | ||
172 | if (hash.len != 16) { | |
173 | goto not_found; | |
174 | } | |
175 | ||
176 | if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) { | |
177 | return NGX_ERROR; | |
178 | } | |
179 | ||
180 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
181 | "secure link md5: \"%V\"", &val); | |
182 | ||
183 | ngx_md5_init(&md5); | |
184 | ngx_md5_update(&md5, val.data, val.len); | |
185 | ngx_md5_final(md5_buf, &md5); | |
186 | ||
187 | if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) { | |
188 | goto not_found; | |
189 | } | |
190 | ||
191 | v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1"); | |
192 | v->len = 1; | |
193 | v->valid = 1; | |
194 | v->no_cacheable = 0; | |
195 | v->not_found = 0; | |
196 | ||
197 | return NGX_OK; | |
198 | ||
199 | not_found: | |
200 | ||
201 | v->not_found = 1; | |
202 | ||
203 | return NGX_OK; | |
204 | } | |
205 | ||
206 | ||
207 | static ngx_int_t | |
208 | ngx_http_secure_link_old_variable(ngx_http_request_t *r, | |
209 | ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v, | |
210 | uintptr_t data) | |
211 | { | |
212 | u_char *p, *start, *end, *last; | |
213 | size_t len; | |
214 | ngx_int_t n; | |
215 | ngx_uint_t i; | |
216 | ngx_md5_t md5; | |
217 | u_char hash[16]; | |
87 | 218 | |
88 | 219 | p = &r->unparsed_uri.data[1]; |
89 | 220 | last = r->unparsed_uri.data + r->unparsed_uri.len; |
144 | 275 | } |
145 | 276 | |
146 | 277 | |
278 | static ngx_int_t | |
279 | ngx_http_secure_link_expires_variable(ngx_http_request_t *r, | |
280 | ngx_http_variable_value_t *v, uintptr_t data) | |
281 | { | |
282 | ngx_http_secure_link_ctx_t *ctx; | |
283 | ||
284 | ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module); | |
285 | ||
286 | if (ctx) { | |
287 | v->len = ctx->expires.len; | |
288 | v->valid = 1; | |
289 | v->no_cacheable = 0; | |
290 | v->not_found = 0; | |
291 | v->data = ctx->expires.data; | |
292 | ||
293 | } else { | |
294 | v->not_found = 1; | |
295 | } | |
296 | ||
297 | return NGX_OK; | |
298 | } | |
299 | ||
300 | ||
147 | 301 | static void * |
148 | 302 | ngx_http_secure_link_create_conf(ngx_conf_t *cf) |
149 | 303 | { |
157 | 311 | /* |
158 | 312 | * set by ngx_pcalloc(): |
159 | 313 | * |
160 | * conf->secret = { 0, NULL } | |
314 | * conf->variable = NULL; | |
315 | * conf->md5 = NULL; | |
316 | * conf->secret = { 0, NULL }; | |
161 | 317 | */ |
318 | ||
319 | conf->expires = NGX_CONF_UNSET; | |
162 | 320 | |
163 | 321 | return conf; |
164 | 322 | } |
172 | 330 | |
173 | 331 | ngx_conf_merge_str_value(conf->secret, prev->secret, ""); |
174 | 332 | |
333 | if (conf->variable == NULL) { | |
334 | conf->variable = prev->variable; | |
335 | } | |
336 | ||
337 | if (conf->md5 == NULL) { | |
338 | conf->md5 = prev->md5; | |
339 | } | |
340 | ||
341 | ngx_conf_merge_value(conf->expires, prev->expires, 0); | |
342 | ||
175 | 343 | return NGX_CONF_OK; |
176 | 344 | } |
177 | 345 | |
181 | 349 | { |
182 | 350 | ngx_http_variable_t *var; |
183 | 351 | |
184 | var = ngx_http_add_variable(cf, &ngx_http_secure_link, NGX_HTTP_VAR_NOHASH); | |
352 | var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0); | |
185 | 353 | if (var == NULL) { |
186 | 354 | return NGX_ERROR; |
187 | 355 | } |
188 | 356 | |
189 | 357 | var->get_handler = ngx_http_secure_link_variable; |
190 | 358 | |
191 | return NGX_OK; | |
192 | } | |
359 | var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0); | |
360 | if (var == NULL) { | |
361 | return NGX_ERROR; | |
362 | } | |
363 | ||
364 | var->get_handler = ngx_http_secure_link_expires_variable; | |
365 | ||
366 | return NGX_OK; | |
367 | } |
210 | 210 | } |
211 | 211 | |
212 | 212 | |
213 | char * | |
214 | ngx_http_set_comlex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
215 | { | |
216 | char *p = conf; | |
217 | ||
218 | ngx_str_t *value; | |
219 | ngx_http_complex_value_t **cv; | |
220 | ngx_http_compile_complex_value_t ccv; | |
221 | ||
222 | cv = (ngx_http_complex_value_t **) (p + cmd->offset); | |
223 | ||
224 | if (*cv != NULL) { | |
225 | return "duplicate"; | |
226 | } | |
227 | ||
228 | *cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); | |
229 | if (*cv == NULL) { | |
230 | return NGX_CONF_ERROR; | |
231 | } | |
232 | ||
233 | value = cf->args->elts; | |
234 | ||
235 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); | |
236 | ||
237 | ccv.cf = cf; | |
238 | ccv.value = &value[1]; | |
239 | ccv.complex_value = *cv; | |
240 | ||
241 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { | |
242 | return NGX_CONF_ERROR; | |
243 | } | |
244 | ||
245 | return NGX_CONF_OK; | |
246 | } | |
247 | ||
248 | ||
213 | 249 | ngx_int_t |
214 | 250 | ngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates) |
215 | 251 | { |
206 | 206 | ngx_int_t ngx_http_complex_value(ngx_http_request_t *r, |
207 | 207 | ngx_http_complex_value_t *val, ngx_str_t *value); |
208 | 208 | ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv); |
209 | char * ngx_http_set_comlex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
210 | void *conf); | |
211 | ||
209 | 212 | |
210 | 213 | ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r, |
211 | 214 | ngx_array_t *predicates); |