Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | /* | |
11 | * Implements most of the client side web authentication protocol | |
12 | */ | |
13 | ||
14 | /* | |
15 | */ | |
16 | ||
17 | #include "apache_afs_utils.h" | |
18 | #include "apache_afs_cache.h" | |
19 | ||
20 | #include "AFS_component_version_number.c" | |
21 | #include "apache_api.h" | |
22 | ||
23 | #define afsassert(str) if(!(str)) { fprintf(stderr, "afs module: assertion failed:%s\t%d\n",__FILE__,__LINE__) ; return SERVER_ERROR; } | |
24 | ||
25 | #define MAXBUFF 1024 | |
26 | ||
27 | #define APACHEAFS_MAX_PATH 1024 /* Maximum path length */ | |
28 | #define APACHEAFS_USERNAME_MAX 64 /* Maximum username length */ | |
29 | #define APACHEAFS_PASSWORD_MAX 64 /* Maximum password length */ | |
30 | #define APACHEAFS_CELLNAME_MAX 64 /* Maximum cellname length */ | |
31 | #define APACHEAFS_MOUNTPTLEN_MAX 64 /* Max mountpoint length */ | |
32 | ||
33 | #ifndef MAX | |
34 | #define MAX(A,B) ((A)>(B)?(A):(B)) | |
35 | #endif /* !MAX */ | |
36 | #ifndef MIN | |
37 | #define MIN(A,B) ((A)<(B)?(A):(B)) | |
38 | #endif /* !MAX */ | |
39 | ||
40 | /* */ | |
41 | static int setCellAuthHeader(request_rec * r); | |
42 | ||
43 | /* Module name for logging stuff */ | |
44 | extern const char module_name[]; | |
45 | ||
46 | /* file descriptors for the pipes for communication with weblog */ | |
47 | extern int readPipe; | |
48 | extern int writePipe; | |
49 | ||
50 | #ifdef AIX | |
51 | /* lock file descriptor */ | |
52 | extern int tempfd; | |
53 | #endif | |
54 | ||
55 | /* do we have an authentication field */ | |
56 | static int haveAuth = 0; | |
57 | ||
58 | /* local cache stuff */ | |
59 | static int cache_initialized; | |
60 | ||
61 | /* we need the defult cell in several places */ | |
62 | static char global_default_cell[APACHEAFS_CELLNAME_MAX]; | |
63 | ||
64 | /* buffers to keep track of the last authenticated user */ | |
65 | static char lastUser[APACHEAFS_USERNAME_MAX]; | |
66 | static char lastCell[APACHEAFS_CELLNAME_MAX]; | |
67 | static char lastCksum[SHA_HASH_BYTES]; | |
68 | ||
69 | /* do I have my own PAG */ | |
70 | static int doneSETPAG = 0; | |
71 | ||
72 | /* | |
73 | * Parse the authorization header for username and password | |
74 | */ | |
75 | static int | |
76 | parse_authhdr(request_rec * r, char *user, char *passwd, char *cell, | |
77 | char *defaultCell) | |
78 | { | |
79 | int i; | |
80 | char *p, *t; | |
81 | const char *auth_line = TABLE_GET(r->headers_in, "Authorization"); | |
82 | ||
83 | if ((r == NULL) || (auth_line == NULL) || (user == NULL) | |
84 | || (passwd == NULL) || (cell == NULL)) { | |
85 | LOG_REASON("AFSAUTH_CLIENT: NULL request record, auth_line, cell," | |
86 | "user or passwd while parsing authentication header", | |
87 | r->uri, r); | |
88 | return SERVER_ERROR; | |
89 | } | |
90 | ||
91 | user[0] = '\0'; | |
92 | passwd[0] = '\0'; | |
93 | cell[0] = '\0'; | |
94 | ||
95 | /* | |
96 | * check for basic authentication | |
97 | */ | |
98 | if (strncasecmp | |
99 | (GETWORD(r->pool, (const char **)&auth_line, ' '), "basic", 6) != 0) { | |
100 | /* Client tried to authenticate using some other auth scheme */ | |
101 | LOG_REASON | |
102 | ("AFSAUTH_CLIENT:client used other than Basic authentication" | |
103 | "scheme", r->uri, r); | |
104 | return SERVER_ERROR; | |
105 | } | |
106 | ||
107 | /* | |
108 | * Username and password are base64 encoded | |
109 | */ | |
110 | t = UUDECODE(r->pool, auth_line); | |
111 | ||
112 | if (t == NULL) { | |
113 | LOG_REASON("AFSAUTH_CLIENT:uudecode failed", r->uri, r); | |
114 | return SERVER_ERROR; | |
115 | } | |
116 | ||
117 | /* | |
118 | * Format is user@cell:passwd. The user, cell or passwd may be missing | |
119 | */ | |
120 | r->connection->user = GETWORD_NULLS(r->pool, (const char **)&t, ':'); | |
121 | r->connection->auth_type = "Basic"; | |
122 | strcpy(passwd, t); | |
123 | ||
124 | p = r->connection->user; | |
125 | ||
126 | for (i = 0; *p != '@' && *p != '\0'; p++, i++) { | |
127 | user[i] = *p; | |
128 | } | |
129 | user[i] = '\0'; | |
130 | if (*p == '@') { | |
131 | for (i = 0, p++; *p != '\0'; p++, i++) { | |
132 | cell[i] = *p; | |
133 | } | |
134 | cell[i] = '\0'; | |
135 | } | |
136 | ||
137 | if (cell[0] == '\0') { | |
138 | strcpy(cell, defaultCell); | |
139 | } | |
140 | return OK; | |
141 | } | |
142 | ||
143 | /* | |
144 | * send a buffer to the weblog process over the pipe. Used for sending | |
145 | * authentication credentials to weblog | |
146 | */ | |
147 | static int | |
148 | sendBuffer(char *buf, int len) | |
149 | { | |
150 | afsassert(buf); | |
151 | if (write(writePipe, buf, len) != len) { | |
152 | afslog(5, | |
153 | ("%s: Error writing to pipe - %s", module_name, | |
154 | strerror(errno))); | |
155 | return -1; | |
156 | } | |
157 | return 0; | |
158 | } | |
159 | ||
160 | /* | |
161 | * packs user credentials into a buffer seperated by newlines and | |
162 | * sends them to weblog | |
163 | */ | |
164 | static int | |
165 | sendTo_afsAuthenticator(char *user, char *passwd, char *cell, char *type) | |
166 | { | |
167 | char buf[MAXBUFF]; | |
168 | ||
169 | afsassert(user); | |
170 | afsassert(passwd); | |
171 | afsassert(cell); | |
172 | afsassert(type); | |
173 | ||
174 | sprintf(buf, "%s\n%s\n%s\n%s", type, user, cell, passwd); | |
175 | return sendBuffer(buf, strlen(buf)); | |
176 | } | |
177 | ||
178 | /* | |
179 | * reads the response from weblog over the pipe | |
180 | */ | |
181 | static int | |
182 | recvFrom_afsAuthenticator(char *buf) | |
183 | { | |
184 | int n; | |
185 | ||
186 | afsassert(buf); | |
187 | n = read(readPipe, buf, MAXBUFF); | |
188 | if (n < 0) { | |
189 | afslog(5, | |
190 | ("%s: Error reading from pipe - %s", module_name, | |
191 | strerror(errno))); | |
192 | return -1; | |
193 | } | |
194 | return n; | |
195 | } | |
196 | ||
197 | #ifndef NO_AFSAPACHE_CACHE | |
198 | /* | |
199 | * check local cache for the token associated with user crds. | |
200 | */ | |
201 | static int | |
202 | check_Cache(char *user, char *passwd, char *cell, char *tokenBuf) | |
203 | { | |
204 | char cksum[SHA_HASH_BYTES]; /* for sha checksum for caching */ | |
205 | ||
206 | /* look up local cache - function in apache_afs_cach.c */ | |
207 | weblog_login_checksum(user, cell, passwd, cksum); | |
208 | return weblog_login_lookup(user, cell, cksum, &tokenBuf[0]); | |
209 | } | |
210 | ||
211 | /* | |
212 | * put the token and the user credentials in the local cache | |
213 | */ | |
214 | static int | |
215 | updateCache(char *user, char *passwd, char *cell, char *tokenBuf, | |
216 | int cacheExpiration) | |
217 | { | |
218 | long expires = 0, testExpires = 0; | |
219 | char cksum[SHA_HASH_BYTES]; /* for sha checksum for caching */ | |
220 | ||
221 | /* put the token in local cache with the expiration date */ | |
222 | expires = getExpiration(tokenBuf); | |
223 | if (expires < 0) { | |
224 | afslog(5, | |
225 | ("%s: Error getting expiration time for cache. Expires %d", | |
226 | module_name, expires)); | |
227 | return -1; | |
228 | } | |
229 | ||
230 | weblog_login_checksum(user, cell, passwd, cksum); | |
231 | ||
232 | if (cacheExpiration == 0) { | |
233 | weblog_login_store(user, cell, cksum, &tokenBuf[0], sizeof(tokenBuf), | |
234 | expires); | |
235 | } else { | |
236 | testExpires = cacheExpiration + time(NULL); | |
237 | weblog_login_store(user, cell, cksum, &tokenBuf[0], sizeof(tokenBuf), | |
238 | MIN(expires, testExpires)); | |
239 | } | |
240 | return 0; | |
241 | } | |
242 | #endif /* NO_APACHEAFS_CACHE */ | |
243 | ||
244 | ||
245 | /* | |
246 | * locking routines to provide exclusive access to the pipes | |
247 | */ | |
248 | static int | |
249 | start_lock(int fd, int cmd, int type) | |
250 | { | |
251 | struct flock lock; | |
252 | lock.l_type = type; | |
253 | lock.l_start = 0; | |
254 | lock.l_whence = SEEK_SET; | |
255 | lock.l_len = 0; | |
256 | return (fcntl(fd, cmd, &lock)); | |
257 | } | |
258 | ||
259 | static int | |
260 | test_lock(int fd, int type) | |
261 | { | |
262 | struct flock lock; | |
263 | lock.l_type = type; | |
264 | lock.l_start = 0; | |
265 | lock.l_whence = SEEK_SET; | |
266 | lock.l_len = 0; | |
267 | ||
268 | if (fcntl(fd, F_GETLK, &lock) < 0) { | |
269 | return -1; | |
270 | } | |
271 | if (lock.l_type == F_UNLCK) { | |
272 | return 0; /* not locked */ | |
273 | } | |
274 | return (lock.l_pid); /* return pid of locking process */ | |
275 | } | |
276 | ||
277 | ||
278 | #define Read_lock(fd) \ | |
279 | start_lock(fd, F_SETLK, F_RDLCK) | |
280 | #define Readw_lock(fd) \ | |
281 | start_lock(fd, F_SETLKW, F_RDLCK) | |
282 | #define Write_lock(fd) \ | |
283 | start_lock(fd, F_SETLK, F_WRLCK) | |
284 | #define Writew_lock(fd) \ | |
285 | start_lock(fd, F_SETLKW, F_WRLCK) | |
286 | #define Unlock(fd) \ | |
287 | start_lock(fd, F_SETLK, F_UNLCK) | |
288 | #define Is_readlock(fd) \ | |
289 | test_lock(fd, F_RDLCK) | |
290 | #define Is_writelock(fd) \ | |
291 | test_lock(fd, F_WRLCK) | |
292 | ||
293 | /* | |
294 | * communication between this process and weblog - sends user credentials | |
295 | * over a shared pipe (mutex provided using locks) and recieves either a | |
296 | * token or an error message | |
297 | */ | |
298 | static int | |
299 | request_Authentication(char *user, char *passwd, char *cell, char *type, | |
300 | char *tokenbuf, char *reason) | |
301 | { | |
302 | int len = 0; | |
303 | int pid; | |
304 | char *temp; | |
305 | int lockfd = 0; | |
306 | ||
307 | afsassert(user); | |
308 | afsassert(passwd); | |
309 | afsassert(cell); | |
310 | afsassert(type); | |
311 | afsassert(tokenbuf); | |
312 | ||
313 | /* | |
314 | * lock the pipe before beginning communication or in case of AIX it is an | |
315 | * error to attempt to lock a pipe or FIFO (EINVAL) therefore we have to create | |
316 | * a temporary file and use that fd instead | |
317 | */ | |
318 | #ifdef AIX | |
319 | lockfd = tempfd; | |
320 | #else | |
321 | lockfd = writePipe; | |
322 | #endif | |
323 | ||
324 | while ((pid = Is_writelock(lockfd)) != 0) { | |
325 | if (pid < 0) { | |
326 | afslog(5, | |
327 | ("%s: pid:%d Error locking pipe - %s", module_name, | |
328 | getpid(), strerror(errno))); | |
329 | return -1; | |
330 | } | |
331 | afslog(40, | |
332 | ("%s: pid %d waiting for lock held by pid %d", module_name, | |
333 | getpid(), pid)); | |
334 | } | |
335 | ||
336 | if (Write_lock(lockfd) == -1) { | |
337 | afslog(5, | |
338 | ("%s: pid:%d Error write lock - %s. Retrying with WriteW", | |
339 | module_name, getpid(), strerror(errno))); | |
340 | if (Writew_lock(lockfd) == -1) { | |
341 | afslog(5, | |
342 | ("%s: pid:%d Error write lock - %s", module_name, getpid(), | |
343 | strerror(errno))); | |
344 | return -1; | |
345 | } | |
346 | } | |
347 | ||
348 | if (sendTo_afsAuthenticator(user, passwd, cell, type) == -1) { | |
349 | Unlock(lockfd); | |
350 | afslog(5, ("%s: Error sending authentication info", module_name)); | |
351 | return -1; | |
352 | } | |
353 | ||
354 | len = recvFrom_afsAuthenticator(tokenbuf); | |
355 | ||
356 | /* release the lock */ | |
357 | if (Unlock(lockfd)) { | |
358 | afslog(5, ("%s: pid:%d Error unlocking", module_name, getpid())); | |
359 | return -1; | |
360 | } | |
361 | ||
362 | if (len > 0) { | |
363 | if (strncmp(tokenbuf, "FAILURE", 7) == 0) { | |
364 | temp = &tokenbuf[7]; | |
365 | strncpy(reason, temp, len); | |
366 | return -2; | |
367 | } | |
368 | } | |
369 | return len; | |
370 | } | |
371 | ||
372 | /* | |
373 | * pioctl setting token | |
374 | */ | |
375 | static int | |
376 | setToken(char *tokenBuf, int tokenLen) | |
377 | { | |
378 | char *temp; | |
379 | afs_int32 i = 0; | |
380 | ||
381 | afsassert(tokenBuf); | |
382 | ||
383 | /* | |
384 | * set the primary flag only if we haven't done a SETPAG previoulsy | |
385 | * by flipping this bit | |
386 | */ | |
387 | if (!doneSETPAG) { | |
388 | #ifdef OLDSETPAG | |
389 | /* skip over the secret token */ | |
390 | temp = tokenBuf; | |
391 | memcpy(&i, temp, sizeof(afs_int32)); | |
392 | temp += (i + sizeof(afs_int32)); | |
393 | ||
394 | /* skip over the clear token */ | |
395 | memcpy(&i, temp, sizeof(afs_int32)); | |
396 | temp += (i + sizeof(afs_int32)); | |
397 | ||
398 | doneSETPAG = 1; | |
399 | memcpy(&i, temp, sizeof(afs_int32)); | |
400 | i |= 0x8000; | |
401 | memcpy(temp, &i, sizeof(afs_int32)); | |
402 | temp += sizeof(afs_int32); | |
403 | #endif | |
404 | ||
405 | if (do_setpag()) { | |
406 | return -1; | |
407 | } | |
408 | doneSETPAG = 1; | |
409 | } | |
410 | return do_pioctl(tokenBuf, tokenLen, tokenBuf, tokenLen, VIOCSETTOK, NULL, | |
411 | 0); | |
412 | } | |
413 | ||
414 | /* | |
415 | * Get the token for the primary cell from the cache manager for this | |
416 | * process. Primary cell is the cell at the first index (index 0) | |
417 | */ | |
418 | static int | |
419 | getToken(char *buf, int bufsize) | |
420 | { | |
421 | /* get just the ONE token for this PAG from cache manager */ | |
422 | afs_int32 i = 0; | |
423 | memcpy((void *)buf, (void *)&i, sizeof(afs_int32)); | |
424 | return do_pioctl(buf, sizeof(afs_int32), buf, bufsize, VIOCGETTOK, NULL, | |
425 | 0); | |
426 | } | |
427 | ||
428 | ||
429 | /* | |
430 | * discard all authentication information for this PAG ie. this process | |
431 | */ | |
432 | int | |
433 | unlog() | |
434 | { | |
435 | return do_pioctl(0, 0, 0, 0, VIOCUNPAG, NULL, 0); | |
436 | } | |
437 | ||
438 | ||
439 | /* | |
440 | * Does the following things: | |
441 | * Checks whether there is a Basic Authentication header - obtains creds. | |
442 | * Checks local cache for the token associated with the user creds. | |
443 | * - if no token in cache - obtains token from weblog using pipes | |
444 | * - sets the token and returns appropriate return code | |
445 | * Return values: OK, SERVER_ERROR, AUTH_REQUIRED, FORBIDDEN | |
446 | */ | |
447 | int | |
448 | authenticateUser(request_rec * r, char *defaultCell, int cacheExpiration, | |
449 | char *type) | |
450 | { | |
451 | char user[APACHEAFS_USERNAME_MAX]; | |
452 | char passwd[APACHEAFS_PASSWORD_MAX]; | |
453 | char cell[APACHEAFS_CELLNAME_MAX]; | |
454 | char tokenbuf[MAXBUFF]; | |
455 | char cksum[SHA_HASH_BYTES]; | |
456 | int rc = 0; | |
457 | int i = 0; | |
458 | const char *auth_line; | |
459 | char reason[MAXBUFF]; /* if authentication failed - this is why */ | |
460 | char err_msg[MAXBUFF]; | |
461 | int userChanged = 0; | |
462 | ||
463 | afsassert(r); | |
464 | afsassert(r->uri); | |
465 | afsassert(defaultCell); | |
466 | afsassert(type); | |
467 | ||
468 | auth_line = TABLE_GET(r->headers_in, "Authorization"); | |
469 | ||
470 | if (strcmp(global_default_cell, defaultCell)) { | |
471 | strcpy(global_default_cell, defaultCell); | |
472 | } | |
473 | ||
474 | memset(user, 0, APACHEAFS_USERNAME_MAX); | |
475 | memset(passwd, 0, APACHEAFS_PASSWORD_MAX); | |
476 | memset(cell, 0, APACHEAFS_CELLNAME_MAX); | |
477 | ||
478 | if (auth_line == NULL) { /* No Authorization field - we don't do anything */ | |
479 | /* | |
480 | * No Authorization field recieved - that's fine by us. | |
481 | * go ahead and attempt to service the request and if we get | |
482 | * back FORBIDDEN then we'll take care of it then | |
483 | */ | |
484 | afslog(15, ("%s: No authline recieved", module_name)); | |
485 | haveAuth = 0; | |
486 | userChanged = 1; | |
487 | memset(lastUser, 0, APACHEAFS_USERNAME_MAX); | |
488 | memset(lastCell, 0, APACHEAFS_CELLNAME_MAX); | |
489 | memset(lastCksum, 0, SHA_HASH_BYTES); | |
490 | rc = unlog(); | |
491 | afslog(25, | |
492 | ("%s: pid:%d No Authorization field. Unlogging ...", | |
493 | module_name, getpid())); | |
494 | if (rc) { | |
495 | sprintf(err_msg, | |
496 | "%s: Error unlogging from AFS cell - rc: %d, errno:%d", | |
497 | module_name, rc, errno); | |
498 | LOG_REASON(err_msg, r->uri, r); | |
499 | return SERVER_ERROR; | |
500 | } | |
501 | return OK; | |
502 | } | |
503 | ||
504 | /* | |
505 | * We should get here only if there IS an Authorization field | |
506 | */ | |
507 | ||
508 | if ((rc = parse_authhdr(r, user, passwd, cell, defaultCell)) != 0) { | |
509 | sprintf(err_msg, "%s: Error parsing Authorization Header rc:%d", | |
510 | module_name, rc); | |
511 | LOG_REASON(err_msg, r->uri, r); | |
512 | return rc; /* SERVER ERROR */ | |
513 | } | |
514 | ||
515 | /* | |
516 | * should get here only after obtaining the username and password and cell | |
517 | * check to make sure anyway | |
518 | */ | |
519 | if ((user[0] == '\0') || (cell[0] == '\0') || (passwd[0] == '\0')) { | |
520 | afslog(15, | |
521 | ("%s: pid:%d No username or password or cell. Unlogging.", | |
522 | module_name, getpid())); | |
523 | haveAuth = 0; | |
524 | userChanged = 1; | |
525 | memset(lastUser, 0, APACHEAFS_USERNAME_MAX); | |
526 | memset(lastCell, 0, APACHEAFS_CELLNAME_MAX); | |
527 | memset(lastCksum, 0, SHA_HASH_BYTES); | |
528 | rc = unlog(); | |
529 | if (rc) { | |
530 | sprintf(err_msg, | |
531 | "%s: Error unlogging from AFS cell - rc: %d, errno:%d", | |
532 | module_name, rc, errno); | |
533 | LOG_REASON(err_msg, r->uri, r); | |
534 | return SERVER_ERROR; | |
535 | } | |
536 | setCellAuthHeader(r); | |
537 | return AUTH_REQUIRED; | |
538 | } | |
539 | #ifdef DEBUG | |
540 | fprintf(stderr, "Cell:%s\tUser:%s\tPasswd:%s\n", cell, user, passwd); | |
541 | #endif | |
542 | ||
543 | /* | |
544 | * compare with previous username/cell/cksum - update it | |
545 | * unlog if required | |
546 | */ | |
547 | ||
548 | weblog_login_checksum(user, cell, passwd, cksum); | |
549 | if (!haveAuth) { | |
550 | haveAuth = 1; | |
551 | strcpy(lastUser, user); | |
552 | strcpy(lastCksum, cksum); | |
553 | strcpy(lastCell, cell); | |
554 | } | |
555 | if (strcmp(user, lastUser) || strcmp(cell, lastCell) | |
556 | || strcmp(cksum, lastCksum)) { | |
557 | /* | |
558 | * unlog the old user from the cell if a new username/passwd is recievd | |
559 | */ | |
560 | ||
561 | userChanged = 1; | |
562 | afslog(25, | |
563 | ("%s: pid:%d\tUnlogging user %s from cell%s", module_name, | |
564 | getpid(), lastUser, lastCell)); | |
565 | afslog(25, ("%s:New user:%s\t New Cell:%s", module_name, user, cell)); | |
566 | afslog(25, ("%s:Trying to get URL:%s", module_name, r->uri)); | |
567 | afslog(25, ("%s: Unlogging ....", module_name)); | |
568 | ||
569 | if (unlog()) { | |
570 | sprintf(err_msg, | |
571 | "%s: Error unlogging from AFS cell - rc: %d, errno:%d", | |
572 | module_name, rc, errno); | |
573 | LOG_REASON(err_msg, r->uri, r); | |
574 | return SERVER_ERROR; | |
575 | } | |
576 | /* change lastUser to this user */ | |
577 | strcpy(lastUser, user); | |
578 | strcpy(lastCksum, cksum); | |
579 | strcpy(lastCell, cell); | |
580 | } | |
581 | ||
582 | /* strcmp checksums - ie. did the user change */ | |
583 | #ifndef NO_AFSAPACHE_CACHE | |
584 | if (!cache_initialized) { | |
585 | token_cache_init(); | |
586 | cache_initialized = 1; | |
587 | } | |
588 | ||
589 | /* have to check local cache for this name/passwd */ | |
590 | ||
591 | rc = check_Cache(user, passwd, cell, tokenbuf); | |
592 | if (rc) { | |
593 | /* if found then just send the request without going through | |
594 | * weblog - this means the user has already been authenticated | |
595 | * once and we have a valid token just need to set it - | |
596 | * only if it is different from the token already set. No need to | |
597 | * even unlog because this token is set for the entire PAG which | |
598 | * of course consists of just this child process | |
599 | */ | |
600 | afslog(35, | |
601 | ("%s: pid:%d found user %s's token (expires:%d) in cache", | |
602 | module_name, getpid(), user, | |
603 | (getExpiration(tokenbuf) - time(NULL)))); | |
604 | ||
605 | /* if the user changed then set this token else leave it since it should | |
606 | * be set */ | |
607 | if (userChanged) { | |
608 | /* set this token obtained from the local cache */ | |
609 | afslog(15, | |
610 | ("%s:pid:%d\t Setting cached token", module_name, | |
611 | getpid())); | |
612 | if (setToken(tokenbuf, rc)) { | |
613 | afslog(5, | |
614 | ("%s: pid:%d Failed to set token obtained from cache." | |
615 | "rc:%d errno:%d Token Expiration:%d", module_name, | |
616 | getpid(), rc, errno, | |
617 | (getExpiration(tokenbuf) - time(NULL)))); | |
618 | #ifdef DEBUG_CACHE | |
619 | parseToken(tokenbuf); | |
620 | #endif | |
621 | /* | |
622 | * BUG WORKAROUND: sometimes we fail while setting token | |
623 | * with errno ESRCH indicating the named cell | |
624 | * in the last field of the token is not recognized - but that's | |
625 | * not quite true according to parseToken()!! Possibly corrupted | |
626 | * tokens from the cache? | |
627 | * Anyway we just get a new token from weblog | |
628 | */ | |
629 | goto reqAuth; | |
630 | } | |
631 | } /* if userChanged */ | |
632 | else { | |
633 | /* if this is a child process getting the request for the first time | |
634 | * then there's no way this guy's got a token for us in which case | |
635 | * getToken should fail with EDOM and that means we should set the token | |
636 | * first and maybe set a static variable saying we have set a token? | |
637 | */ | |
638 | char temp[MAXBUFF]; | |
639 | if (getToken(temp, sizeof(temp))) { | |
640 | if (errno == EDOM) { | |
641 | /* try setting the cached token */ | |
642 | if (setToken(tokenbuf, rc)) { | |
643 | /* | |
644 | * same bug workaround here. ie. go to weblog if setting | |
645 | * the cached token fails. | |
646 | */ | |
647 | sprintf(err_msg, | |
648 | "%s: pid:%d Failed to set cached token." | |
649 | "errno:%d rc:%d", module_name, getpid(), | |
650 | errno, rc); | |
651 | LOG_REASON(err_msg, r->uri, r); | |
652 | goto reqAuth; | |
653 | } | |
654 | } else { | |
655 | /* and again for any getToken failure other than EDOM */ | |
656 | sprintf(err_msg, | |
657 | "%s: Failed to get token: errno:%d rc:%d", | |
658 | module_name, errno, rc); | |
659 | LOG_REASON(err_msg, r->uri, r); | |
660 | goto reqAuth; | |
661 | } | |
662 | } /* so we already have a token set since the gettoken succeeded */ | |
663 | } | |
664 | ||
665 | /* to set the REMOTE_USER environment variable */ | |
666 | strcpy(r->connection->user, user); | |
667 | return OK; | |
668 | } | |
669 | /* | |
670 | * else - request afs_Authenticator's for it and update local cache | |
671 | * then go about serving the request URI | |
672 | */ | |
673 | else { | |
674 | reqAuth: | |
675 | #endif /* NO_AFSAPACHE_CACHE */ | |
676 | ||
677 | rc = request_Authentication(user, passwd, cell, type, tokenbuf, | |
678 | reason); | |
679 | if (rc > 0) { | |
680 | /* we got back a token from weblog */ | |
681 | /* set the token with setToken */ | |
682 | if (setToken(tokenbuf, rc)) { | |
683 | sprintf(err_msg, | |
684 | "%s: Failed to set token given by weblog. errno:%d", | |
685 | module_name, errno); | |
686 | LOG_REASON(err_msg, r->uri, r); | |
687 | return SERVER_ERROR; | |
688 | } | |
689 | #ifdef DEBUG_TOKENS | |
690 | system("/usr/afsws/bin/tokens"); | |
691 | #endif | |
692 | ||
693 | #ifndef NO_AFSAPACHE_CACHE | |
694 | /* update local cache */ | |
695 | if (updateCache(user, passwd, cell, tokenbuf, cacheExpiration)) { | |
696 | sprintf(err_msg, "%s: Error updating cache", module_name); | |
697 | LOG_REASON(err_msg, r->uri, r); | |
698 | return SERVER_ERROR; | |
699 | } | |
700 | afslog(15, | |
701 | ("%s: pid:%d\t put user:%s tokens in cache", module_name, | |
702 | getpid(), user)); | |
703 | #endif /* NO_AFSAPACHE_CACHE */ | |
704 | ||
705 | /* now we've got a token, updated the cache and set it so we should | |
706 | * have no problems accessing AFS files - however if we do then | |
707 | * we handle it in afs_accessCheck() when the error comes back | |
708 | */ | |
709 | ||
710 | /* to set the REMOTE_USER environment variable to the username */ | |
711 | strcpy(r->connection->user, user); | |
712 | return OK; | |
713 | } else if (rc == -2) { | |
714 | sprintf(err_msg, | |
715 | ":%s: AFS authentication failed for %s@%s because %s", | |
716 | module_name, user, cell, reason); | |
717 | LOG_REASON(err_msg, r->uri, r); | |
718 | setCellAuthHeader(r); | |
719 | return AUTH_REQUIRED; | |
720 | } else if (rc == -1) { | |
721 | sprintf(err_msg, "%s: Error readiong from pipe. errno:%d", | |
722 | module_name, errno); | |
723 | LOG_REASON(err_msg, r->uri, r); | |
724 | return SERVER_ERROR; | |
725 | } | |
726 | ||
727 | else { | |
728 | /* | |
729 | * unknown error from weblog - this should not occur | |
730 | * if afs_Authenticator can't authenticate you, then return FORBIDDEN | |
731 | */ | |
732 | sprintf(err_msg, | |
733 | "%s: AFS could not authenticate user %s in cell %s." | |
734 | "Returning FORBIDDEN", module_name, user, cell); | |
735 | LOG_REASON(err_msg, r->uri, r); | |
736 | return FORBIDDEN; | |
737 | } | |
738 | #ifndef NO_AFSAPACHE_CACHE | |
739 | } | |
740 | #endif | |
741 | /* should never get here */ | |
742 | LOG_REASON("AFS Authentication: WE SHOULD NEVER GET HERE", r->uri, r); | |
743 | return SERVER_ERROR; | |
744 | } | |
745 | ||
746 | ||
747 | /* | |
748 | * pioctl call to get the cell name hosting the object specified by path. | |
749 | * returns 0 if successful -1 if failure. Assumes memory has been allocated | |
750 | * for cell. Used to set the www-authenticate header. | |
751 | */ | |
752 | static int | |
753 | get_cellname_from_path(char *apath, char *cell) | |
754 | { | |
755 | int rc; | |
756 | ||
757 | afsassert(apath); | |
758 | afsassert(cell); | |
759 | ||
760 | rc = do_pioctl(NULL, 0, cell, APACHEAFS_CELLNAME_MAX, VIOC_FILE_CELL_NAME, | |
761 | apath, 1); | |
762 | if (rc) | |
763 | afslog(30, | |
764 | ("%s: Error getting cell from path %s. errno:%d rc:%d", | |
765 | module_name, apath, errno, rc)); | |
766 | else | |
767 | afslog(30, | |
768 | ("%s: Obtained cell %s from path %s", module_name, cell, | |
769 | apath)); | |
770 | ||
771 | return rc; | |
772 | } | |
773 | ||
774 | /* | |
775 | * obtains the path to the file requested and sets things up to | |
776 | * call get_cell_by_name. | |
777 | * TODO: These could well be combined into one single function. | |
778 | */ | |
779 | static int | |
780 | getcellname(request_rec * r, char *buf) | |
781 | { | |
782 | int rc = 0; | |
783 | ||
784 | afsassert(r); | |
785 | afsassert(buf); | |
786 | ||
787 | if (r->filename) { | |
788 | rc = get_cellname_from_path(r->filename, buf); | |
789 | } else { | |
790 | char path[1024]; | |
791 | sprintf(path, "%s/%s", DOCUMENT_ROOT(r), r->uri); | |
792 | rc = get_cellname_from_path(path, buf); | |
793 | } | |
794 | return rc; | |
795 | } | |
796 | ||
797 | /* | |
798 | * Returns a part of the url upto the second slash in the buf | |
799 | */ | |
800 | static int | |
801 | geturi(request_rec * r, char *buf) | |
802 | { | |
803 | int rc = 0; | |
804 | char *pos; | |
805 | char *end; | |
806 | int i = 0; | |
807 | int max = 2; | |
808 | ||
809 | afsassert(r); | |
810 | afsassert(buf); | |
811 | ||
812 | memset(buf, 0, APACHEAFS_CELLNAME_MAX); | |
813 | pos = strchr(r->uri, '/'); | |
814 | if (pos != NULL) { | |
815 | pos++; | |
816 | for (i = 0; i < max; i++) { | |
817 | end = strchr(pos, '/'); | |
818 | if (end != NULL) { | |
819 | int len = strlen(pos) - strlen(end); | |
820 | strcat(buf, "/"); | |
821 | strncat(buf, pos, len); | |
822 | afslog(35, | |
823 | ("%s: Getting URI upto second slash buf:%s", | |
824 | module_name, buf)); | |
825 | pos += (len + 1); | |
826 | end = strchr(pos, '/'); | |
827 | if (end == NULL) { | |
828 | break; | |
829 | } | |
830 | } else { | |
831 | strcat(buf, pos); | |
832 | break; | |
833 | } | |
834 | } | |
835 | } else { | |
836 | strcpy(buf, " "); | |
837 | } | |
838 | return rc; | |
839 | } | |
840 | ||
841 | /* | |
842 | * This function recursively parses buf and places the output in msg | |
843 | * Eg. <%c%uUnknown> gets translated to the cellname that the file | |
844 | * resides in, failing which the first part of the uri failing which the | |
845 | * string Unknown | |
846 | */ | |
847 | static int | |
848 | parseAuthName_int(request_rec * r, char *buf, char *msg) | |
849 | { | |
850 | char *pos; | |
851 | char *end; | |
852 | int len = 0; | |
853 | int rc = 0; | |
854 | char blank[APACHEAFS_CELLNAME_MAX]; | |
855 | ||
856 | afsassert(r); | |
857 | afsassert(buf); | |
858 | afsassert(msg); | |
859 | ||
860 | memset(blank, 0, sizeof(blank)); | |
861 | afslog(50, | |
862 | ("%s: Parsing Authorization Required reply. buf:%s", module_name, | |
863 | buf)); | |
864 | ||
865 | pos = strchr(buf, '<'); | |
866 | if (pos) { | |
867 | len = strlen(pos); | |
868 | pos++; | |
869 | end = strchr(pos, '>'); | |
870 | if (end == NULL) { | |
871 | afslog(0, | |
872 | ("%s:Parse error for AUTH_REQUIRED reply - mismatched <", | |
873 | module_name)); | |
874 | fprintf(stderr, "Parse Error: mismatched <\n"); | |
875 | strncpy(msg, buf, strlen(buf) - len); | |
876 | afslog(0, ("%s: msg:%s", msg)); | |
877 | return -1; | |
878 | } | |
879 | end++; | |
880 | if (pos[0] == '%') { | |
881 | pos++; | |
882 | switch (pos[0]) { | |
883 | case 'c': | |
884 | rc = getcellname(r, blank); | |
885 | if (!rc) { | |
886 | strncpy(msg, buf, strlen(buf) - len); | |
887 | strcat(msg, blank); | |
888 | strcat(msg, end); | |
889 | return 0; | |
890 | } | |
891 | break; | |
892 | ||
893 | case 'u': | |
894 | rc = geturi(r, blank); | |
895 | if (!rc) { | |
896 | strncpy(msg, buf, strlen(buf) - len); | |
897 | strcat(msg, blank); | |
898 | strcat(msg, end); | |
899 | return 0; | |
900 | } | |
901 | break; | |
902 | ||
903 | case 'd': | |
904 | if (global_default_cell != NULL) { | |
905 | strncpy(msg, buf, strlen(buf) - len); | |
906 | strcat(msg, global_default_cell); | |
907 | strcat(msg, end); | |
908 | return 0; | |
909 | } | |
910 | break; | |
911 | } | |
912 | strncpy(msg, buf, strlen(buf) - len); | |
913 | strcat(msg, "<"); | |
914 | pos++; | |
915 | strcat(msg, pos); | |
916 | strcpy(buf, msg); | |
917 | memset(msg, 0, 1024); | |
918 | parseAuthName_int(r, buf, msg); | |
919 | return 0; | |
920 | } else { | |
921 | strncpy(msg, buf, strlen(buf) - len); | |
922 | strncat(msg, pos, strlen(pos) - strlen(end) - 1); | |
923 | strcat(msg, end); | |
924 | return 0; | |
925 | } | |
926 | } | |
927 | } | |
928 | ||
929 | /* | |
930 | * Parses the entire auth_name string - ie. takes care of multiple | |
931 | * <%...> <%...> | |
932 | */ | |
933 | static int | |
934 | parseAuthName(request_rec * r, char *buf) | |
935 | { | |
936 | char *pos; | |
937 | int rc; | |
938 | char msg[1024]; | |
939 | ||
940 | afsassert(r); | |
941 | afsassert(buf); | |
942 | ||
943 | memset(msg, 0, sizeof(msg)); | |
944 | ||
945 | pos = strchr(buf, '<'); | |
946 | while (pos != NULL) { | |
947 | rc = parseAuthName_int(r, buf, msg); | |
948 | if (rc) { | |
949 | strcpy(buf, msg); | |
950 | afslog(35, | |
951 | ("%s: Failed to parse Auth Name. buf:%s", module_name, | |
952 | buf)); | |
953 | return -1; | |
954 | } | |
955 | strcpy(buf, msg); | |
956 | memset(msg, 0, sizeof(msg)); | |
957 | pos = strchr(buf, '<'); | |
958 | } | |
959 | afslog(50, | |
960 | ("%s: Parsing WWW Auth required reply. final message:%s", | |
961 | module_name, buf)); | |
962 | return 0; | |
963 | } | |
964 | ||
965 | ||
966 | /* | |
967 | * Set the www-authenticate header - this is the login prompt the users see | |
968 | */ | |
969 | static int | |
970 | setCellAuthHeader(request_rec * r) | |
971 | { | |
972 | char *name; | |
973 | char buf[1024]; | |
974 | int rc = 0; | |
975 | ||
976 | afsassert(r); | |
977 | ||
978 | name = (char *)get_afs_authprompt(r); | |
979 | if (name != NULL) { | |
980 | strcpy(buf, name); | |
981 | rc = parseAuthName(r, buf); | |
982 | } else { | |
983 | strcpy(buf, " "); | |
984 | } | |
985 | TABLE_SET(r->err_headers_out, "WWW-Authenticate", | |
986 | PSTRCAT(r->pool, "Basic realm=\"", buf, "\"", NULL)); | |
987 | return rc; | |
988 | } | |
989 | ||
990 | ||
991 | /* | |
992 | * Checks if we have some authentication credentials, if we do returns | |
993 | * FORBIDDEN and if we don't then returns AUTH_REQUIRED with the appropriate | |
994 | * www-authenticate header. Should be called if we can't access a file because | |
995 | * permission is denied. | |
996 | */ | |
997 | int | |
998 | forbToAuthReqd(request_rec * r) | |
999 | { | |
1000 | if (haveAuth) { | |
1001 | return FORBIDDEN; | |
1002 | } else { | |
1003 | setCellAuthHeader(r); | |
1004 | return AUTH_REQUIRED; | |
1005 | } | |
1006 | } |