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 /* plugin config for all request/connections */
14
15 typedef struct {
16 buffer *cookie_name;
17 buffer *cookie_domain;
18 unsigned int cookie_max_age;
19 } plugin_config;
20
21 typedef struct {
22 PLUGIN_DATA;
23
24 plugin_config **config_storage;
25
26 plugin_config conf;
27 } plugin_data;
28
29 /* init the plugin data */
INIT_FUNC(mod_usertrack_init)30 INIT_FUNC(mod_usertrack_init) {
31 plugin_data *p;
32
33 p = calloc(1, sizeof(*p));
34
35 return p;
36 }
37
38 /* detroy the plugin data */
FREE_FUNC(mod_usertrack_free)39 FREE_FUNC(mod_usertrack_free) {
40 plugin_data *p = p_d;
41
42 UNUSED(srv);
43
44 if (!p) return HANDLER_GO_ON;
45
46 if (p->config_storage) {
47 size_t i;
48 for (i = 0; i < srv->config_context->used; i++) {
49 plugin_config *s = p->config_storage[i];
50
51 buffer_free(s->cookie_name);
52 buffer_free(s->cookie_domain);
53
54 free(s);
55 }
56 free(p->config_storage);
57 }
58
59 free(p);
60
61 return HANDLER_GO_ON;
62 }
63
64 /* handle plugin config and check values */
65
SETDEFAULTS_FUNC(mod_usertrack_set_defaults)66 SETDEFAULTS_FUNC(mod_usertrack_set_defaults) {
67 plugin_data *p = p_d;
68 size_t i = 0;
69
70 config_values_t cv[] = {
71 { "usertrack.cookie-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
72 { "usertrack.cookie-max-age", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
73 { "usertrack.cookie-domain", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
74
75 { "usertrack.cookiename", NULL, T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_CONNECTION },
76 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
77 };
78
79 if (!p) return HANDLER_ERROR;
80
81 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
82
83 for (i = 0; i < srv->config_context->used; i++) {
84 plugin_config *s;
85
86 s = calloc(1, sizeof(plugin_config));
87 s->cookie_name = buffer_init();
88 s->cookie_domain = buffer_init();
89 s->cookie_max_age = 0;
90
91 cv[0].destination = s->cookie_name;
92 cv[1].destination = &(s->cookie_max_age);
93 cv[2].destination = s->cookie_domain;
94
95 p->config_storage[i] = s;
96
97 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
98 return HANDLER_ERROR;
99 }
100
101 if (buffer_is_empty(s->cookie_name)) {
102 buffer_copy_string_len(s->cookie_name, CONST_STR_LEN("TRACKID"));
103 } else {
104 size_t j;
105 for (j = 0; j < s->cookie_name->used - 1; j++) {
106 char c = s->cookie_name->ptr[j] | 32;
107 if (c < 'a' || c > 'z') {
108 log_error_write(srv, __FILE__, __LINE__, "sb",
109 "invalid character in usertrack.cookie-name:",
110 s->cookie_name);
111
112 return HANDLER_ERROR;
113 }
114 }
115 }
116
117 if (!buffer_is_empty(s->cookie_domain)) {
118 size_t j;
119 for (j = 0; j < s->cookie_domain->used - 1; j++) {
120 char c = s->cookie_domain->ptr[j];
121 if (c <= 32 || c >= 127 || c == '"' || c == '\\') {
122 log_error_write(srv, __FILE__, __LINE__, "sb",
123 "invalid character in usertrack.cookie-domain:",
124 s->cookie_domain);
125
126 return HANDLER_ERROR;
127 }
128 }
129 }
130 }
131
132 return HANDLER_GO_ON;
133 }
134
135 #define PATCH(x) \
136 p->conf.x = s->x;
mod_usertrack_patch_connection(server * srv,connection * con,plugin_data * p)137 static int mod_usertrack_patch_connection(server *srv, connection *con, plugin_data *p) {
138 size_t i, j;
139 plugin_config *s = p->config_storage[0];
140
141 PATCH(cookie_name);
142 PATCH(cookie_domain);
143 PATCH(cookie_max_age);
144
145 /* skip the first, the global context */
146 for (i = 1; i < srv->config_context->used; i++) {
147 data_config *dc = (data_config *)srv->config_context->data[i];
148 s = p->config_storage[i];
149
150 /* condition didn't match */
151 if (!config_check_cond(srv, con, dc)) continue;
152
153 /* merge config */
154 for (j = 0; j < dc->value->used; j++) {
155 data_unset *du = dc->value->data[j];
156
157 if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-name"))) {
158 PATCH(cookie_name);
159 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-max-age"))) {
160 PATCH(cookie_max_age);
161 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-domain"))) {
162 PATCH(cookie_domain);
163 }
164 }
165 }
166
167 return 0;
168 }
169 #undef PATCH
170
URIHANDLER_FUNC(mod_usertrack_uri_handler)171 URIHANDLER_FUNC(mod_usertrack_uri_handler) {
172 plugin_data *p = p_d;
173 data_string *ds;
174 unsigned char h[16];
175 li_MD5_CTX Md5Ctx;
176 char hh[32];
177
178 if (con->uri.path->used == 0) return HANDLER_GO_ON;
179
180 mod_usertrack_patch_connection(srv, con, p);
181
182 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
183 char *g;
184 /* we have a cookie, does it contain a valid name ? */
185
186 /* parse the cookie
187 *
188 * check for cookiename + (WS | '=')
189 *
190 */
191
192 if (NULL != (g = strstr(ds->value->ptr, p->conf.cookie_name->ptr))) {
193 char *nc;
194
195 /* skip WS */
196 for (nc = g + p->conf.cookie_name->used-1; *nc == ' ' || *nc == '\t'; nc++);
197
198 if (*nc == '=') {
199 /* ok, found the key of our own cookie */
200
201 if (strlen(nc) > 32) {
202 /* i'm lazy */
203 return HANDLER_GO_ON;
204 }
205 }
206 }
207 }
208
209 /* set a cookie */
210 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
211 ds = data_response_init();
212 }
213 buffer_copy_string_len(ds->key, CONST_STR_LEN("Set-Cookie"));
214 buffer_copy_string_buffer(ds->value, p->conf.cookie_name);
215 buffer_append_string_len(ds->value, CONST_STR_LEN("="));
216
217
218 /* taken from mod_auth.c */
219
220 /* generate shared-secret */
221 li_MD5_Init(&Md5Ctx);
222 li_MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1);
223 li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1);
224
225 /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
226 LI_ltostr(hh, srv->cur_ts);
227 li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
228 li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
229 LI_ltostr(hh, rand());
230 li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
231
232 li_MD5_Final(h, &Md5Ctx);
233
234 buffer_append_string_encoded(ds->value, (char *)h, 16, ENCODING_HEX);
235 buffer_append_string_len(ds->value, CONST_STR_LEN("; Path=/"));
236 buffer_append_string_len(ds->value, CONST_STR_LEN("; Version=1"));
237
238 if (!buffer_is_empty(p->conf.cookie_domain)) {
239 buffer_append_string_len(ds->value, CONST_STR_LEN("; Domain="));
240 buffer_append_string_encoded(ds->value, CONST_BUF_LEN(p->conf.cookie_domain), ENCODING_REL_URI);
241 }
242
243 if (p->conf.cookie_max_age) {
244 buffer_append_string_len(ds->value, CONST_STR_LEN("; max-age="));
245 buffer_append_long(ds->value, p->conf.cookie_max_age);
246 }
247
248 array_insert_unique(con->response.headers, (data_unset *)ds);
249
250 return HANDLER_GO_ON;
251 }
252
253 /* this function is called at dlopen() time and inits the callbacks */
254
255 int mod_usertrack_plugin_init(plugin *p);
mod_usertrack_plugin_init(plugin * p)256 int mod_usertrack_plugin_init(plugin *p) {
257 p->version = LIGHTTPD_VERSION_ID;
258 p->name = buffer_init_string("usertrack");
259
260 p->init = mod_usertrack_init;
261 p->handle_uri_clean = mod_usertrack_uri_handler;
262 p->set_defaults = mod_usertrack_set_defaults;
263 p->cleanup = mod_usertrack_free;
264
265 p->data = NULL;
266
267 return 0;
268 }
269