1
2 #include <netinet/in.h>
3 #include <arpa/inet.h>
4
5 #include "httpd.h"
6 #include "http_config.h"
7 #include "http_log.h"
8 #include "http_core.h"
9 #include "http_request.h"
10 #include "http_protocol.h"
11 #include "util_md5.h"
12 #include "sha2.h"
13
14 #ifdef APACHE13
15 #include "ap_compat.h"
16 #else
17 #define UUID_SUBS 2
18 #include "apr_lib.h"
19 #include "apr_strings.h"
20 #include "apr_uuid.h"
21 #include "apr_base64.h"
22 #ifndef APACHE22
23 #include "pcreposix.h"
24 #else
25 #include "ap22_compat.h"
26 #include "ap_regex.h"
27 #endif
28 #endif
29
30 #define AUTH_COOKIE_NAME "auth_tkt"
31 #define BACK_ARG_NAME "back"
32 #define DEFAULT_DIGEST_TYPE "MD5"
33 #define MD5_DIGEST_SZ 32
34 #define TSTAMP_SZ 8
35 #define SEPARATOR '!'
36 #define SEPARATOR_HEX "%21"
37 #define REMOTE_USER_ENV "REMOTE_USER"
38 #define REMOTE_USER_DATA_ENV "REMOTE_USER_DATA"
39 #define REMOTE_USER_TOKENS_ENV "REMOTE_USER_TOKENS"
40 #define DEFAULT_TIMEOUT_SEC 7200
41 #define DEFAULT_GUEST_USER "guest"
42 #define QUERY_SEPARATOR ';'
43
44 #define FORCE_REFRESH 1
45 #define CHECK_REFRESH 0
46
47 #define TKT_AUTH_VERSION "2.1.0"
48
49 /* ----------------------------------------------------------------------- */
50 /* Per-directory configuration */
51 typedef struct {
52 char *directory;
53 char *login_url;
54 char *timeout_url;
55 char *post_timeout_url;
56 char *unauth_url;
57 char *auth_domain;
58 int cookie_expires;
59 char *auth_cookie_name;
60 char *back_cookie_name;
61 char *back_arg_name;
62 apr_array_header_t *auth_token;
63 int ignore_ip;
64 int require_ssl;
65 int secure_cookie;
66 int timeout_sec;
67 double timeout_refresh;
68 int guest_login;
69 int guest_cookie;
70 char *guest_user;
71 int guest_fallback;
72 int debug;
73 const char *query_separator;
74 } auth_tkt_dir_conf;
75
76 /* Per-server configuration */
77 typedef struct {
78 const char *secret;
79 const char *old_secret;
80 const char *digest_type;
81 int digest_sz;
82 char *docroot;
83 } auth_tkt_serv_conf;
84
85 typedef struct auth_tkt_struct {
86 char *uid;
87 char *tokens;
88 char *user_data;
89 unsigned int timestamp;
90 } auth_tkt;
91
92 typedef struct {
93 request_rec *r;
94 char *cookie;
95 char *cookie_name;
96 } cookie_res;
97
98 /* ----------------------------------------------------------------------- */
99 /* Initializer */
100 #ifdef APACHE13
101 void
auth_tkt_version(server_rec * s,pool * p)102 auth_tkt_version(server_rec *s, pool *p)
103 {
104 ap_add_version_component("mod_auth_tkt/" TKT_AUTH_VERSION);
105 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
106 "mod_auth_tkt: version %s", TKT_AUTH_VERSION);
107 }
108
109 #else
110 static int
auth_tkt_version(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)111 auth_tkt_version(apr_pool_t *p,
112 apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
113 {
114 ap_add_version_component(p, "mod_auth_tkt/" TKT_AUTH_VERSION);
115 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
116 "mod_auth_tkt: version %s", TKT_AUTH_VERSION);
117 return DECLINED;
118 }
119 #endif
120
121 /* Create per-dir config structures */
122 static void *
create_auth_tkt_config(apr_pool_t * p,char * path)123 create_auth_tkt_config(apr_pool_t *p, char* path)
124 {
125 auth_tkt_dir_conf *conf = apr_palloc(p, sizeof(*conf));
126
127 conf->directory = path;
128 conf->login_url = NULL;
129 conf->timeout_url = NULL;
130 conf->post_timeout_url = NULL;
131 conf->unauth_url = NULL;
132 conf->auth_domain = NULL;
133 conf->cookie_expires = -1;
134 conf->auth_token = apr_array_make(p, 0, sizeof (char *));
135 conf->auth_cookie_name = AUTH_COOKIE_NAME;
136 conf->back_cookie_name = NULL;
137 conf->back_arg_name = BACK_ARG_NAME;
138 conf->ignore_ip = -1;
139 conf->require_ssl = -1;
140 conf->secure_cookie = -1;
141 conf->timeout_sec = -1;
142 conf->timeout_refresh = .5;
143 conf->guest_login = -1;
144 conf->guest_cookie = -1;
145 conf->guest_user = NULL;
146 conf->guest_fallback = -1;
147 conf->debug = -1;
148 conf->query_separator = (char *)QUERY_SEPARATOR;
149 return conf;
150 }
151
152 /* Merge per-dir config structures */
153 static void *
merge_auth_tkt_config(apr_pool_t * p,void * parent_dirv,void * subdirv)154 merge_auth_tkt_config(apr_pool_t *p, void* parent_dirv, void* subdirv)
155 {
156 auth_tkt_dir_conf *parent = (auth_tkt_dir_conf *) parent_dirv;
157 auth_tkt_dir_conf *subdir = (auth_tkt_dir_conf *) subdirv;
158 auth_tkt_dir_conf *conf = apr_palloc(p, sizeof(*conf));
159
160 conf->directory = (subdir->directory) ? subdir->directory : parent->directory;
161 conf->login_url = (subdir->login_url) ? subdir->login_url : parent->login_url;
162 conf->timeout_url = (subdir->timeout_url) ? subdir->timeout_url : parent->timeout_url;
163 conf->post_timeout_url = (subdir->post_timeout_url) ? subdir->post_timeout_url : parent->post_timeout_url;
164 conf->unauth_url = (subdir->unauth_url) ? subdir->unauth_url : parent->unauth_url;
165 conf->auth_domain = (subdir->auth_domain) ? subdir->auth_domain : parent->auth_domain;
166 conf->cookie_expires = (subdir->cookie_expires >= 0) ? subdir->cookie_expires : parent->cookie_expires;
167 conf->auth_token = (subdir->auth_token->nelts > 0) ? subdir->auth_token : parent->auth_token;
168 conf->auth_cookie_name = (subdir->auth_cookie_name) ? subdir->auth_cookie_name : parent->auth_cookie_name;
169 conf->back_cookie_name = (subdir->back_cookie_name) ? subdir->back_cookie_name : parent->back_cookie_name;
170 conf->back_arg_name = (subdir->back_arg_name) ? subdir->back_arg_name : parent->back_arg_name;
171 conf->ignore_ip = (subdir->ignore_ip >= 0) ? subdir->ignore_ip : parent->ignore_ip;
172 conf->require_ssl = (subdir->require_ssl >= 0) ? subdir->require_ssl : parent->require_ssl;
173 conf->secure_cookie = (subdir->secure_cookie >= 0) ? subdir->secure_cookie : parent->secure_cookie;
174 conf->timeout_sec = (subdir->timeout_sec >= 0) ? subdir->timeout_sec : parent->timeout_sec;
175 conf->timeout_refresh = (subdir->timeout_refresh >= 0) ? subdir->timeout_refresh : parent->timeout_refresh;
176 conf->guest_login = (subdir->guest_login >= 0) ? subdir->guest_login : parent->guest_login;
177 conf->guest_cookie = (subdir->guest_cookie >= 0) ? subdir->guest_cookie : parent->guest_cookie;
178 conf->guest_user = (subdir->guest_user) ? subdir->guest_user : parent->guest_user;
179 conf->guest_fallback = (subdir->guest_fallback >= 0) ? subdir->guest_fallback : parent->guest_fallback;
180 conf->debug = (subdir->debug >= 0) ? subdir->debug : parent->debug;
181 conf->query_separator = (subdir->query_separator) ? subdir->query_separator : parent->query_separator;
182
183 return conf;
184 }
185
186 /* Create per-server config structures */
187 static void *
create_auth_tkt_serv_config(apr_pool_t * p,server_rec * s)188 create_auth_tkt_serv_config(apr_pool_t *p, server_rec* s)
189 {
190 auth_tkt_serv_conf *sconf = apr_palloc(p, sizeof(*sconf));
191 sconf->secret = NULL;
192 sconf->old_secret = NULL;
193 sconf->digest_type = NULL;
194 sconf->digest_sz = 0;
195 return sconf;
196 }
197
198 /* Merge per-server config structures */
199 static void *
merge_auth_tkt_serv_config(apr_pool_t * p,void * parent_dirv,void * subdirv)200 merge_auth_tkt_serv_config(apr_pool_t *p, void* parent_dirv, void* subdirv)
201 {
202 auth_tkt_serv_conf *parent = (auth_tkt_serv_conf *) parent_dirv;
203 auth_tkt_serv_conf *subdir = (auth_tkt_serv_conf *) subdirv;
204 auth_tkt_serv_conf *sconf = apr_palloc(p, sizeof(*sconf));
205
206 sconf->secret = (subdir->secret) ? subdir->secret : parent->secret;
207 sconf->old_secret = (subdir->old_secret) ? subdir->old_secret : parent->old_secret;
208 sconf->digest_type = (subdir->digest_type) ? subdir->digest_type : parent->digest_type;
209 sconf->digest_sz = (subdir->digest_sz) ? subdir->digest_sz : parent->digest_sz;
210 return sconf;
211 }
212
213 /* ----------------------------------------------------------------------- */
214 /* Command-specific functions */
215
216 module AP_MODULE_DECLARE_DATA auth_tkt_module;
217
218 /* Loosely based on mod_expires */
219 static const char *
convert_to_seconds(cmd_parms * cmd,const char * param,int * seconds)220 convert_to_seconds (cmd_parms *cmd, const char *param, int *seconds)
221 {
222 int num, multiplier;
223 char unit;
224
225 if (apr_isdigit(param[0])) {
226 num = atoi(param);
227 }
228 else {
229 return "Bad time string - numeric expected.";
230 }
231
232 if (*seconds < 0) *seconds = 0;
233 multiplier = 1;
234
235 unit = param[strlen(param)-1];
236 if (apr_isalpha(unit)) {
237 if (unit == 's')
238 multiplier = 1;
239 else if (unit == 'm')
240 multiplier = 60;
241 else if (unit == 'h')
242 multiplier = 60 * 60;
243 else if (unit == 'd')
244 multiplier = 24 * 60 * 60;
245 else if (unit == 'w')
246 multiplier = 7 * 24 * 60 * 60;
247 else if (unit == 'M')
248 multiplier = 30 * 24 * 60 * 60;
249 else if (unit == 'y')
250 multiplier = 365 * 24 * 60 * 60;
251 else
252 return apr_psprintf(cmd->pool,
253 "Bad time string - unrecognised unit '%c'", unit);
254 }
255
256 *seconds += num * multiplier;
257
258 return NULL;
259 }
260
261 static const char *
set_auth_tkt_token(cmd_parms * cmd,void * cfg,const char * param)262 set_auth_tkt_token (cmd_parms *cmd, void *cfg, const char *param)
263 {
264 char **new;
265 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *) cfg;
266
267 new = (char **) apr_array_push(conf->auth_token);
268 *new = apr_pstrdup(cmd->pool, param);
269 return NULL;
270 }
271
272 static const char *
set_auth_tkt_timeout(cmd_parms * cmd,void * cfg,const char * param)273 set_auth_tkt_timeout (cmd_parms *cmd, void *cfg, const char *param)
274 {
275 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *)cfg;
276 int seconds = conf->timeout_sec;
277 const char *error;
278
279 /* Easy case - looks like all digits */
280 if (apr_isdigit(param[0]) && apr_isdigit(param[strlen(param) - 1])) {
281 seconds = atoi(param);
282 }
283
284 /* Harder case - convert units to seconds */
285 else {
286 error = convert_to_seconds(cmd, param, &seconds);
287 if (error) return(error);
288 }
289
290 if (seconds < 0) return ("Timeout must be positive");
291 if (seconds == INT_MAX) return ("Integer overflow or invalid number");
292
293 conf->timeout_sec = seconds;
294
295 return NULL;
296 }
297
298 static const char *
set_auth_tkt_timeout_min(cmd_parms * cmd,void * cfg,const char * param)299 set_auth_tkt_timeout_min (cmd_parms *cmd, void *cfg, const char *param)
300 {
301 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *)cfg;
302
303 int minutes = atoi(param);
304
305 if (minutes < 0) return ("Timeout must be positive");
306 if (minutes == INT_MAX) return ("Integer overflow or invalid number");
307
308 conf->timeout_sec = minutes * 60;
309
310 return NULL;
311 }
312
313 static const char *
set_auth_tkt_timeout_refresh(cmd_parms * cmd,void * cfg,const char * param)314 set_auth_tkt_timeout_refresh (cmd_parms *cmd, void *cfg, const char *param)
315 {
316 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *)cfg;
317
318 double refresh = atof(param);
319
320 if (refresh < 0 || refresh > 1)
321 return "Refresh flag must be between 0 and 1";
322
323 conf->timeout_refresh = refresh;
324
325 return NULL;
326 }
327
328 static const char *
setup_secret(cmd_parms * cmd,void * cfg,const char * param)329 setup_secret (cmd_parms *cmd, void *cfg, const char *param)
330 {
331 auth_tkt_serv_conf *sconf =
332 ap_get_module_config(cmd->server->module_config, &auth_tkt_module);
333 sconf->secret = param;
334 return NULL;
335 }
336
337 static const char *
setup_old_secret(cmd_parms * cmd,void * cfg,const char * param)338 setup_old_secret (cmd_parms *cmd, void *cfg, const char *param)
339 {
340 auth_tkt_serv_conf *sconf = ap_get_module_config(cmd->server->module_config,
341 &auth_tkt_module);
342 sconf->old_secret = param;
343 return NULL;
344 }
345
346 static const char *
setup_query_separator(cmd_parms * cmd,void * cfg,const char * param)347 setup_query_separator (cmd_parms *cmd, void *cfg, const char *param)
348 {
349 if (strcmp(param, ";") != 0 && strcmp(param, "&") != 0)
350 return "QuerySeparator must be either ';' or '&'.";
351 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *)cfg;
352 conf->query_separator = param;
353 return NULL;
354 }
355
356 void
setup_digest_sz(auth_tkt_serv_conf * sconf)357 setup_digest_sz (auth_tkt_serv_conf *sconf)
358 {
359 if (strcmp(sconf->digest_type, "MD5") == 0) {
360 sconf->digest_sz = MD5_DIGEST_SZ;
361 }
362 else if (strcmp(sconf->digest_type, "SHA256") == 0) {
363 sconf->digest_sz = SHA256_BLOCK_LENGTH;
364 }
365 else if (strcmp(sconf->digest_type, "SHA512") == 0) {
366 sconf->digest_sz = SHA512_BLOCK_LENGTH;
367 }
368 }
369
370 static const char *
setup_digest_type(cmd_parms * cmd,void * cfg,const char * param)371 setup_digest_type (cmd_parms *cmd, void *cfg, const char *param)
372 {
373 auth_tkt_serv_conf *sconf =
374 ap_get_module_config(cmd->server->module_config, &auth_tkt_module);
375
376 if (strcmp(param, "MD5") != 0 &&
377 strcmp(param, "SHA256") != 0 &&
378 strcmp(param, "SHA512") != 0)
379 return "Digest type must be one of: MD5 | SHA256 | SHA512.";
380
381 sconf->digest_type = param;
382 setup_digest_sz(sconf);
383
384 return NULL;
385 }
386
387 static const char *
set_cookie_expires(cmd_parms * cmd,void * cfg,const char * param)388 set_cookie_expires (cmd_parms *cmd, void *cfg, const char *param)
389 {
390 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *)cfg;
391 int seconds = conf->cookie_expires;
392 const char *error;
393
394 /* Easy case - looks like all digits */
395 if (apr_isdigit(param[0]) && apr_isdigit(param[strlen(param) - 1])) {
396 seconds = atoi(param);
397 }
398
399 /* Harder case - convert units to seconds */
400 else {
401 error = convert_to_seconds(cmd, param, &seconds);
402 if (error) return(error);
403 }
404
405 if (seconds < 0) return ("Expires must be positive");
406 if (seconds == INT_MAX) return ("Integer overflow or invalid number");
407
408 conf->cookie_expires = seconds;
409
410 return NULL;
411 }
412
413 static const char *
set_auth_tkt_debug(cmd_parms * cmd,void * cfg,const char * param)414 set_auth_tkt_debug (cmd_parms *cmd, void *cfg, const char *param)
415 {
416 auth_tkt_dir_conf *conf = (auth_tkt_dir_conf *)cfg;
417
418 int debug = atoi(param);
419
420 if (debug < 0) return ("Debug level must be positive");
421 if (debug == INT_MAX) return ("Integer overflow or invalid number");
422
423 conf->debug = debug;
424
425 return NULL;
426 }
427
428 /* Command table */
429 static const command_rec auth_tkt_cmds[] =
430 {
431 AP_INIT_TAKE1("TKTAuthLoginURL", ap_set_string_slot,
432 (void *)APR_OFFSETOF(auth_tkt_dir_conf, login_url),
433 OR_AUTHCFG, "URL to redirect to if authentication fails"),
434 AP_INIT_TAKE1("TKTAuthTimeoutURL", ap_set_string_slot,
435 (void *)APR_OFFSETOF(auth_tkt_dir_conf, timeout_url),
436 OR_AUTHCFG, "URL to redirect to if cookie times-out"),
437 AP_INIT_TAKE1("TKTAuthPostTimeoutURL", ap_set_string_slot,
438 (void *)APR_OFFSETOF(auth_tkt_dir_conf, post_timeout_url),
439 OR_AUTHCFG, "URL to redirect to if cookie times-out doing a POST"),
440 AP_INIT_TAKE1("TKTAuthUnauthURL", ap_set_string_slot,
441 (void *)APR_OFFSETOF(auth_tkt_dir_conf, unauth_url),
442 OR_AUTHCFG, "URL to redirect to if valid user without required token"),
443 AP_INIT_TAKE1("TKTAuthCookieName", ap_set_string_slot,
444 (void *)APR_OFFSETOF(auth_tkt_dir_conf, auth_cookie_name),
445 OR_AUTHCFG, "name to use for ticket cookie"),
446 AP_INIT_TAKE1("TKTAuthDomain", ap_set_string_slot,
447 (void *)APR_OFFSETOF(auth_tkt_dir_conf, auth_domain),
448 OR_AUTHCFG, "domain to use in cookies"),
449 #ifndef APACHE13
450 /* TKTAuthCookieExpires is not supported under Apache 1.3 */
451 AP_INIT_ITERATE("TKTAuthCookieExpires", set_cookie_expires,
452 (void *)APR_OFFSETOF(auth_tkt_dir_conf, cookie_expires),
453 OR_AUTHCFG, "cookie expiry period, in seconds or units [smhdwMy]"),
454 #endif
455 AP_INIT_TAKE1("TKTAuthBackCookieName", ap_set_string_slot,
456 (void *)APR_OFFSETOF(auth_tkt_dir_conf, back_cookie_name),
457 OR_AUTHCFG, "name to use for back cookie (default: none)"),
458 AP_INIT_TAKE1("TKTAuthBackArgName", ap_set_string_slot,
459 (void *)APR_OFFSETOF(auth_tkt_dir_conf, back_arg_name),
460 OR_AUTHCFG, "name to use for back url argument ('None' to not use)"),
461 AP_INIT_FLAG("TKTAuthIgnoreIP", ap_set_flag_slot,
462 (void *)APR_OFFSETOF(auth_tkt_dir_conf, ignore_ip),
463 OR_AUTHCFG, "whether to ignore remote IP address in ticket"),
464 AP_INIT_FLAG("TKTAuthRequireSSL", ap_set_flag_slot,
465 (void *)APR_OFFSETOF(auth_tkt_dir_conf, require_ssl),
466 OR_AUTHCFG, "whether to refuse non-HTTPS requests"),
467 AP_INIT_FLAG("TKTAuthCookieSecure", ap_set_flag_slot,
468 (void *)APR_OFFSETOF(auth_tkt_dir_conf, secure_cookie),
469 OR_AUTHCFG, "whether to set secure flag on ticket cookies"),
470 AP_INIT_ITERATE("TKTAuthToken", set_auth_tkt_token,
471 (void *)APR_OFFSETOF(auth_tkt_dir_conf, auth_token),
472 OR_AUTHCFG, "token required to access this area (NULL for none)"),
473 AP_INIT_ITERATE("TKTAuthTimeout", set_auth_tkt_timeout,
474 (void *)APR_OFFSETOF(auth_tkt_dir_conf, timeout_sec),
475 OR_AUTHCFG, "ticket inactivity timeout, in seconds or units [smhdwMy]"),
476 AP_INIT_TAKE1("TKTAuthTimeoutMin", set_auth_tkt_timeout_min,
477 NULL, OR_AUTHCFG, "ticket inactivity timeout, in minutes (deprecated)"),
478 AP_INIT_TAKE1("TKTAuthTimeoutRefresh", set_auth_tkt_timeout_refresh,
479 NULL, OR_AUTHCFG, "ticket timeout refresh flag (0-1)"),
480 AP_INIT_TAKE1("TKTAuthSecret", setup_secret,
481 NULL, RSRC_CONF, "secret key to use in digest"),
482 AP_INIT_TAKE1("TKTAuthSecretOld", setup_old_secret,
483 NULL, RSRC_CONF, "old/alternative secret key to check in digests"),
484 AP_INIT_TAKE1("TKTAuthDigestType", setup_digest_type,
485 NULL, RSRC_CONF, "digest type to use [MD5|SHA256|SHA512], default: MD5"),
486 AP_INIT_FLAG("TKTAuthGuestLogin", ap_set_flag_slot,
487 (void *)APR_OFFSETOF(auth_tkt_dir_conf, guest_login),
488 OR_AUTHCFG, "whether to log people in as guest if no other auth available"),
489 AP_INIT_FLAG("TKTAuthGuestCookie", ap_set_flag_slot,
490 (void *)APR_OFFSETOF(auth_tkt_dir_conf, guest_cookie),
491 OR_AUTHCFG, "whether to set a cookie when accepting guest users (default: off)"),
492 AP_INIT_TAKE1("TKTAuthGuestUser", ap_set_string_slot,
493 (void *)APR_OFFSETOF(auth_tkt_dir_conf, guest_user),
494 OR_AUTHCFG, "username to use for guest logins"),
495 AP_INIT_FLAG("TKTAuthGuestFallback", ap_set_flag_slot,
496 (void *)APR_OFFSETOF(auth_tkt_dir_conf, guest_fallback),
497 OR_AUTHCFG, "whether to fall back to guest on an expired ticket (default: off)"),
498 AP_INIT_ITERATE("TKTAuthDebug", set_auth_tkt_debug,
499 (void *)APR_OFFSETOF(auth_tkt_dir_conf, debug),
500 OR_AUTHCFG, "debug level (1-3, higher for more debug output)"),
501 AP_INIT_TAKE1("TKTAuthQuerySeparator", setup_query_separator,
502 (void *)APR_OFFSETOF(auth_tkt_dir_conf, query_separator),
503 OR_AUTHCFG, "Character used in query strings to separate arguments (default: ';')"),
504 {NULL},
505 };
506
507 /* ----------------------------------------------------------------------- */
508 /* Support functions */
509
510 /* Parse cookie. Returns 1 if valid, and details in *parsed; 0 if not */
511 static int
parse_ticket(request_rec * r,char ** magic,auth_tkt * parsed)512 parse_ticket(request_rec *r, char **magic, auth_tkt *parsed)
513 {
514 int sepidx, sep2idx;
515 char *ticket = *magic;
516 int len = strlen(ticket);
517 auth_tkt_serv_conf *sconf =
518 ap_get_module_config(r->server->module_config, &auth_tkt_module);
519 auth_tkt_dir_conf *conf =
520 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
521
522 /* For some reason (some clients?), tickets sometimes come in quoted */
523 if (ticket[len-1] == '"') ticket[len-1] = 0;
524 if (ticket[0] == '"') *magic = ++ticket;
525
526 /* Basic length check for min size */
527 if (len <= (sconf->digest_sz + TSTAMP_SZ))
528 return 0;
529
530 /* See if there is a uid/data separator */
531 sepidx = ap_ind(ticket, SEPARATOR);
532 if (sepidx == -1) {
533 /* Ticket either uri-escaped, base64-escaped, or bogus */
534 if (strstr(ticket, SEPARATOR_HEX)) {
535 ap_unescape_url(ticket);
536 sepidx = ap_ind(ticket, SEPARATOR);
537 }
538 else {
539 /* base64 encoded string always longer than original, so len+1 sufficient */
540 char *buf = (char *) apr_palloc(r->pool, len+1);
541 apr_base64_decode(buf, ticket);
542 sepidx = ap_ind(buf, SEPARATOR);
543 /* If still no sepidx, must be bogus */
544 if (sepidx == -1) return 0;
545 /* Update ticket and *magic to decoded version */
546 ticket = *magic = buf;
547 }
548 /* Reset len */
549 len = strlen(ticket);
550 }
551
552 /* Recheck length */
553 if (len <= (sconf->digest_sz + TSTAMP_SZ) ||
554 sepidx < (sconf->digest_sz + TSTAMP_SZ))
555 return 0;
556
557 if (conf->debug >= 1) {
558 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
559 "TKT parse_ticket decoded ticket: '%s'", ticket);
560 }
561
562 /* Get the user id */
563 parsed->uid = apr_palloc(r->pool, sepidx - (sconf->digest_sz + TSTAMP_SZ) + 1);
564 memcpy(parsed->uid, &ticket[(sconf->digest_sz + TSTAMP_SZ)],
565 sepidx - (sconf->digest_sz + TSTAMP_SZ));
566 parsed->uid[sepidx - (sconf->digest_sz + TSTAMP_SZ)] = '\0';
567
568 /* Check for tokens */
569 sep2idx = ap_ind(&ticket[sepidx+1], SEPARATOR);
570 if (sep2idx == -1) {
571 if (conf->debug >= 2) {
572 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
573 "TKT parse_ticket: no tokens");
574 }
575 parsed->tokens = apr_palloc(r->pool, 1);
576 *parsed->tokens = '\0';
577 }
578 else {
579 /* Swap sepidx and sep2idx */
580 int tmp = sepidx;
581 sepidx = tmp + sep2idx + 1;
582 sep2idx = tmp;
583 if (conf->debug >= 2) {
584 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
585 "TKT parse_ticket: tokens found - sep2=%d, sep=%d, len=%d",
586 sep2idx, sepidx, len);
587 }
588 /* Copy tokens to parsed->tokens */
589 parsed->tokens = apr_palloc(r->pool, sepidx-sep2idx);
590 apr_snprintf(parsed->tokens, sepidx-sep2idx, "%s", &ticket[sep2idx+1]);
591 if (conf->debug >= 2) {
592 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
593 "TKT parse_ticket tokens: '%s'", parsed->tokens);
594 }
595 }
596
597 /* Copy user data to parsed->user_data */
598 parsed->user_data = apr_palloc(r->pool, len-sepidx+1);
599 apr_snprintf(parsed->user_data, len-sepidx+1, "%s", &ticket[sepidx+1]);
600
601 /* Copy timestamp to parsed->timestamp */
602 sscanf(&ticket[sconf->digest_sz], "%8x", &(parsed->timestamp));
603
604 return 1;
605 }
606
607 /* Search cookie headers for our ticket */
608 static int
cookie_match(void * result,const char * key,const char * cookie)609 cookie_match(void *result, const char *key, const char *cookie)
610 {
611 cookie_res * cr = (cookie_res *) result;
612 auth_tkt_dir_conf *conf =
613 ap_get_module_config(cr->r->per_dir_config, &auth_tkt_module);
614
615 if (cookie != NULL) {
616 char *cookie_name, *value, *cookiebuf, *end;
617 if (conf->debug >= 2) {
618 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, cr->r,
619 "TKT cookie_match, key %s against <%s> (name=%s)",
620 key, cookie, cr->cookie_name);
621 }
622
623 cookie_name = apr_palloc(cr->r->pool, strlen(cr->cookie_name) + 2);
624 strncpy(cookie_name, cr->cookie_name, strlen(cr->cookie_name));
625 cookie_name[strlen(cr->cookie_name)] = '=';
626 cookie_name[strlen(cr->cookie_name) + 1] = '\0';
627
628 value = (char*) cookie;
629 while ((value = strstr(value, cookie_name))) {
630 /* cookie_name must be preceded by a space or be at the very beginning */
631 if (value > cookie && *(value-1) != ' ') {
632 value++;
633 continue;
634 }
635 /* Cookie includes our cookie_name - copy (first) value into cookiebuf */
636 value += strlen(cookie_name);
637 cookiebuf = apr_pstrdup(cr->r->pool, value);
638 end = ap_strchr(cookiebuf, ';');
639 if (end) *end = '\0'; /* Ignore anything after the next ; */
640 /* Skip empty cookies (such as with misconfigured logoffs) */
641 if (strlen(cookiebuf)) {
642 cr->cookie = cookiebuf;
643 if (conf->debug >= 1) {
644 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, cr->r,
645 "TKT cookie_match: found '%s'", cookiebuf);
646 }
647 return(0);
648 }
649 }
650 }
651 if (conf->debug >= 2) {
652 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, cr->r,
653 "TKT cookie_match: NOT found");
654 }
655 return (1);
656 }
657
658 /* Return the domain to use in cookies */
659 char *
get_domain(request_rec * r,auth_tkt_dir_conf * conf)660 get_domain(request_rec *r, auth_tkt_dir_conf *conf)
661 {
662 /* Set the cookie domain to the first set of TKTAuthDomain,
663 X-Forwarded-Host, Host, or server hostname. Viljo Viitanen
664 pointed out that using the wildcard domain is a security hole
665 in the event that other servers on your domain are hostile. */
666 char *domain = conf->auth_domain;
667 char *p;
668 if (!domain) domain = (char *) apr_table_get(r->headers_in, "X-Forwarded-Host");
669 if (!domain) domain = (char *) apr_table_get(r->headers_in, "Host");
670 if (domain) {
671 /* Ignore any trailing port in domain */
672 if ((p = ap_strchr(domain, ':'))) {
673 *p = '\0';
674 }
675 }
676 else {
677 domain = (char *) r->hostname;
678 }
679 return domain;
680 }
681
682 /* Send an auth cookie with the given value */
683 static void
send_auth_cookie(request_rec * r,char * value)684 send_auth_cookie(request_rec *r, char *value)
685 {
686 auth_tkt_dir_conf *conf =
687 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
688 char *cookie, *expires;
689 char *domain = get_domain(r,conf);
690 char *secure_cookie = conf->secure_cookie > 0 ? "; secure" : "";
691
692 /* Set cookie domain */
693 domain = domain ? apr_psprintf(r->pool, "; domain=%s", domain) : "";
694
695 /* Set cookie expires */
696 expires = "";
697 #ifndef APACHE13
698 if (conf->cookie_expires > 0) {
699 apr_time_exp_t tms;
700 apr_time_exp_gmt(&tms, r->request_time +
701 apr_time_from_sec(conf->cookie_expires));
702 expires =
703 apr_psprintf(r->pool, "; expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
704 apr_day_snames[tms.tm_wday],
705 tms.tm_mday,
706 apr_month_snames[tms.tm_mon],
707 tms.tm_year % 100,
708 tms.tm_hour, tms.tm_min, tms.tm_sec
709 );
710 }
711 #endif
712
713 /* Send the cookie */
714 cookie = apr_psprintf(r->pool, "%s=%s; path=/%s%s%s",
715 conf->auth_cookie_name, value, domain, expires, secure_cookie);
716 apr_table_setn(r->err_headers_out, "Set-Cookie", cookie);
717
718 if (conf->debug >= 1) {
719 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
720 "TKT: sending cookie: %s=%s; path=/%s%s%s",
721 conf->auth_cookie_name, value, domain, expires, secure_cookie);
722 }
723 }
724
725 /* Look for a url ticket */
726 static char *
get_url_ticket(request_rec * r)727 get_url_ticket(request_rec *r)
728 {
729 auth_tkt_dir_conf *conf =
730 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
731 const char *args = NULL; /* url arguments string */
732 const char *key, *val;
733 char *ticket = NULL;
734
735 /* Use main request args if subrequest */
736 request_rec *r_main = r->main == NULL ? r : r->main;
737 if (r_main->args != NULL) {
738 args = apr_pstrdup(r->pool, r_main->args);
739 }
740
741 if (args != NULL) {
742 if (conf->debug >= 1) {
743 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
744 "TKT: looking for ticket in url: <%s>", args);
745 }
746
747 while (*args && (val = ap_getword(r->pool, &args, '&'))) {
748 key = ap_getword(r->pool, &val, '=');
749
750 if (strcmp(key,conf->auth_cookie_name) == 0) {
751 if (conf->debug >= 1) {
752 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
753 "TKT: found url ticket: <%s>", val);
754 }
755
756 /* Setup auth cookie using ticket value */
757 send_auth_cookie(r, (char *) val);
758
759 /* Found ticket - ignore rest of arguments */
760 ticket = (char *) val;
761 break;
762 }
763 }
764 }
765
766 return ticket;
767 }
768
769 /* Look for a cookie ticket */
770 static char *
get_cookie_ticket(request_rec * r)771 get_cookie_ticket(request_rec *r)
772 {
773 auth_tkt_serv_conf *sconf =
774 ap_get_module_config(r->server->module_config, &auth_tkt_module);
775 auth_tkt_dir_conf *conf =
776 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
777
778 /* Walk cookie headers looking for matching ticket */
779 cookie_res *cr = apr_palloc(r->pool, sizeof(*cr));
780 cr->r = r;
781 cr->cookie = NULL;
782 cr->cookie_name = conf->auth_cookie_name;
783 apr_table_do(cookie_match, (void *) cr, r->headers_in, "Cookie", NULL);
784
785 /* Give up if cookie not found or too short */
786 if (! cr->cookie) {
787 return NULL;
788 }
789 if (strlen(cr->cookie) < sconf->digest_sz + TSTAMP_SZ) {
790 if (conf->debug >= 1) {
791 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
792 "TKT get_cookie_tkt: found cookie ticket, "
793 "but it's too short for a %s digest (%zu < %d)",
794 sconf->digest_type, strlen(cr->cookie), sconf->digest_sz + TSTAMP_SZ);
795 }
796 return NULL;
797 }
798
799 return cr->cookie;
800 }
801
802 /* Generate a ticket digest string from the given details */
803 static char *
ticket_digest(request_rec * r,auth_tkt * parsed,unsigned int timestamp,const char * secret)804 ticket_digest(request_rec *r, auth_tkt *parsed, unsigned int timestamp, const char *secret)
805 {
806 auth_tkt_serv_conf *sconf =
807 ap_get_module_config(r->server->module_config, &auth_tkt_module);
808 auth_tkt_dir_conf *conf =
809 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
810 char *uid = parsed->uid;
811 char *tokens = parsed->tokens;
812 char *user_data = parsed->user_data;
813
814 unsigned char *buf = apr_palloc(r->pool,
815 TSTAMP_SZ + strlen(secret) + strlen(uid) + 1 + strlen(tokens) + 1 + strlen(user_data) + 1);
816 unsigned char *buf2 = apr_palloc(r->pool, sconf->digest_sz + strlen(secret));
817 int len = 0;
818 char *digest = NULL;
819 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
820 char *remote_ip = conf->ignore_ip > 0 ? "0.0.0.0" : r->connection->client_ip;
821 #else
822 char *remote_ip = conf->ignore_ip > 0 ? "0.0.0.0" : r->connection->remote_ip;
823 #endif
824 unsigned long ip;
825 struct in_addr ia;
826 char *d;
827
828 /* Convert remote_ip to unsigned long */
829 if (inet_aton(remote_ip, &ia) == 0) {
830 return (NULL);
831 }
832 ip = ntohl(ia.s_addr);
833
834 if (timestamp == 0) timestamp = parsed->timestamp;
835
836 if (conf->debug >= 2) {
837 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
838 "TKT ticket_digest: using secret '%s', ip '%s', ts '%d'", secret, remote_ip, timestamp);
839 }
840
841 /* Fatals */
842 if (buf == NULL) return (NULL);
843 if (ip == APR_INADDR_NONE) return (NULL);
844
845 /* First 8 bytes for ip address + timestamp */
846 buf[0] = (unsigned char ) ((ip & 0xff000000) >> 24);
847 buf[1] = (unsigned char ) ((ip & 0xff0000) >> 16);
848 buf[2] = (unsigned char ) ((ip & 0xff00) >> 8);
849 buf[3] = (unsigned char ) ((ip & 0xff));
850 buf[4] = (unsigned char ) ((timestamp & 0xff000000) >> 24);
851 buf[5] = (unsigned char ) ((timestamp & 0xff0000) >> 16);
852 buf[6] = (unsigned char ) ((timestamp & 0xff00) >> 8);
853 buf[7] = (unsigned char ) ((timestamp & 0xff));
854 len = 8;
855
856 /* Append remaining components to buf */
857 strcpy((char *)&buf[len], secret);
858 len += strlen(secret);
859 strcpy((char *)&buf[len], uid);
860 len += strlen(uid);
861 buf[len++] = 0;
862 strcpy((char *)&buf[len], tokens);
863 len += strlen(tokens);
864 buf[len++] = 0;
865 strcpy((char *)&buf[len], user_data);
866 len += strlen(user_data);
867 buf[len] = 0;
868
869 /* Generate the initial digest */
870 if (strcmp(sconf->digest_type, "SHA256") == 0) {
871 d = apr_palloc(r->pool, SHA256_DIGEST_STRING_LENGTH);
872 digest = mat_SHA256_Data(buf, len, d);
873 }
874 else if (strcmp(sconf->digest_type, "SHA512") == 0) {
875 d = apr_palloc(r->pool, SHA512_DIGEST_STRING_LENGTH);
876 digest = mat_SHA512_Data(buf, len, d);
877 }
878 else {
879 digest = ap_md5_binary(r->pool, buf, len);
880 }
881 if (conf->debug >= 3) {
882 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
883 "TKT ticket_digest: digest0: '%s' (input length %d)", digest, len);
884 }
885
886 /* Copy digest + secret into buf2 */
887 len = sconf->digest_sz + strlen(secret);
888 memcpy(buf2, digest, sconf->digest_sz);
889 memcpy(&buf2[sconf->digest_sz], secret, len - sconf->digest_sz);
890
891 /* Generate the second digest */
892 if (strcmp(sconf->digest_type, "SHA256") == 0) {
893 d = apr_palloc(r->pool, SHA256_DIGEST_STRING_LENGTH);
894 digest = mat_SHA256_Data(buf2, len, d);
895 }
896 else if (strcmp(sconf->digest_type, "SHA512") == 0) {
897 d = apr_palloc(r->pool, SHA512_DIGEST_STRING_LENGTH);
898 digest = mat_SHA512_Data(buf2, len, d);
899 }
900 else {
901 digest = ap_md5_binary(r->pool, buf2, len);
902 }
903 if (conf->debug >= 3) {
904 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
905 "TKT ticket_digest: digest: '%s'", digest);
906 }
907
908 /* Should be noop, but just in case ... */
909 if (strlen(digest) > sconf->digest_sz) digest[sconf->digest_sz] = 0;
910
911 return (digest);
912 }
913
914 /* Check if this is a parseable and valid ticket
915 * Returns 1 if valid, and the parsed ticket in parsed, 0 if not */
916 static int
valid_ticket(request_rec * r,const char * source,char * ticket,auth_tkt * parsed,int * force_refresh)917 valid_ticket(request_rec *r, const char *source, char *ticket, auth_tkt *parsed, int *force_refresh)
918 {
919 char *digest;
920 auth_tkt_serv_conf *sconf =
921 ap_get_module_config(r->server->module_config, &auth_tkt_module);
922 auth_tkt_dir_conf *conf =
923 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
924
925 /* Attempt to parse ticket */
926 if (! parse_ticket(r, &ticket, parsed)) {
927 if (conf->debug >= 1) {
928 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r,
929 "TKT valid_ticket: unparseable %s ticket found ('%s')", source, ticket);
930 }
931 return 0;
932 }
933
934 if (conf->debug >= 1) {
935 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
936 "TKT valid_ticket: (parsed) uid '%s', tokens '%s', user_data '%s', ts '%d'",
937 parsed->uid, parsed->tokens, parsed->user_data, parsed->timestamp);
938 }
939
940 /* Check ticket hash against a calculated digest using the current secret */
941 digest = ticket_digest(r, parsed, 0, sconf->secret);
942 if (memcmp(ticket, digest, sconf->digest_sz) != 0) {
943
944 /* Digest mismatch - if no old secret set, fail */
945 if(sconf->old_secret == NULL) {
946 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r,
947 "TKT valid_ticket: ticket hash (current secret) is invalid, and no old secret set "
948 "- digest '%s', ticket '%s'",
949 digest, ticket);
950 return 0;
951 }
952
953 /* Digest mismatch - if old_secret is set, recalculate using that */
954 else {
955 if (conf->debug >= 1) {
956 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
957 "TKT valid_ticket: ticket hash (current secret) is invalid, but old_secret is set - checking ticket digest against that");
958 }
959 digest = ticket_digest(r, parsed, 0, sconf->old_secret);
960 if (memcmp(ticket, digest, sconf->digest_sz) != 0) {
961 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r,
962 "TKT valid_ticket: ticket hash (old secret) is also invalid - digest '%s', ticket '%s'",
963 digest, ticket);
964 return 0;
965 }
966
967 /* Ticket validates against old_secret, so we should force a cookie refresh */
968 else {
969 if (force_refresh != NULL) {
970 if (conf->debug >= 1) {
971 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
972 "TKT valid_ticket: ticket_digest validated with old_secret - forcing a cookie refresh");
973 }
974 *force_refresh = 1;
975 }
976 }
977 }
978 }
979
980 return 1;
981 }
982
983 /* Check for required auth tokens
984 * Returns 1 on success, 0 on failure */
985 static int
check_tokens(request_rec * r,char * tokens)986 check_tokens(request_rec *r, char *tokens)
987 {
988 auth_tkt_dir_conf *conf =
989 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
990 char *next_parsed_token;
991 const char *t = NULL;
992 int match = 0;
993
994 /* Success if no tokens required */
995 if (conf->auth_token->nelts == 0 ||
996 strcmp(((char **) conf->auth_token->elts)[0], "NULL") == 0) {
997 return 1;
998 }
999 /* Failure if required and no user tokens found */
1000 if (tokens == NULL || strlen(tokens) == 0) {
1001 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1002 "TKT: no matching tokens! (no user tokens found)");
1003 return 0;
1004 }
1005
1006 t = apr_pstrdup(r->pool, tokens);
1007
1008 while (*t && (next_parsed_token = ap_getword(r->pool, &t, ','))) {
1009 char ** auth_tokens = (char **) conf->auth_token->elts;
1010 int i;
1011
1012 for (i=0; i < conf->auth_token->nelts; i++) {
1013 int token_len = strlen(auth_tokens[i]);
1014 if (strncmp(auth_tokens[i], next_parsed_token, token_len) == 0 &&
1015 next_parsed_token[token_len] == 0) {
1016 match = 1;
1017 break;
1018 }
1019 }
1020 if (match) break;
1021 }
1022
1023 if (conf->debug >= 1 && ! match) {
1024 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1025 "TKT: no matching tokens! (user tokens '%s')", tokens);
1026 }
1027
1028 return match;
1029 }
1030
1031 /* Refresh the auth cookie if timeout refresh is set */
1032 static void
refresh_cookie(request_rec * r,auth_tkt * parsed,int timeout,int force_flag)1033 refresh_cookie(request_rec *r, auth_tkt *parsed, int timeout, int force_flag)
1034 {
1035 auth_tkt_serv_conf *sconf =
1036 ap_get_module_config(r->server->module_config, &auth_tkt_module);
1037 auth_tkt_dir_conf *conf =
1038 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
1039
1040 /* The timeout refresh is a double between 0 and 1, signifying what
1041 * proportion of the timeout should be left before we refresh i.e.
1042 * 0 means never refresh (hard timeouts); 1 means always refresh;
1043 * .33 means only refresh if less than a third of the timeout
1044 * period remains. */
1045 unsigned int now = time(NULL);
1046 int remainder = parsed->timestamp + timeout - now;
1047 double refresh_sec = conf->timeout_refresh * timeout;
1048
1049 if (conf->debug >= 1) {
1050 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1051 "TKT refresh_cookie: timeout %d, refresh %.3f, remainder %d, refresh_sec %.3f, force_flag %d",
1052 timeout, conf->timeout_refresh, remainder, refresh_sec, force_flag);
1053 }
1054
1055 /* If less than our refresh_sec threshold, freshen the cookie */
1056 if (force_flag || remainder < refresh_sec) {
1057 char *ticket, *ticket_base64;
1058 char *digest = ticket_digest(r, parsed, now, sconf->secret);
1059 if (parsed->tokens) {
1060 ticket = apr_psprintf(r->pool,
1061 "%s%08x%s%c%s%c%s",
1062 digest, now, parsed->uid,
1063 SEPARATOR, parsed->tokens,
1064 SEPARATOR, parsed->user_data);
1065 }
1066 else {
1067 ticket = apr_psprintf(r->pool,
1068 "%s%08x%s%c%s",
1069 digest, now, parsed->uid, SEPARATOR, parsed->user_data);
1070 }
1071 ticket_base64 = ap_pbase64encode(r->pool, ticket);
1072
1073 send_auth_cookie(r, ticket_base64);
1074 }
1075 }
1076
1077 /* Check whether the given timestamp has timed out
1078 * Returns 1 if okay, 0 if timed out */
1079 static int
check_timeout(request_rec * r,auth_tkt * parsed)1080 check_timeout(request_rec *r, auth_tkt *parsed)
1081 {
1082 char *timeout_date, *timeout_cookie;
1083 auth_tkt_dir_conf *conf =
1084 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
1085 unsigned int now = time(NULL);
1086 unsigned int timestamp = parsed->timestamp;
1087 char *domain = get_domain(r,conf);
1088 char *secure_cookie = conf->secure_cookie > 0 ? "; secure" : "";
1089 int timeout;
1090
1091 /* Return OK if no timeout configured */
1092 if (conf->timeout_sec == 0) return 1;
1093 timeout = conf->timeout_sec == -1 ? DEFAULT_TIMEOUT_SEC : conf->timeout_sec;
1094
1095 /* Check whether timestamp is still fresh */
1096 if (timestamp + timeout >= now) {
1097 if (conf->debug >= 1) {
1098 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1099 "TKT: cookie timeout still good: now %d, timeout: %d, tstamp: %d",
1100 now, timeout, timestamp);
1101 }
1102
1103 /* Check whether to refresh the cookie */
1104 if (conf->timeout_refresh > 0)
1105 refresh_cookie(r, parsed, timeout, CHECK_REFRESH);
1106
1107 return 1;
1108 }
1109
1110 if (conf->debug >= 1) {
1111 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1112 "TKT: ticket timed out: now %d, timeout: %d, tstamp: %d",
1113 now, timeout, timestamp);
1114 }
1115
1116 /* Delete cookie (set expired) if invalid, in case we want to set from url */
1117 timeout_date = ap_ht_time(r->pool, now - 3600, "%a %d %b %Y %T %Z", 0);
1118 timeout_cookie = domain ?
1119 apr_psprintf(r->pool,
1120 "%s=; path=/; domain=%s; expires=%s%s",
1121 conf->auth_cookie_name, domain, timeout_date, secure_cookie) :
1122 apr_psprintf(r->pool,
1123 "%s=; path=/; expires=%s%s",
1124 conf->auth_cookie_name, timeout_date, secure_cookie);
1125 apr_table_setn(r->err_headers_out, "Set-Cookie", timeout_cookie);
1126
1127 return 0;
1128 }
1129
1130 /* Strip specified query args from a url */
1131 static char *
query_strip(request_rec * r,const char * strip)1132 query_strip(request_rec *r, const char *strip)
1133 {
1134 const char *args = NULL; /* url arguments string */
1135 const char *key, *val;
1136 char *new_args = "";
1137 char *p;
1138
1139 /* Use main request args if subrequest */
1140 request_rec *r_main = r->main == NULL ? r : r->main;
1141 if (r_main->args == NULL || strip == NULL)
1142 return NULL;
1143
1144 args = apr_pstrdup(r->pool, r_main->args);
1145
1146 /* Convert all '&' to ';' */
1147 while ((p = ap_strchr(args, '&')))
1148 *p = ';';
1149
1150 /* Split into individual args */
1151 while (*args && (val = ap_getword(r->pool, &args, ';'))) {
1152 key = ap_getword(r->pool, &val, '=');
1153
1154 /* Add to new_args only if key != strip */
1155 if (strlen(strip) != strlen(key) || strncmp(key,strip,strlen(strip)) != 0)
1156 new_args = apr_psprintf(r->pool, "%s&%s=%s", new_args, key, val);
1157 }
1158
1159 if (strlen(new_args) > 0)
1160 return new_args + 1;
1161
1162 return NULL;
1163 }
1164
1165 /* Hex conversion, from httpd util.c */
1166 static const char c2x_table[] = "0123456789abcdef";
1167 static APR_INLINE unsigned char *
c2x(unsigned what,unsigned char * where)1168 c2x(unsigned what, unsigned char *where)
1169 {
1170 #if APR_CHARSET_EBCDIC
1171 what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
1172 #endif /*APR_CHARSET_EBCDIC*/
1173 *where++ = '%';
1174 *where++ = c2x_table[what >> 4];
1175 *where++ = c2x_table[what & 0xf];
1176 return where;
1177 }
1178
1179 /* Extra escaping - variant of httpd util.c ap_escape_path_segment */
1180 static char *
escape_extras(apr_pool_t * p,const char * segment)1181 escape_extras(apr_pool_t *p, const char *segment)
1182 {
1183 char *copy = apr_palloc(p, 3 * strlen(segment) + 1);
1184 const unsigned char *s = (const unsigned char *)segment;
1185 unsigned char *d = (unsigned char *)copy;
1186 unsigned c;
1187
1188 while ((c = *s)) {
1189 if (c == '=' || c == '&' || c == ':') {
1190 d = c2x(c, d);
1191 }
1192 else {
1193 *d++ = c;
1194 }
1195 ++s;
1196 }
1197 *d = '\0';
1198 return copy;
1199 }
1200
1201 /* External redirect to the given url, setting back cookie or arg */
1202 static int
redirect(request_rec * r,char * location)1203 redirect(request_rec *r, char *location)
1204 {
1205 auth_tkt_dir_conf *conf =
1206 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
1207
1208 char *domain = get_domain(r,conf);
1209 char *back_cookie_name = conf->back_cookie_name;
1210 char *back_arg_name = conf->back_arg_name;
1211 char *url = location;
1212 char *cookie, *back;
1213 const char *hostinfo = 0;
1214 int port;
1215
1216 /* Get the scheme we use (http or https) */
1217 const char *scheme = ap_http_method(r);
1218
1219 /* Strip any auth_cookie_name arguments from the current args */
1220 char *query = query_strip(r, conf->auth_cookie_name);
1221 if (query == NULL) {
1222 query = "";
1223 }
1224 else if (strlen(query) > 0) {
1225 query = apr_psprintf(r->pool, "?%s", query);
1226 }
1227
1228 /* Build back URL */
1229 /* Use X-Forward-Host header for host:port info if available */
1230 /* Failing that, use Host header */
1231 hostinfo = apr_table_get(r->headers_in, "X-Forwarded-Host");
1232 if (! hostinfo) hostinfo = apr_table_get(r->headers_in, "Host");
1233 if (! hostinfo) {
1234 /* Fallback to using r->hostname and the server port. This usually
1235 works, but behind a reverse proxy the port may well be wrong.
1236 On the other hand, it's really the proxy's problem, not ours.
1237 */
1238 ap_log_rerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, r,
1239 "TKT: could not find Host header, falling back to hostname/server port");
1240 port = ap_get_server_port(r);
1241 hostinfo = port == apr_uri_default_port_for_scheme(scheme) ?
1242 apr_psprintf(r->pool, "%s", r->hostname) :
1243 apr_psprintf(r->pool, "%s:%d", r->hostname, port);
1244 }
1245
1246 /* If no scheme, assume location is relative and expand using scheme and hostinfo */
1247 if (strncasecmp(location, "http", 4) != 0) {
1248 char *old_location = apr_pstrdup(r->pool, location);
1249 location = apr_psprintf(r->pool, "%s://%s/%s", scheme, hostinfo, location);
1250 if (conf->debug >= 1) {
1251 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1252 "TKT relative URL '%s' expanded to '%s'", old_location, location);
1253 }
1254 }
1255
1256 /* Setup back URL */
1257 back = apr_psprintf(r->pool, "%s://%s%s%s",
1258 scheme, hostinfo, r->uri, query);
1259 if (conf->debug >= 1) {
1260 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1261 "TKT: back url '%s'", back);
1262 }
1263
1264 /* Escape testing */
1265 back = ap_escape_path_segment(r->pool, back);
1266 back = escape_extras(r->pool, back);
1267
1268 /* Set back cookie if name is not null */
1269 if (back_cookie_name) {
1270 cookie = domain ?
1271 apr_psprintf(r->pool, "%s=%s; path=/; domain=%s",
1272 back_cookie_name, back, domain) :
1273 apr_psprintf(r->pool, "%s=%s; path=/",
1274 back_cookie_name, back);
1275
1276 apr_table_setn(r->err_headers_out, "Set-Cookie", cookie);
1277 url = location;
1278 }
1279
1280 /* If back_cookie_name not set, add a back url argument to url */
1281 else if (back_arg_name) {
1282 char sep = ap_strchr(location, '?') ? conf->query_separator[0] : '?';
1283 url = apr_psprintf(r->pool, "%s%c%s=%s",
1284 location, sep, back_arg_name, back);
1285 }
1286
1287 if (conf->debug >= 2) {
1288 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1289 "TKT: redirect '%s'", url);
1290 }
1291 apr_table_setn(r->headers_out, "Location", url);
1292
1293 return HTTP_TEMPORARY_REDIRECT;
1294 }
1295
1296 /* Determine the guest username */
1297 static char *
get_guest_uid(request_rec * r,auth_tkt_dir_conf * conf)1298 get_guest_uid(request_rec *r, auth_tkt_dir_conf *conf)
1299 {
1300 #ifndef APACHE13
1301 char *guest_user;
1302 int guest_user_length;
1303 apr_uuid_t *uuid;
1304 char *uuid_str, *uuid_length_str;
1305 #ifndef APACHE22
1306 regex_t *uuid_regex;
1307 regmatch_t regm[UUID_SUBS];
1308 #else
1309 ap_regex_t *uuid_regex;
1310 ap_regmatch_t regm[UUID_SUBS];
1311 #endif
1312 int uuid_length = -1;
1313 char *uuid_pre, *uuid_post;
1314 #endif
1315
1316 /* no guest user specified via config, use the default */
1317 if (! conf->guest_user) {
1318 return DEFAULT_GUEST_USER;
1319 }
1320
1321 #ifdef APACHE13
1322 /* We don't support %U under apache1 at this point */
1323 return conf->guest_user;
1324 #else
1325
1326 /* use UUID if configured */
1327 guest_user = apr_pstrdup(r->pool, conf->guest_user);
1328 uuid_regex = ap_pregcomp(r->pool, "%([0-9]*)U", 0);
1329 if (!ap_regexec(uuid_regex, guest_user, UUID_SUBS, regm, 0)) {
1330 /* Check whether a UUID length was specified */
1331 if (regm[1].rm_so != -1) {
1332 uuid_length_str = ap_pregsub(r->pool, "$1", guest_user,
1333 UUID_SUBS, regm);
1334 if (uuid_length_str)
1335 uuid_length = atoi(uuid_length_str);
1336 }
1337 if (uuid_length <= 0 || uuid_length > APR_UUID_FORMATTED_LENGTH) {
1338 uuid_length = APR_UUID_FORMATTED_LENGTH;
1339 }
1340 if (conf->debug >= 1) {
1341 ap_log_rerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, r,
1342 "TKT: %%U found in guest user (length %d)", uuid_length);
1343 }
1344 /* Generate the UUID */
1345 uuid = apr_palloc(r->pool, sizeof(*uuid));
1346 uuid_str = apr_palloc(r->pool, APR_UUID_FORMATTED_LENGTH + 1);
1347 apr_uuid_get(uuid);
1348 apr_uuid_format(uuid_str, uuid);
1349 if (uuid_length < APR_UUID_FORMATTED_LENGTH)
1350 uuid_str[uuid_length] = '\0';
1351 /* Generate the new guest_user string */
1352 guest_user_length = strlen(guest_user);
1353 if (regm[0].rm_so > 1) {
1354 guest_user[regm[1].rm_so-1] = '\0';
1355 uuid_pre = guest_user;
1356 }
1357 else
1358 uuid_pre = "";
1359 if (regm[0].rm_eo < guest_user_length)
1360 uuid_post = guest_user + regm[0].rm_eo;
1361 else
1362 uuid_post = "";
1363
1364 return apr_psprintf(r->pool, "%s%s%s",
1365 uuid_pre, uuid_str, uuid_post);
1366 }
1367
1368 /* Otherwise, it's just a plain username. Return that. */
1369 return conf->guest_user;
1370 #endif /* ! APACHE13 */
1371 }
1372
1373 /* Set up the guest user info */
1374 static int
setup_guest(request_rec * r,auth_tkt_dir_conf * conf,auth_tkt * tkt)1375 setup_guest(request_rec *r, auth_tkt_dir_conf *conf, auth_tkt *tkt)
1376 {
1377 /* Only applicable if guest access on */
1378 if (conf->guest_login <= 0) {
1379 return 0;
1380 }
1381
1382 tkt->uid = get_guest_uid(r, conf);
1383 tkt->user_data = "";
1384 tkt->tokens = "";
1385 ap_log_rerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, r,
1386 "TKT: no valid ticket found - accepting as guest user '%s'",
1387 tkt->uid);
1388
1389 return 1;
1390 }
1391
1392 /* ----------------------------------------------------------------------- */
1393 /* Debug routines */
1394 void
dump_config(request_rec * r,auth_tkt_serv_conf * sconf,auth_tkt_dir_conf * conf)1395 dump_config(request_rec *r, auth_tkt_serv_conf *sconf, auth_tkt_dir_conf *conf)
1396 {
1397 /* Dump config settings */
1398 fprintf(stderr,"[ mod_auth_tkt config ]\n");
1399 fprintf(stderr,"URI: %s\n", r->uri);
1400 fprintf(stderr,"Filename: %s\n", r->filename);
1401 fprintf(stderr,"TKTAuthSecret: %s\n", sconf->secret);
1402 fprintf(stderr,"TKTAuthSecretOld: %s\n", sconf->old_secret);
1403 fprintf(stderr,"TKTAuthDigestType: %s\n", sconf->digest_type);
1404 fprintf(stderr,"digest_sz: %d\n", sconf->digest_sz);
1405 fprintf(stderr,"directory: %s\n", conf->directory);
1406 fprintf(stderr,"TKTAuthLoginURL: %s\n", conf->login_url);
1407 fprintf(stderr,"TKTAuthTimeoutURL: %s\n", conf->timeout_url);
1408 fprintf(stderr,"TKTAuthPostTimeoutURL: %s\n", conf->post_timeout_url);
1409 fprintf(stderr,"TKTAuthUnauthURL: %s\n", conf->unauth_url);
1410 fprintf(stderr,"TKTAuthCookieName: %s\n", conf->auth_cookie_name);
1411 fprintf(stderr,"TKTAuthDomain: %s\n", conf->auth_domain);
1412 fprintf(stderr,"TKTAuthCookieExpires: %d\n", conf->cookie_expires);
1413 fprintf(stderr,"TKTAuthBackCookieName: %s\n", conf->back_cookie_name);
1414 fprintf(stderr,"TKTAuthBackArgName: %s\n", conf->back_arg_name);
1415 fprintf(stderr,"TKTAuthIgnoreIP: %d\n", conf->ignore_ip);
1416 fprintf(stderr,"TKTAuthRequireSSL: %d\n", conf->require_ssl);
1417 fprintf(stderr,"TKTAuthCookieSecure: %d\n", conf->secure_cookie);
1418 fprintf(stderr,"TKTAuthTimeoutMin: %d\n", conf->timeout_sec);
1419 fprintf(stderr,"TKTAuthTimeoutRefresh: %f\n", conf->timeout_refresh);
1420 fprintf(stderr,"TKTAuthGuestLogin: %d\n", conf->guest_login);
1421 fprintf(stderr,"TKTAuthGuestCookie: %d\n", conf->guest_cookie);
1422 fprintf(stderr,"TKTAuthGuestUser: %s\n", conf->guest_user);
1423 fprintf(stderr,"TKTAuthGuestFallback %d\n", conf->guest_fallback);
1424 fprintf(stderr,"TKTAuthQuerySeparator: %c\n", conf->query_separator);
1425 if (conf->auth_token->nelts > 0) {
1426 char ** auth_token = (char **) conf->auth_token->elts;
1427 int i;
1428 for (i = 0; i < conf->auth_token->nelts; i++) {
1429 fprintf(stderr, "TKTAuthToken: %s\n", auth_token[i]);
1430 }
1431 }
1432 fprintf(stderr,"TKTAuthDebug: %d\n", conf->debug);
1433 fflush(stderr);
1434 }
1435
1436 /* ----------------------------------------------------------------------- */
1437 /* Main ticket authentication */
1438 static int
auth_tkt_check(request_rec * r)1439 auth_tkt_check(request_rec *r)
1440 {
1441 char *ticket;
1442 auth_tkt *parsed = apr_palloc(r->pool, sizeof(*parsed));
1443 auth_tkt_dir_conf *conf =
1444 ap_get_module_config(r->per_dir_config, &auth_tkt_module);
1445 auth_tkt_serv_conf *sconf =
1446 ap_get_module_config(r->server->module_config, &auth_tkt_module);
1447 const char *scheme = ap_http_method(r);
1448 int guest = 0;
1449 int timeout;
1450 int force_cookie_refresh = 0;
1451 char *url = NULL;
1452
1453 /* Default digest_type if not set */
1454 if (! sconf->digest_type) {
1455 sconf->digest_type = DEFAULT_DIGEST_TYPE;
1456 setup_digest_sz(sconf);
1457 }
1458
1459 /* Map "None" back_arg_name to NULL */
1460 if (conf->back_arg_name && strcmp(conf->back_arg_name, "None") == 0)
1461 conf->back_arg_name = NULL;
1462
1463 /* Dump config if debugging */
1464 if (conf->debug >= 2)
1465 dump_config(r, sconf, conf);
1466
1467 /* Module not configured unless login_url or guest_login is set */
1468 if (! conf->login_url && conf->guest_login <= 0) {
1469 return DECLINED;
1470 }
1471 /* Module misconfigured unless secret set */
1472 if (! sconf->secret) {
1473 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r,
1474 "TKT: TKTAuthSecret missing");
1475 return HTTP_INTERNAL_SERVER_ERROR;
1476 }
1477 /* Redirect/login if scheme not "https" and require_ssl is set */
1478 if (conf->require_ssl > 0 && strcmp(scheme,"https") != 0) {
1479 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r,
1480 "TKT: redirect/login - unsecured request, TKTAuthRequireSSL is on");
1481 return redirect(r, conf->login_url);
1482 }
1483 /* Backwards compatibility mode for TKTAuthRequireSSL */
1484 if (conf->require_ssl > 0 && conf->secure_cookie == -1) {
1485 /* Set secure_cookie flag if require_ssl is set and secure_cookie is
1486 undefined (as opposed to 'off') */
1487 ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r,
1488 "TKT: TKTAuthRequireSSL on, but no TKTAuthCookieSecure found - "
1489 "please set TKTAuthCookieSecure explicitly, assuming 'on'");
1490 conf->secure_cookie = 1;
1491 }
1492
1493 /* Check for url ticket - either found (accept) or empty (reset/login) */
1494 ticket = get_url_ticket(r);
1495 if (! ticket || ! valid_ticket(r, "url", ticket, parsed, &force_cookie_refresh)) {
1496 ticket = get_cookie_ticket(r);
1497 if (! ticket || ! valid_ticket(r, "cookie", ticket, parsed, &force_cookie_refresh)) {
1498 if (conf->guest_login > 0) {
1499 guest = setup_guest(r, conf, parsed);
1500 }
1501 if (! guest) {
1502 if (conf->login_url) {
1503 ap_log_rerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, r,
1504 "TKT: no valid ticket found - redirecting to login url");
1505 return redirect(r, conf->login_url);
1506 }
1507 else {
1508 /* Fatal error: guest setup failed, but we have no login url defined */
1509 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r,
1510 "TKT: guest login failed and no login url to fall back to - aborting");
1511 return HTTP_INTERNAL_SERVER_ERROR;
1512 }
1513 }
1514 }
1515 }
1516
1517 /* Valid ticket, check timeout - redirect/timed-out if so */
1518 if (! guest && ! check_timeout(r, parsed)) {
1519 /* Timeout! Check for guest access and guest_fallback */
1520 if (conf->guest_login > 0 && conf->guest_fallback > 0) {
1521 guest = setup_guest(r, conf, parsed);
1522 }
1523 if (!guest) {
1524 /* Special timeout URL can be defined for POST requests */
1525 if (strcmp(r->method, "POST") == 0 && conf->post_timeout_url) {
1526 url = conf->post_timeout_url;
1527 }
1528 else {
1529 url = conf->timeout_url ? conf->timeout_url : conf->login_url;
1530 }
1531 if (url) {
1532 return redirect(r, url);
1533 }
1534 else {
1535 /* Fatal error: guest setup failed, but we have no url to redirect to */
1536 ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r,
1537 "TKT: ticket timeout, guest login failed, and no url to fall back to - aborting");
1538 return HTTP_INTERNAL_SERVER_ERROR;
1539 }
1540 }
1541 }
1542
1543 /* Valid ticket, check tokens - redirect/unauthorised if so */
1544 if (! check_tokens(r, parsed->tokens)) {
1545 return redirect(r, conf->unauth_url ? conf->unauth_url : conf->login_url);
1546 }
1547
1548 /* If force_cookie_refresh flag is set (because the current ticket is using TKTAuthSecretOld),
1549 * or this is a new guest login and the guest_cookie flag is set, force a cookie refresh */
1550 if (force_cookie_refresh || (guest && conf->guest_cookie > 0)) {
1551 timeout = conf->timeout_sec == -1 ? DEFAULT_TIMEOUT_SEC : conf->timeout_sec;
1552 refresh_cookie(r, parsed, timeout, FORCE_REFRESH);
1553 }
1554
1555 /* Setup apache user, auth_type, and environment variables */
1556 #ifdef APACHE13
1557 r->connection->user = parsed->uid;
1558 r->connection->ap_auth_type = "Basic";
1559 #else
1560 r->user = parsed->uid;
1561 r->ap_auth_type = "Basic";
1562 #endif
1563 apr_table_set(r->subprocess_env, REMOTE_USER_ENV, parsed->uid);
1564 apr_table_set(r->subprocess_env, REMOTE_USER_DATA_ENV, parsed->user_data);
1565 apr_table_set(r->subprocess_env, REMOTE_USER_TOKENS_ENV, parsed->tokens);
1566
1567 return OK;
1568 }
1569
1570 /* ----------------------------------------------------------------------- */
1571 /* Setup main module data structure */
1572
1573 #ifdef APACHE13
1574 /* Apache 1.3 style */
1575
1576 module MODULE_VAR_EXPORT auth_tkt_module = {
1577 STANDARD_MODULE_STUFF,
1578 auth_tkt_version, /* initializer */
1579 create_auth_tkt_config, /* create per-dir config structures */
1580 merge_auth_tkt_config, /* merge per-dir config structures */
1581 create_auth_tkt_serv_config, /* create per-server config structures */
1582 merge_auth_tkt_serv_config, /* merge per-server config structures */
1583 auth_tkt_cmds, /* table of config file commands */
1584 NULL, /* handlers */
1585 NULL, /* filename translation */
1586 auth_tkt_check, /* check user_id */
1587 NULL, /* check auth */
1588 NULL, /* check access */
1589 NULL, /* type_checker */
1590 NULL, /* fixups */
1591 NULL, /* logger */
1592 NULL, /* header parser */
1593 NULL, /* chitkt_init */
1594 NULL, /* chitkt_exit */
1595 NULL /* post read-request */
1596 };
1597
1598 #else
1599 /* Apache 2.0 style */
1600
1601 /* Register hooks */
1602 static void
auth_tkt_register_hooks(apr_pool_t * p)1603 auth_tkt_register_hooks (apr_pool_t *p)
1604 {
1605 ap_hook_post_config(auth_tkt_version, NULL, NULL, APR_HOOK_MIDDLE);
1606 #if AP_MODULE_MAGIC_AT_LEAST(20080403,1)
1607 ap_hook_check_authn(auth_tkt_check, NULL, NULL, APR_HOOK_FIRST, AP_AUTH_INTERNAL_PER_CONF);
1608 #else
1609 ap_hook_check_user_id(auth_tkt_check, NULL, NULL, APR_HOOK_FIRST);
1610 #endif
1611 }
1612
1613 /* Declare and populate the main module data structure */
1614 module AP_MODULE_DECLARE_DATA auth_tkt_module = {
1615 STANDARD20_MODULE_STUFF,
1616 create_auth_tkt_config, /* create per-dir config structures */
1617 merge_auth_tkt_config, /* merge per-dir config structures */
1618 create_auth_tkt_serv_config, /* create per-server config structures */
1619 merge_auth_tkt_serv_config, /* merge per-server config structures */
1620 auth_tkt_cmds, /* table of config file commands */
1621 auth_tkt_register_hooks /* register hooks */
1622 };
1623
1624 #endif
1625
1626 /*
1627 * vim:sw=2:sm:et
1628 */
1629