reindent-20030715
[openafs.git] / src / afsweb / apache_afs_client.c
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     register 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 }