Standardize License information
[openafs.git] / src / afsweb / nsafs.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  * User space client specific interface glue
12  */
13
14 #include "../afs/param.h"       /* Should be always first */
15 #include "../afs/sysincludes.h" /* Standard vendor system headers */
16 #include <net/if.h>
17 #include "../afs/afsincludes.h" /* Afs-based standard headers */
18 #include "../afs/afs_stats.h"
19 #include "../afs/afs_usrops.h"
20 #include "../afs/auth.h"
21 #include "../afs/cellconfig.h"
22 #include "../afs/vice.h"
23 #include "../afs/kautils.h"
24 #include "../afs/nsafs.h"
25
26 #define NSAFS_DFLT_RCVTHREADS   2       /* Dflt number recevice threads */
27 #define NSAFS_BUFFER_SIZE       4096    /* Send/Receive buffer size */
28 #define NSAFS_MAX_PATH          1024    /* Maximum path length */
29 #define NSAFS_USERNAME_MAX      64      /* Maximum username length */
30 #define NSAFS_PASSWORD_MAX      64      /* Maximum password length */
31 #define NSAFS_LOGIN_HASH_SIZE   1024    /* MUST be power of two */
32 #define TEN_MINUTES             600     /* 10 minutes = 600 seconds */
33
34 #define NSAFS_DIR_ALLOW         "GET,HEAD,MOVE,INDEX,RMDIR"
35 #define NSAFS_LINK_ALLOW        "GET,HEAD,MOVE,DELETE"
36 #define NSAFS_FILE_ALLOW        "GET,HEAD,MOVE,PUT,DELETE"
37
38 #ifndef MAX
39 #define MAX(A,B)                ((A)>(B)?(A):(B))
40 #endif /* !MAX */
41 #ifndef MIN
42 #define MIN(A,B)                ((A)<(B)?(A):(B))
43 #endif /* !MAX */
44
45 /*
46  * Used by KA module to get local cell info
47  */
48 struct afsconf_dir *KA_conf;
49
50 /*
51  * Initialization parameters. The plugin is initialized in
52  * the Netscape parent process, but we don't initialize AFS
53  * until we are in the child process.
54  */
55 CRITICAL nsafs_init_lock;
56 int nsafs_init_done;
57 char *mountDirParam;
58 char *cellNameParam;
59 char *confDirParam;
60 char *logFileParam;
61 char *cacheBaseDirParam;
62 int cacheBlocksParam;
63 int cacheFilesParam;
64 int cacheStatEntriesParam;
65 int dCacheSizeParam;
66 int vCacheSizeParam;
67 int chunkSizeParam;
68 int debugParam;
69 int nDaemonsParam;
70 long maxExpirationParam;
71
72 /*
73  * Structure user to store entries in AFS login cache
74  */
75 struct nsafs_login {
76     afs_uint32 expiration;
77     long viceid;
78     long group0;
79     long group1;
80     char username[NSAFS_USERNAME_MAX];
81     char cellname[NSAFS_USERNAME_MAX];
82     char cksum[SHA_HASH_BYTES];
83     struct nsafs_login *next;
84     struct nsafs_login *prev;
85 };
86
87 /*
88  * Value used to initialize SHA checksums on username/password pairs
89  */
90 afs_uint32 nsafs_login_pad[SHA_HASH_INTS] = {
91     0x0D544971, 0x2281AC5B, 0x58B51218, 0x4085E08D, 0xB68C484B
92 };
93
94 /*
95  * Cache of AFS logins
96  */
97 CRITICAL nsafs_login_lock;
98 struct {
99     struct nsafs_login *head;
100     struct nsafs_login *tail;
101 } nsafs_login_cache[NSAFS_LOGIN_HASH_SIZE];
102
103 /*
104  * Mapping from characters to 64 bit values
105  */
106 static int base64_to_value[256] = {
107     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112     0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f,
113     0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
114     0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115     0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
116     0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
117     0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
118     0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
119     0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
120     0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
121     0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
122     0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
123     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
139 };
140
141 /*
142  * Decode a base64 encoded buffer in place
143  */
144 void nsafs_decode64(char *buf)
145 {
146     int i,j;
147     int len;
148     int val1;
149     int val2;
150     int val3;
151     int val4;
152
153     /*
154      * Allow trailing blanks
155      */
156     for (len = strlen(buf) ; buf[len-1] == ' ' && len > 0 ; len--);
157
158     /*
159      * Valid encodings are multiples of four characters
160      */
161     if (len & 0x3) {
162         buf[0] = '\0';
163         return;
164     }
165
166     for (i = 0, j = 0 ; i < len ; i += 4, j += 3) {
167         val1 = base64_to_value[buf[i]];
168         val2 = base64_to_value[buf[i+1]];
169         val3 = base64_to_value[buf[i+2]];
170         val4 = base64_to_value[buf[i+3]];
171         buf[j] = ((val1<<2)&0xfc) | ((val2>>4)&0x3);
172         buf[j+1] = ((val2<<4)&0xf0) | ((val3>>2)&0xf);
173         buf[j+2] = ((val3<<6)&0xc0) | (val4&0x3f);
174     }
175     buf[j] = '\0';
176 }
177
178
179 /*
180  * Interface for pioctls - used for unlogging 
181  */
182 #include "../afs/venus.h"
183 int do_pioctl(char *in_buffer, int in_size, 
184               char *out_buffer, int out_size, 
185               int opcode, char *path, int followSymLinks)
186 {
187   struct ViceIoctl iob;
188   iob.in = in_buffer;
189   iob.in_size = in_size;
190   iob.out = out_buffer;
191   iob.out_size = out_size;
192   
193 #ifdef AFS_USR_SUN5_ENV
194   return syscall(AFS_SYSCALL, AFSCALL_PIOCTL, path, _VICEIOCTL(opcode), &iob, 
195                  followSymLinks);
196 #else /* AFS_USR_SUN5_ENV */
197   return lpioctl(path, _VICEIOCTL(opcode), &iob, followSymLinks);
198 #endif /* AFS_USR_SUN5_ENV */
199 }
200
201 /*
202  * unlog - invalidate any existing AFS tokens with the kernel cache
203  * manager. In case the server is started up with tokens
204  */
205 int unlog()
206 {
207   return do_pioctl(NULL, 0, NULL, 0, VIOCUNPAG, NULL, 0); 
208 }
209
210 /*
211  * Initialize the AFS client and the login cache
212  */
213 void nsafs_init_once()
214 {
215     int i;
216     crit_enter(nsafs_init_lock);
217     if (nsafs_init_done == 0) {
218       i=unlog();
219       if (i) {
220 /*      printf("unlog from AFS failed: errno:%d\n", errno); */
221       }
222         uafs_Init("nsafs-init", mountDirParam, confDirParam,
223                   cacheBaseDirParam, cacheBlocksParam, cacheFilesParam,
224                   cacheStatEntriesParam, dCacheSizeParam, vCacheSizeParam,
225                   chunkSizeParam, 0, debugParam, nDaemonsParam, -1,
226                   logFileParam);
227         nsafs_login_lock = crit_init();
228         for (i = 0 ; i < NSAFS_LOGIN_HASH_SIZE ; i++) {
229             DLL_INIT_LIST(nsafs_login_cache[i].head,
230                           nsafs_login_cache[i].tail);
231         }
232         KA_conf = afs_cdir;
233         nsafs_init_done = 1;
234     }
235     crit_exit(nsafs_init_lock);
236 }
237
238 /*
239  * Hash function for the AFS login cache
240  */
241 int nsafs_login_hash(char *name, char *cell)
242 {
243     char *p;
244     afs_uint32 val;
245     for (val = *name , p = name ; *p != '\0' ; p++) {
246         val = (val << 2) ^ val ^ (afs_uint32)(*p);
247     }
248     for (p = cell ; *p != '\0' ; p++) {
249         val = (val << 2) ^ val ^ (afs_uint32)(*p);
250     }
251     return val & (NSAFS_LOGIN_HASH_SIZE-1);
252 }
253
254 /*
255  * Compute a SHA checksum on the username, cellname, and password
256  */
257 void nsafs_login_checksum(
258     char *user,
259     char *cell,
260     char *passwd,
261     char *cksum)
262 {
263     int passwdLen;
264     int userLen;
265     int cellLen;
266     char *shaBuffer;
267     shaState state;
268
269     /*
270      * Compute SHA(username,SHA(password,pad))
271      */
272     passwdLen = strlen(passwd);
273     userLen = strlen(user);
274     cellLen = strlen(cell);
275     shaBuffer = afs_osi_Alloc(MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
276     strcpy(shaBuffer, passwd);
277     memcpy((void *)(shaBuffer + passwdLen), (void *)(&nsafs_login_pad[0]),
278            SHA_HASH_BYTES);
279     sha_clear(&state);
280     sha_hash(&state, shaBuffer, passwdLen+SHA_HASH_BYTES);
281     memcpy(shaBuffer, user, userLen);
282     memcpy(shaBuffer+userLen, cell, cellLen);
283     sha_bytes(&state, shaBuffer+userLen+cellLen);
284     sha_clear(&state);
285     sha_hash(&state, shaBuffer, userLen+cellLen+SHA_HASH_BYTES);
286     sha_bytes(&state, &cksum[0]);
287     memset(shaBuffer, 0, MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
288     afs_osi_Free(shaBuffer, MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
289 }
290
291 /*
292  * Set the AFS identity given from the group0 and group1 strings
293  */
294 void nsafs_set_id_from_ints(int viceid, int group0, int group1)
295 {
296     int i;
297     struct usr_ucred *crp;
298
299     u.u_viceid = viceid;
300     crp = u.u_cred;
301     crp->cr_uid = viceid;
302     crp->cr_ruid = viceid;
303     crp->cr_suid = viceid;
304     crp->cr_groups[0] = group0;
305     crp->cr_groups[1] = group1;
306     crp->cr_groups[2] = getgid();
307     crp->cr_ngroups = 1;
308     for (i = 3 ; i < NGROUPS ; i++) {
309         crp->cr_groups[i] = NOGROUP;
310     }
311 }
312
313 /*
314  * Set the AFS identity given from the viceid, group0 and group1 strings
315  */
316 void nsafs_set_id_from_strings(char *viceid, char *group0, char *group1)
317 {
318     int i;
319     struct usr_ucred *crp;
320
321     if (viceid != NULL && group0 != NULL && group1 != NULL) {
322         nsafs_set_id_from_ints(atoi(viceid), atoi(group0), atoi(group1));
323     } else {
324         u.u_viceid = getuid();
325         crp = u.u_cred;
326         crp->cr_uid = getuid();
327         crp->cr_ruid = getuid();
328         crp->cr_suid = getuid();
329         crp->cr_groups[0] = getgid();
330         crp->cr_ngroups = 1;
331         for (i = 1 ; i < NGROUPS ; i++) {
332             crp->cr_groups[i] = NOGROUP;
333         }
334     }
335 }
336
337 /*
338  * Look up a login ID in the cache. If an entry name is found for the
339  * given username, and the SHA checksums match, then set the group0
340  * and group1 parameters and return 1, otherwise return 0.
341  */
342 int nsafs_login_lookup(
343     char *user,
344     char *cell,
345     char *cksum,
346     int *viceid,
347     int *group0,
348     int *group1)
349 {
350     int index;
351     long curTime;
352     struct nsafs_login *loginP, *tmpP, loginTmp;
353
354     /*
355      * Search the hash chain for a matching entry, free
356      * expired entries as we search
357      */
358     index = nsafs_login_hash(user, cell);
359     curTime = time(NULL);
360     crit_enter(nsafs_login_lock);
361     loginP = nsafs_login_cache[index].head;
362     while (loginP != NULL) {
363         if (loginP->expiration < curTime) {
364             tmpP = loginP;
365             loginP = tmpP->next;
366             nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
367             uafs_unlog();
368             DLL_DELETE(tmpP, nsafs_login_cache[index].head,
369                        nsafs_login_cache[index].tail, next, prev);
370             afs_osi_Free(tmpP, sizeof(struct nsafs_login));
371             continue;
372         }
373         if (strcmp(loginP->username, user) == 0 &&
374             strcmp(loginP->cellname, cell) == 0 &&
375             memcmp((void *)&loginP->cksum[0], (void *)cksum,
376                    SHA_HASH_BYTES) == 0) {
377             *viceid = loginP->viceid;
378             *group0 = loginP->group0;
379             *group1 = loginP->group1;
380             crit_exit(nsafs_login_lock);
381             return 1;
382         }
383         loginP = loginP->next;
384     }
385     crit_exit(nsafs_login_lock);
386     return 0;
387 }
388
389 /*
390  * Insert a login ID into the cache. If the user already has an entry,
391  * then overwrite the old entry.
392  */
393 int nsafs_login_store(
394     char *user,
395     char *cell,
396     char *cksum,
397     int viceid,
398     int group0,
399     int group1,
400     afs_uint32 expiration)
401 {
402     int index;
403     long curTime;
404     struct nsafs_login *loginP, *tmpP, loginTmp;
405
406     /*
407      * Search the hash chain for a matching entry, free
408      * expired entries as we search
409      */
410     index = nsafs_login_hash(user, cell);
411     curTime = time(NULL);
412     crit_enter(nsafs_login_lock);
413     loginP = nsafs_login_cache[index].head;
414     while (loginP != NULL) {
415         if (strcmp(loginP->username, user) == 0 &&
416             strcmp(loginP->cellname, cell) == 0) {
417             break;
418         }
419         if (loginP->expiration < curTime) {
420             tmpP = loginP;
421             loginP = tmpP->next;
422             nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
423             uafs_unlog();
424             DLL_DELETE(tmpP, nsafs_login_cache[index].head,
425                        nsafs_login_cache[index].tail, next, prev);
426             afs_osi_Free(tmpP, sizeof(struct nsafs_login));
427             continue;
428         }
429         loginP = loginP->next;
430     }
431     if (loginP == NULL) {
432         loginP = (struct nsafs_login *)
433                  afs_osi_Alloc(sizeof(struct nsafs_login));
434         strcpy(&loginP->username[0], user);
435         strcpy(&loginP->cellname[0], cell);
436     } else {
437         DLL_DELETE(loginP, nsafs_login_cache[index].head,
438                    nsafs_login_cache[index].tail, next, prev);
439         nsafs_set_id_from_ints(loginP->viceid, loginP->group0, loginP->group1);
440         uafs_unlog();
441     }
442     nsafs_set_id_from_ints(viceid, group0, group1);
443     memcpy((void *)&loginP->cksum[0], (void *)cksum, SHA_HASH_BYTES);
444     loginP->viceid = viceid;
445     loginP->group0 = group0;
446     loginP->group1 = group1;
447     loginP->expiration = expiration;
448     DLL_INSERT_TAIL(loginP, nsafs_login_cache[index].head,
449                     nsafs_login_cache[index].tail, next, prev);
450     crit_exit(nsafs_login_lock);
451     return 0;
452 }
453
454 /*
455  * Extract a string parameter from the parameter block
456  */
457 int nsafs_get_string(
458     char **paramP,
459     char *dflt,
460     char *name,
461     pblock *pb,
462     Session *sn,
463     Request *rq)
464 {
465     char *tmpPtr;
466     char error[128];
467
468     tmpPtr = pblock_findval(name, pb);
469     if(tmpPtr == NULL) {
470         if (dflt == NULL) {
471             log_error(LOG_MISCONFIG, "nsafs", sn, rq,
472                       "nsafs_init: please supply a %s parameter", name);
473             return REQ_ABORTED;
474         } else {
475             tmpPtr = dflt;
476         }
477     }
478     *paramP = afs_osi_Alloc(strlen(tmpPtr)+1);
479     strcpy(*paramP, tmpPtr);
480     return REQ_PROCEED;
481 }
482
483 /*
484  * Extract a long integer parameter from the parameter block
485  */
486 int nsafs_get_long(
487     long *paramP,
488     long dflt,
489     char *name,
490     pblock *pb,
491     Session *sn,
492     Request *rq)
493 {
494     char *start, *end;
495     long val;
496
497     start = pblock_findval(name, pb);
498     if(start == NULL) {
499         if (dflt < 0) {
500             log_error(LOG_MISCONFIG, "nsafs", sn, rq,
501                       "nsafs_init: please supply a %s parameter", name);
502             return REQ_ABORTED;
503         } else {
504             *paramP = dflt;
505             return REQ_PROCEED;
506         }
507     }
508     val = strtol(start, &end, 10);
509     if (val <= 0 || end == start || *end != '\0') {
510         log_error(LOG_MISCONFIG, "nsafs", sn, rq,
511                   "nsafs_init: invalid %s parameter '%s'", name, start);
512         return REQ_ABORTED;
513     }
514     *paramP = val;
515     return REQ_PROCEED;
516 }
517
518 /*
519  * Extract an integer parameter from the parameter block
520  */
521 int nsafs_get_int(
522     int *paramP,
523     int dflt,
524     char *name,
525     pblock *pb,
526     Session *sn,
527     Request *rq)
528 {
529     int code;
530     long val;
531
532     code = nsafs_get_long(&val, (long)dflt, name, pb, sn, rq);
533     if (code == REQ_PROCEED) {
534         *paramP = val;
535     }
536     return code;
537 }
538
539 /*
540  * Parse the authorization header for username and password
541  */
542 void nsafs_parse_authhdr(
543     char *authHdr,
544     char *user,
545     char *cell,
546     char *passwd)
547 {
548     int i;
549     char *p;
550
551     user[0] = '\0';
552     passwd[0] = '\0';
553     cell[0] = '\0';
554
555     /*
556      * Skip leading blanks, check for basic authentication
557      */
558     for (p = authHdr ; *p == ' ' && *p != '\0' ; p++);
559     if (strncasecmp(p, "basic ", 6) != 0) {
560         return;
561     }
562     for (p += 6 ; *p == ' ' ; p++);
563
564     /*
565      * Username and password are base64 encoded
566      */
567     nsafs_decode64(p);
568
569     /*
570      * Format is user@cell:passwd. The user, cell or passwd may be missing
571      */
572     for ( i = 0 ; *p != '@' && *p != ':' && *p != '\0' ; p++ , i++) {
573         user[i] = *p;
574     }
575     user[i] = '\0';
576     if (*p == '@') {
577         for (i = 0 , p++ ; *p != ':' && *p != '\0' ; p++ , i++) {
578             cell[i] = *p;
579         }
580         cell[i] = '\0';
581     }
582     if (*p == ':') {
583         for (i = 0 , p++ ; *p != '\0' ; p++ , i++) {
584             passwd[i] = *p;
585         }
586         passwd[i] = '\0';
587     }
588 }
589
590 /*
591  * Return an appropriate error given a system errno
592  */
593 int nsafs_error_check(
594     int code,
595     char *text,
596     pblock *pb,
597     Session *sn,
598     Request *rq)
599 {
600     char txtbuf[256];
601     char *realmBuf;
602     char *path;
603     char *tmp;
604
605     if (text != NULL) {
606         sprintf(txtbuf, "%s: %s\n", text, strerror(code));
607     } else {
608         sprintf(txtbuf, "%s\n", strerror(code));
609     }
610
611     /*
612      * Special case, if we get EACCES inside a public directory
613      * and are unauthenticated, change the error to EPERM unless
614      * nsafs_nocheck is set. If nsafs_nocheck is set then change
615      * EPERM to EACCES
616      */
617     if (code == EACCES &&
618         pblock_findval("nsafs_nocheck", rq->vars) == NULL &&
619         (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
620          pblock_findval("nsafs_group0", rq->vars) == NULL ||
621          pblock_findval("nsafs_group1", rq->vars) == NULL)) {
622         code = EPERM;
623     } else if (code == EPERM &&
624                pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
625         char *status=pblock_findval("status",rq->vars);
626         if (strcmp(status,"Login Failed"))
627           code = EACCES;
628     }
629
630     switch (code) {
631       case EPERM:
632         /*
633          * We overload EPERM (not super-user) to mean unauthenticated.
634          * We use the first subdirectory beneath the AFS mount point
635          * as the realm.
636          */
637         log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
638         protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
639         path = pblock_findval("path", rq->vars);
640         realmBuf = NULL;
641         if(path != NULL) {
642             path = uafs_afsPathName(path);
643         }
644         if (path != NULL && *path != '\0') {
645             realmBuf = strdup(path);
646         }
647         if (realmBuf == NULL) {
648             /* Don't dump core, just make AFS into one big realm */
649             sprintf(txtbuf, "Basic realm=\"%s\"", afs_mountDir);
650         } else {
651             /* extract the first subdirectory in AFS */
652             if ((tmp = strchr(realmBuf, '/')) != NULL)
653                 *tmp = '\0';
654             sprintf(txtbuf, "Basic realm=\"%s/%s\"", afs_mountDir, realmBuf);
655             free(realmBuf);
656         }
657         pblock_nvinsert("WWW-authenticate", txtbuf, rq->srvhdrs);
658         break;
659       case EACCES:
660         log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
661         protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
662         break;
663       case ENOENT:
664       case ENOTDIR:
665         log_error(LOG_INFORM, "nsafs", sn, rq, txtbuf);
666         protocol_status(sn, rq, PROTOCOL_NOT_FOUND, NULL);
667         break;
668       default:
669         log_error(LOG_FAILURE, "nsafs", sn, rq, txtbuf);
670         protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
671         break;
672     }
673     return REQ_ABORTED;
674 }
675
676 /*
677  * Check the preconditions on a request. Return REQ_PROCEED
678  * if the preconditions are met. Any other return value means
679  * that the request has been aborted.
680  */
681 int nsafs_check_preconditions(
682     struct stat *stp,
683     pblock *pb,
684     Session *sn,
685     Request *rq)
686 {
687     int code;
688     time_t mtime;
689     struct tm tms, *tmsp;
690     char *reqhdr;
691
692     mtime = stp->st_mtime;
693     tmsp = system_gmtime(&mtime, &tms);
694
695     request_header("if-modified-since", &reqhdr, sn, rq);
696     if (reqhdr != NULL && util_later_than(tmsp, reqhdr)) {
697         protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL);
698         return REQ_ABORTED;
699     }
700
701     request_header("if-unmodified-since", &reqhdr, sn, rq);
702     if (reqhdr != NULL && !util_later_than(tmsp, reqhdr)) {
703         protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL);
704         return REQ_ABORTED;
705     }
706
707     return REQ_PROCEED;
708 }
709
710 /*
711  * Set the content-length and last-modified response header
712  *
713  * We used to call protocol_set_finfo, but it wasn't handling
714  * if-unmodified-since headers correctly.
715  */
716 int nsafs_set_finfo(
717     Session *sn,
718     Request *rq,
719     struct stat *stp)
720 {
721     int code;
722     time_t mtime;
723     struct tm tms, *tmsp;
724     char *reqhdr;
725     char dateStr[128];
726     char *days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
727     char *months[] = {"Jan","Feb","Mar","Apr","May","Jun",
728                       "Jul","Aug","Sep","Oct","Nov","Dec"};
729
730     mtime = stp->st_mtime;
731     tmsp = system_gmtime(&mtime, &tms);
732     sprintf(&dateStr[0],"%s, %02d %s %d %02d:%02d:%02d GMT",
733             days[tmsp->tm_wday], tmsp->tm_mday,
734             months[tmsp->tm_mon], tmsp->tm_year+1900,
735             tmsp->tm_hour, tmsp->tm_min, tmsp->tm_sec);
736     pblock_nvinsert("Last-Modified", &dateStr[0], rq->srvhdrs);
737     pblock_nninsert("Content-Length", stp->st_size, rq->srvhdrs);
738
739     return REQ_PROCEED;
740 }
741
742 /*
743  * Initialize the AFS plugin. We do not initialize the AFS client
744  * here because we are still in the parent process. We don't
745  * initialize AFS until we get the first service request.
746  */
747 NSAPI_PUBLIC int nsafs_init(pblock *pb, Session *sn, Request *rq)
748 {
749     int code;
750
751     nsafs_init_done = 0;
752     nsafs_init_lock = crit_init();
753
754     /*
755      * Parse the startup parameters
756      */
757     code = nsafs_get_string(&mountDirParam, "/afs", "mount", pb, sn, rq);
758     if (code != REQ_PROCEED) {
759         return code;
760     }
761     code = nsafs_get_string(&cellNameParam, NULL, "cell", pb, sn, rq);
762     if (code != REQ_PROCEED) {
763         return code;
764     }
765     code = nsafs_get_string(&confDirParam, NULL, "confdir", pb, sn, rq);
766     if (code != REQ_PROCEED) {
767         return code;
768     }
769     code = nsafs_get_string(&cacheBaseDirParam, NULL, "cachedir", pb, sn, rq);
770     if (code != REQ_PROCEED) {
771         return code;
772     }
773     code = nsafs_get_int(&cacheBlocksParam, -1, "blocks", pb, sn, rq);
774     if (code != REQ_PROCEED) {
775         return code;
776     }
777     code = nsafs_get_int(&cacheFilesParam, 0, "files", pb, sn, rq);
778     if (code != REQ_PROCEED) {
779         return code;
780     }
781     code = nsafs_get_int(&cacheStatEntriesParam, -1, "stat", pb, sn, rq);
782     if (code != REQ_PROCEED) {
783         return code;
784     }
785     code = nsafs_get_int(&nDaemonsParam, -1, "daemons", pb, sn, rq);
786     if (code != REQ_PROCEED) {
787         return code;
788     }
789     code = nsafs_get_int(&dCacheSizeParam, 0, "dcache", pb, sn, rq);
790     if (code != REQ_PROCEED) {
791         return code;
792     }
793     code = nsafs_get_int(&vCacheSizeParam, 0, "volumes", pb, sn, rq);
794     if (code != REQ_PROCEED) {
795         return code;
796     }
797     code = nsafs_get_int(&chunkSizeParam, 0, "chunksize", pb, sn, rq);
798     if (code != REQ_PROCEED) {
799         return code;
800     }
801     code = nsafs_get_int(&debugParam, 0, "debug", pb, sn, rq);
802     if (code != REQ_PROCEED) {
803         return code;
804     }
805     code = nsafs_get_string(&logFileParam, NULL, "logfile", pb, sn, rq);
806     if (code != REQ_PROCEED) {
807         return code;
808     }
809     code = nsafs_get_long(&maxExpirationParam, LONG_MAX, "exp-max", pb, sn, rq);
810     if (code != REQ_PROCEED) {
811         return code;
812     }
813
814     return REQ_PROCEED;
815 }
816
817 /*
818  * Extract name strings from a comma separated list
819  */
820 char *nsafs_NameFromNames(char *last, char *list, int *pos)
821 {
822     int len;
823     char *start;
824     char *retVal;
825
826     if (last == NULL) {
827         *pos = 0;
828     } else {
829         afs_osi_Free(last, strlen(last)+1);
830     }
831     start = &list[*pos];
832     if (*start == '\0') {
833         return NULL;
834     }
835     for (len = 0 ; start[len] != ',' && start[len] != '\0' ; len++);
836     *pos += len;
837     if (list[*pos] == ',') {
838         *pos += 1;
839     }
840     retVal = afs_osi_Alloc(len+1);
841     memcpy(retVal, start, len);
842     retVal[len] = '\0';
843     return retVal;
844 }
845
846 /*
847  * Authcheck function for AFS
848  *
849  * Check for an Authorization header. If there is one then do the
850  * AFS login and place the first two groups in the user's creds
851  * into the nsafs-group1 and nsafs-group2 request variables.
852  * Send an Unauthorized response if login fails to prompt the user
853  * to reenter the username and password.
854  */
855 NSAPI_PUBLIC int nsafs_basic(pblock *pb, Session *sn, Request *rq)
856 {
857     int i;
858     int rc;
859     int code;
860     int pos;
861     char *authHdr;
862     char *cellP;
863     char user[NSAFS_USERNAME_MAX];
864     char cell[NSAFS_USERNAME_MAX];
865     char passwd[NSAFS_PASSWORD_MAX];
866     char cksum[SHA_HASH_BYTES];
867     struct usr_ucred *crp;
868     afs_int32 password_expires = -1;
869     int viceid;
870     int group0;
871     int group1;
872     afs_uint32 expiration;
873     char *reason;
874     char txtbuf[128];
875
876     if (nsafs_init_done == 0) {
877         nsafs_init_once();
878     }
879
880     /*
881      * Get the authorization header, if none found then continue,
882      * we check whether authorization is required later on.
883      */
884     request_header("authorization", &authHdr, sn, rq);
885     if (authHdr == NULL || strlen(authHdr) == 0) {
886         return REQ_NOACTION;
887     }
888
889     /*
890      * Get the user name and password from the authorization header
891      */
892     nsafs_parse_authhdr(authHdr, &user[0], &cell[0], &passwd[0]);
893     if (user[0] == '\0' || passwd[0] == '\0') {
894         memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
895         pblock_nvinsert("status","Login Failed",rq->vars);
896         return nsafs_error_check(EPERM, "Invalid auth header", pb, sn, rq);
897     }
898     if (cell[0] == '\0') {
899         strcpy(cell, afs_LclCellName);
900     }
901
902     /*
903      * Lookup the username and password in the login cache.
904      * If we find a match we reuse the existing identity.
905      */
906     nsafs_login_checksum(user, cell, passwd, &cksum[0]);
907     rc = nsafs_login_lookup(user, cell, cksum, &viceid, &group0, &group1);
908     if (rc != 0) {
909         pblock_nninsert("nsafs_viceid", viceid, rq->vars);
910         pblock_nninsert("nsafs_group0", group0, rq->vars);
911         pblock_nninsert("nsafs_group1", group1, rq->vars);
912         memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
913         return REQ_PROCEED;
914     }
915
916     /*
917      * Make sure the user is from one of the cells we are configured for
918      */
919     cellP = nsafs_NameFromNames(NULL, cellNameParam, &pos);
920     while (cellP != NULL) {
921         if (strcmp(cellP, cell) == 0) {
922             break;
923         }
924         cellP = nsafs_NameFromNames(cellP, cellNameParam, &pos);
925     }
926     if (cellP == NULL) {
927         memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
928         pblock_nvinsert("status","Login Failed",rq->vars);
929         return nsafs_error_check(EPERM, "Invalid cell", pb, sn, rq);
930     }
931     afs_osi_Free(cellP, strlen(cellP)+1);
932
933     u.u_expiration = 0;
934     nsafs_set_id_from_strings(NULL, NULL, NULL);
935     code = uafs_klog(user, cell, passwd, &reason);
936     memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
937     if (code != 0) {
938 #if 0
939         sprintf(txtbuf, "%s@%s: %s\n", user, cell, reason);
940         pblock_nvinsert("status","Login Failed",rq->vars);
941         return nsafs_error_check(EPERM, txtbuf, pb, sn, rq);
942 #else /* 0 */
943     return REQ_PROCEED;
944 #endif /* 0 */
945     }
946     expiration = u.u_expiration;
947     usr_assert(expiration != 0);
948     expiration = MIN(expiration, (afs_uint32)(time(NULL) + maxExpirationParam));
949
950     /*
951      * Insert the credentials into the login cache
952      */
953     pblock_nvinsert("auth-type", "basic", rq->vars);
954     pblock_nvinsert("auth-user", user, rq->vars);
955     pblock_nninsert("nsafs_viceid", u.u_viceid, rq->vars);
956     pblock_nninsert("nsafs_group0", u.u_cred->cr_groups[0], rq->vars);
957     pblock_nninsert("nsafs_group1", u.u_cred->cr_groups[1], rq->vars);
958     nsafs_login_store(user, cell, cksum, u.u_viceid, u.u_cred->cr_groups[0],
959                       u.u_cred->cr_groups[1], expiration);
960
961     return REQ_PROCEED;
962 }
963
964 /*
965  * Name trans function for AFS
966  *
967  * Terminates the name translation step for files in AFS.
968  * Puts the AFS pathname into path and into ppath request vars.
969  */
970 NSAPI_PUBLIC int nsafs_mount(pblock *pb, Session *sn, Request *rq)
971 {
972     char *reqUri;
973     char *newReqUri;
974     int isAfsPath;
975
976     /*
977      * Get the URI from the request block
978      */
979     reqUri = pblock_findval("uri", rq->reqpb);
980     if (reqUri == NULL) {
981         return REQ_NOACTION;
982     }
983
984     if (uafs_afsPathName(reqUri) == NULL) {
985         isAfsPath = 0;
986     } else {
987         isAfsPath = 1;
988     }
989
990     /*
991      * If we have a new-url parameter then the new-url may be in AFS
992      * if and only if the path is in AFS
993      */
994     request_header("new-url", &newReqUri, sn, rq);
995     if (newReqUri != NULL) {
996         if (util_uri_is_evil(newReqUri)) {
997             util_uri_parse(newReqUri);
998         }
999         if (uafs_afsPathName(newReqUri) != NULL) {
1000             if (!isAfsPath) {
1001                 /*
1002                  * We do not support moving files in or out of AFS
1003                  */
1004                 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
1005                 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1006                 return REQ_ABORTED;
1007             } else {
1008                 pblock_nvinsert("newpath", newReqUri, rq->vars);
1009             }
1010         } else if (isAfsPath) {
1011             /*
1012              * We do not support moving files in or out of AFS
1013              */
1014             log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
1015             protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1016             return REQ_ABORTED;
1017         }
1018     } else if (!isAfsPath) {
1019         return REQ_NOACTION;
1020     }
1021
1022     /*
1023      * This is an AFS request
1024      */
1025     pblock_nvinsert("path", reqUri, rq->vars);
1026     pblock_nvinsert("ppath", reqUri, rq->vars);
1027     return REQ_PROCEED;
1028 }
1029
1030 /*
1031  * Allow unauthorized users to access a specific directory in AFS
1032  */
1033 NSAPI_PUBLIC int nsafs_public(pblock *pb, Session *sn, Request *rq)
1034 {
1035     char *path;
1036     char *public;
1037     int pubLen;
1038
1039     path = pblock_findval("path", rq->vars);
1040     if (path == NULL) {
1041         return REQ_NOACTION;
1042     }
1043     public = pblock_findval("public", pb);
1044     if (public == NULL) {
1045         return REQ_NOACTION;
1046     }
1047
1048     /*
1049      * if the path is in AFS and is in the given directory then allow access
1050      */
1051     if (util_uri_is_evil(path)) {
1052         util_uri_parse(path);
1053     }
1054     if (uafs_afsPathName(path) == NULL) {
1055         return REQ_NOACTION;
1056     }
1057     if (util_uri_is_evil(public)) {
1058         util_uri_parse(public);
1059     }
1060     pubLen = strlen(public);
1061     if (strncmp(path, public, pubLen) != 0 ||
1062         (path[pubLen] != '/' && path[pubLen] != '\0')) {
1063         return REQ_NOACTION;
1064     }
1065     if (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
1066         pblock_findval("nsafs_group0", rq->vars) == NULL ||
1067         pblock_findval("nsafs_group1", rq->vars) == NULL) {
1068         pblock_nvinsert("nsafs_public", "TRUE", rq->vars);
1069     }
1070     return REQ_PROCEED;
1071 }
1072
1073 /*
1074  * Identify a path that should be an authentication realm.
1075  */
1076 NSAPI_PUBLIC int nsafs_realm(pblock *pb, Session *sn, Request *rq)
1077 {
1078     char *path;
1079     char *realm;
1080     int pathLen;
1081
1082     /*
1083      * Ignore matches for the current path once we find a realm
1084      * line that matches this path.
1085      */
1086     realm = pblock_findval("nsafs_realm", rq->vars);
1087     if (realm != NULL) {
1088         return REQ_NOACTION;
1089     }
1090
1091     path = pblock_findval("path", rq->vars);
1092     if (path == NULL) {
1093         return REQ_NOACTION;
1094     }
1095     if (util_uri_is_evil(path)) {
1096         util_uri_parse(path);
1097     }
1098     if (uafs_afsPathName(path) == NULL) {
1099         return REQ_NOACTION;
1100     }
1101
1102     realm = pblock_findval("path", pb);
1103     if (realm == NULL) {
1104         return REQ_NOACTION;
1105     }
1106     if (util_uri_is_evil(realm)) {
1107         util_uri_parse(realm);
1108     }
1109
1110     pathLen = strlen(realm);
1111     if (strncmp(path, realm, pathLen) != 0 ||
1112         (path[pathLen] != '/' && path[pathLen] != '\0')) {
1113         return REQ_NOACTION;
1114     }
1115     pblock_nvinsert("nsafs_realm", realm, rq->vars);
1116     return REQ_PROCEED;
1117 }
1118
1119 /*
1120  * Check whether any path elements beneath the nolinks directory
1121  * are symbolic links. Return REQ_PROCEED if no links are found.
1122  */
1123 int nsafs_check_for_links(
1124     char *path,
1125     char *nolinks,
1126     pblock *pb,
1127     Session *sn,
1128     Request *rq)
1129 {
1130     int rc;
1131     int code;
1132     int dirLen;
1133     char *tmpPath;
1134     int allocSize;
1135     int pathLen;
1136     struct stat st;
1137
1138     /*
1139      * Check each component of the path below the nolinks directory.
1140      */
1141     dirLen = strlen(nolinks);
1142     pathLen = strlen(path);
1143     if (pathLen < dirLen ||
1144         strncmp(path, nolinks, dirLen) != 0 ||
1145         (path[dirLen] != '/' && path[dirLen] != '\0')) {
1146         return REQ_PROCEED;
1147     }
1148     if (path[dirLen] == '/') {
1149         dirLen++;
1150     }
1151
1152     allocSize = pathLen+1;
1153     tmpPath = (char *)afs_osi_Alloc(allocSize);
1154     strcpy(tmpPath, path);
1155     while(tmpPath[pathLen] == '/' && pathLen > dirLen) {
1156         tmpPath[pathLen] = '\0';
1157         pathLen--;
1158     }
1159     while (pathLen > dirLen) {
1160         rc = uafs_lstat(tmpPath, &st);
1161         if (rc < 0) {
1162             code = errno;
1163             afs_osi_Free(tmpPath, allocSize);
1164             return nsafs_error_check(code, NULL, pb, sn, rq);
1165         }
1166         if ((st.st_mode & S_IFMT) == S_IFLNK) {
1167             afs_osi_Free(tmpPath, allocSize);
1168             return nsafs_error_check(ENOENT, NULL, pb, sn, rq);
1169         }
1170         while(tmpPath[pathLen] != '/' && pathLen > dirLen) {
1171             tmpPath[pathLen] = '\0';
1172             pathLen--;
1173         }
1174         while(tmpPath[pathLen] == '/' && pathLen > dirLen) {
1175             tmpPath[pathLen] = '\0';
1176             pathLen--;
1177         }
1178     }
1179     afs_osi_Free(tmpPath, allocSize);
1180     return REQ_PROCEED;
1181 }
1182
1183 /*
1184  * Deny access to symbolic links in a directory or its descendents.
1185  */
1186 NSAPI_PUBLIC int nsafs_nolinks(pblock *pb, Session *sn, Request *rq)
1187 {
1188     int code;
1189     char *path;
1190     char *newPath;
1191     char *nolinks;
1192     char *viceid;
1193     char *group0;
1194     char *group1;
1195
1196     path = pblock_findval("path", rq->vars);
1197     if (path == NULL) {
1198         return REQ_NOACTION;
1199     }
1200     nolinks = pblock_findval("nolinks", pb);
1201     if (nolinks == NULL) {
1202         return REQ_NOACTION;
1203     }
1204     newPath = pblock_findval("newpath", rq->vars);
1205
1206     /*
1207      * if the path is in AFS and is in the nolinks directory then deny access
1208      * to any symbolic links below the nolinks directory.
1209      */
1210     if (util_uri_is_evil(path)) {
1211         util_uri_parse(path);
1212     }
1213     if (uafs_afsPathName(path) == NULL) {
1214         return REQ_NOACTION;
1215     }
1216     if (util_uri_is_evil(nolinks)) {
1217         util_uri_parse(nolinks);
1218     }
1219
1220     /*
1221      * Check for group numbers in the request vars, otherwise use the
1222      * defaults
1223      */
1224     viceid = pblock_findval("nsafs_viceid", rq->vars);
1225     group0 = pblock_findval("nsafs_group0", rq->vars);
1226     group1 = pblock_findval("nsafs_group1", rq->vars);
1227     nsafs_set_id_from_strings(viceid, group0, group1);
1228
1229     code = nsafs_check_for_links(path, nolinks, pb, sn, rq);
1230     if (code != REQ_PROCEED) {
1231         return code;
1232     }
1233
1234     if (newPath != NULL) {
1235         if (util_uri_is_evil(newPath)) {
1236             util_uri_parse(newPath);
1237         }
1238         if (uafs_afsPathName(newPath) == NULL) {
1239             return REQ_NOACTION;
1240         }
1241         code = nsafs_check_for_links(newPath, nolinks, pb, sn, rq);
1242         if (code != REQ_PROCEED) {
1243             return code;
1244         }
1245     }
1246
1247     return REQ_NOACTION;
1248 }
1249
1250 /*
1251  * Set the MIME type for files in AFS.
1252  */
1253 NSAPI_PUBLIC int nsafs_force_type(pblock *pb, Session *sn, Request *rq)
1254 {
1255     char *path;
1256     char *dflt;
1257
1258     path = pblock_findval("path", rq->vars);
1259     if (path == NULL) {
1260         return REQ_NOACTION;
1261     }
1262     dflt = pblock_findval("type", pb);
1263     if (dflt == NULL) {
1264         return REQ_NOACTION;
1265     }
1266
1267     /*
1268      * If the path is in AFS then set the nsafs_variable in the
1269      * request variables. The nsafs_error_check routine only
1270      * sends an Unauthorized error if this variable is not set.
1271      */
1272     if (util_uri_is_evil(path)) {
1273         util_uri_parse(path);
1274     }
1275     if (uafs_afsPathName(path) == NULL) {
1276         return REQ_NOACTION;
1277     }
1278
1279     if (pblock_findval("content-type", rq->srvhdrs) == NULL) {
1280         pblock_nvinsert("content-type", dflt, rq->srvhdrs);
1281     }
1282
1283     return REQ_PROCEED;
1284 }
1285
1286 /*
1287  * Disable the Unauthorized response message so users never get
1288  * prompted for their name and password.
1289  */
1290 NSAPI_PUBLIC int nsafs_nocheck(pblock *pb, Session *sn, Request *rq)
1291 {
1292     char *path;
1293
1294     path = pblock_findval("path", rq->vars);
1295     if (path == NULL) {
1296         return REQ_NOACTION;
1297     }
1298
1299     /*
1300      * If the path is in AFS then set the nsafs_variable in the
1301      * request variables. The nsafs_error_check routine only
1302      * sends an Unauthorized error if this variable is not set.
1303      */
1304     if (util_uri_is_evil(path)) {
1305         util_uri_parse(path);
1306     }
1307     if (uafs_afsPathName(path) == NULL) {
1308         return REQ_NOACTION;
1309     }
1310
1311     if (pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
1312         pblock_nvinsert("nsafs_nocheck", "TRUE", rq->vars);
1313     }
1314
1315     /*
1316      * If this is a public directory then proceed, otherwise access
1317      * is forbidden
1318      */
1319     if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1320         return REQ_PROCEED;
1321     }
1322
1323     return nsafs_error_check(EACCES, NULL, pb, sn, rq);
1324 }
1325
1326 /*
1327  * Require all requests for AFS files that are not explicitly made
1328  * public to be authenticated.
1329  */
1330 NSAPI_PUBLIC int nsafs_check(pblock *pb, Session *sn, Request *rq)
1331 {
1332     char *path;
1333
1334     path = pblock_findval("path", rq->vars);
1335     if (path == NULL) {
1336         return REQ_NOACTION;
1337     }
1338
1339     /*
1340      * If the path is in AFS then require authentication
1341      */
1342     if (util_uri_is_evil(path)) {
1343         util_uri_parse(path);
1344     }
1345     if (uafs_afsPathName(path) == NULL) {
1346         return REQ_NOACTION;
1347     }
1348
1349     /*
1350      * If this is a public directory then proceed
1351      */
1352     if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1353         return REQ_PROCEED;
1354     }
1355
1356     if (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
1357         pblock_findval("nsafs_group0", rq->vars) == NULL ||
1358         pblock_findval("nsafs_group1", rq->vars) == NULL) {
1359         return nsafs_error_check(EPERM, NULL, pb, sn, rq);
1360     }
1361
1362     return REQ_PROCEED;
1363 }
1364
1365 /*
1366  * Find index files for directories
1367  */
1368 NSAPI_PUBLIC int nsafs_find_index(pblock *pb, Session *sn, Request *rq)
1369 {
1370     char *path;
1371     char *indexNames;
1372     char *indexP;
1373     char *nameP;
1374     char *viceid;
1375     char *group0;
1376     char *group1;
1377     struct stat st;
1378     int rc;
1379     int code;
1380     int pos;
1381
1382     path = pblock_findval("path", rq->vars);
1383     if (path == NULL) {
1384         return REQ_NOACTION;
1385     }
1386
1387     indexNames = pblock_findval("index-names", pb);
1388     if (indexNames == NULL) {
1389         return REQ_NOACTION;
1390     }
1391
1392     /*
1393      * Skip pathnames that don't end in a slash
1394      */
1395     if (*path == '\0' || path[strlen(path)-1] != '/') {
1396         return REQ_NOACTION;
1397     }
1398
1399     /*
1400      * If the path is a directory then check if the directory has
1401      * an index file.
1402      */
1403     if (util_uri_is_evil(path)) {
1404         util_uri_parse(path);
1405     }
1406     if (uafs_afsPathName(path) == NULL) {
1407         /*
1408          * Look for index files in local file system
1409          */
1410         rc = stat(path, &st);
1411         if (rc < 0) {
1412             code = errno;
1413             return nsafs_error_check(code, NULL, pb, sn, rq);
1414         }
1415
1416         if ((st.st_mode & S_IFMT) != S_IFDIR) {
1417             return REQ_NOACTION;
1418         }
1419
1420         indexP = nsafs_NameFromNames(NULL, indexNames, &pos);
1421         while (indexP != NULL) {
1422             nameP = afs_osi_Alloc(strlen(path)+strlen(indexP)+2);
1423             sprintf(nameP, "%s/%s", path, indexP);
1424             rc = stat(nameP, &st);
1425             if (rc == 0 && (st.st_mode & S_IFMT) != S_IFDIR) {
1426                 param_free(pblock_remove("path", rq->vars));
1427                 pblock_nvinsert("path", nameP, rq->vars);
1428                 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1429                 afs_osi_Free(indexP, strlen(indexP)+1);
1430                 return REQ_PROCEED;
1431             }
1432             afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1433             indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1434         }
1435     } else {
1436         /*
1437          * Check for group numbers in the request vars, otherwise use the
1438          * defaults
1439          */
1440         viceid = pblock_findval("nsafs_viceid", rq->vars);
1441         group0 = pblock_findval("nsafs_group0", rq->vars);
1442         group1 = pblock_findval("nsafs_group1", rq->vars);
1443         nsafs_set_id_from_strings(viceid, group0, group1);
1444
1445         /*
1446          * Look for index files in AFS
1447          */
1448         rc = uafs_stat(path, &st);
1449         if (rc < 0) {
1450             code = errno;
1451             return nsafs_error_check(code, NULL, pb, sn, rq);
1452         }
1453
1454         if ((st.st_mode & S_IFMT) != S_IFDIR) {
1455             return REQ_NOACTION;
1456         }
1457
1458         indexP = nsafs_NameFromNames(NULL, indexNames, &pos);
1459         while (indexP != NULL) {
1460             nameP = afs_osi_Alloc(strlen(path)+strlen(indexP)+2);
1461             sprintf(nameP, "%s/%s", path, indexP);
1462             rc = uafs_stat(nameP, &st);
1463             if (rc == 0 && (st.st_mode & S_IFMT) != S_IFDIR) {
1464                 param_free(pblock_remove("path", rq->vars));
1465                 pblock_nvinsert("path", nameP, rq->vars);
1466                 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1467                 afs_osi_Free(indexP, strlen(indexP)+1);
1468                 return REQ_PROCEED;
1469             }
1470             afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1471             indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1472         }
1473     }
1474     return REQ_NOACTION;
1475 }
1476
1477 /*
1478  * Node in binary tree used to sort directory entries
1479  */
1480 struct nsafs_tree {
1481     char *name;                 /* directory member name */
1482     char *text;                 /* directory index text */
1483     int allocLen;               /* Size of this allocated block */
1484     int textLen;                /* Size of the text string */
1485     int balance;                /* used to balance binary trees */
1486     struct nsafs_tree *left;
1487     struct nsafs_tree *right;
1488 };
1489
1490 #ifdef NSAFS_TREE_DEBUG
1491 /*
1492  * Validate that the given tree is a valid balanced binary tree
1493  */
1494 int nsafs_tree_check(struct nsafs_tree *root)
1495 {
1496     int leftDepth;
1497     int rightDepth;
1498     int balance;
1499
1500     if (root->left == NULL) {
1501         leftDepth = 0;
1502     } else {
1503         assert(strcmp(root->left->name, root->name) < 0);
1504         leftDepth = nsafs_tree_check(root->left);
1505     }
1506     if (root->right == NULL) {
1507         rightDepth = 0;
1508     } else {
1509         assert(strcmp(root->right->name, root->name) >= 0);
1510         rightDepth = nsafs_tree_check(root->right);
1511     }
1512     balance = rightDepth - leftDepth;
1513     assert(balance == root->balance);
1514     assert(balance >= -1);
1515     assert(balance <= 1);
1516     return (MAX(leftDepth, rightDepth)+1);
1517 }
1518 #endif /* NSAFS_TREE_DEBUG */
1519
1520 /*
1521  * Insert a node into the balanced binary tree of directory entries.
1522  * rootP is the address of the parent's pointer to this node.
1523  * Returns the change in depth of this tree (0 or 1)
1524  */
1525 int nsafs_node_insert(
1526     struct nsafs_tree *newNode,
1527     struct nsafs_tree **rootP)
1528 {
1529     struct nsafs_tree *thisNode;
1530     int delta;
1531
1532     thisNode = *rootP;
1533     if (strcmp(newNode->name, thisNode->name) < 0) {
1534         /*
1535          * Insert left
1536          */
1537         if (thisNode->left == NULL) {
1538             thisNode->left = newNode;
1539             if (thisNode->right == NULL) {
1540                 thisNode->balance = -1;
1541                 return 1;
1542             } else {
1543                 thisNode->balance = 0;
1544                 return 0;
1545             }
1546         } else {
1547             delta = nsafs_node_insert(newNode, &thisNode->left);
1548             if (delta == 0) {
1549                 return 0;
1550             }
1551             thisNode->balance -= delta;
1552             if (thisNode->balance == -2) {
1553                 /*
1554                  * rotate right
1555                  */
1556                 if (thisNode->left->balance > 0) {
1557 #ifdef NSAFS_TREE_DEBUG
1558                     printf("rotate left, line %d\n", __LINE__);
1559 #endif /* NSAFS_TREE_DEBUG */
1560                     *rootP = thisNode->left->right;
1561                     if ((*rootP)->balance > 0) {
1562                         thisNode->left->balance = -(*rootP)->balance;
1563                         (*rootP)->balance = 0;
1564                         thisNode->balance = 0;
1565                     } else {
1566                         thisNode->left->balance = 0;
1567                         thisNode->balance = -(*rootP)->balance;
1568                         (*rootP)->balance = 0;
1569                     }
1570                     thisNode->left->right = (*rootP)->left;
1571                     (*rootP)->left = thisNode->left;
1572                     thisNode->left = (*rootP)->right;
1573                     (*rootP)->right = thisNode;
1574                 } else {
1575 #ifdef NSAFS_TREE_DEBUG
1576                     printf("rotate left, line %d\n", __LINE__);
1577 #endif /* NSAFS_TREE_DEBUG */
1578                     *rootP = thisNode->left;
1579                     (*rootP)->balance = 0;
1580                     thisNode->balance = 0;
1581                     thisNode->left = (*rootP)->right;
1582                     (*rootP)->right = thisNode;
1583                 }
1584                 return 0;
1585             } else if (thisNode->balance != 0) {
1586                 return 1;
1587             } else {
1588                 return 0;
1589             }
1590         }
1591     } else {
1592         /*
1593          * Insert right
1594          */
1595         if (thisNode->right == NULL) {
1596             thisNode->right = newNode;
1597             if (thisNode->left == NULL) {
1598                 thisNode->balance = 1;
1599                 return 1;
1600             } else {
1601                 thisNode->balance = 0;
1602                 return 0;
1603             }
1604         } else {
1605             delta = nsafs_node_insert(newNode, &thisNode->right);
1606             if (delta == 0) {
1607                 return 0;
1608             }
1609             thisNode->balance += delta;
1610             if (thisNode->balance == 2) {
1611                 /*
1612                  * rotate left
1613                  */
1614                 if (thisNode->right->balance < 0) {
1615 #ifdef NSAFS_TREE_DEBUG
1616                     printf("rotate right, line %d\n", __LINE__);
1617 #endif /* NSAFS_TREE_DEBUG */
1618                     *rootP = thisNode->right->left;
1619                     if ((*rootP)->balance < 0) {
1620                         thisNode->right->balance = -(*rootP)->balance;
1621                         (*rootP)->balance = 0;
1622                         thisNode->balance = 0;
1623                     } else {
1624                         thisNode->right->balance = 0;
1625                         thisNode->balance = -(*rootP)->balance;
1626                         (*rootP)->balance = 0;
1627                     }
1628                     thisNode->right->left = (*rootP)->right;
1629                     (*rootP)->right = thisNode->right;
1630                     thisNode->right = (*rootP)->left;
1631                     (*rootP)->left = thisNode;
1632                 } else {
1633 #ifdef NSAFS_TREE_DEBUG
1634                     printf("rotate right, line %d\n", __LINE__);
1635 #endif /* NSAFS_TREE_DEBUG */
1636                     *rootP = thisNode->right;
1637                     (*rootP)->balance = 0;
1638                     thisNode->balance = 0;
1639                     thisNode->right = (*rootP)->left;
1640                     (*rootP)->left = thisNode;
1641                 }
1642                 return 0;
1643             } else if (thisNode->balance != 0) {
1644                 return 1;
1645             } else {
1646                 return 0;
1647             }
1648         }
1649     }
1650 }
1651
1652 /*
1653  * Allocate storage for a new directory entry, copy in the name and
1654  * text, and insert the entry into the balanced binary tree.
1655  */
1656 void nsafs_tree_insert(
1657     char *name,
1658     char *text,
1659     struct nsafs_tree **rootP)
1660 {
1661     int nameLen;
1662     int textLen;
1663     int allocLen;
1664     struct nsafs_tree *newNode;
1665
1666     /*
1667      * allocate storage, initialize the fields, and copy in the data
1668      */
1669     nameLen = strlen(name);
1670     textLen = strlen(text);
1671     allocLen = sizeof(struct nsafs_tree) + nameLen + textLen + 2;
1672     newNode = (struct nsafs_tree *)afs_osi_Alloc(allocLen);
1673     usr_assert(newNode != NULL);
1674     newNode->name = (char *)(newNode+1);
1675     newNode->text = newNode->name + nameLen + 1;
1676     newNode->textLen = textLen;
1677     newNode->allocLen = allocLen;
1678     newNode->balance = 0;
1679     newNode->left = NULL;
1680     newNode->right = NULL;
1681     strcpy(newNode->name, name);
1682     strcpy(newNode->text, text);
1683
1684     /*
1685      * If this is the first node, insert it here, otherwise call
1686      * nsafs_node_insert to insert the node into the balanced
1687      * binary tree.
1688      */
1689     if (*rootP == NULL) {
1690         *rootP = newNode;
1691     } else {
1692         nsafs_node_insert(newNode, rootP);
1693     }
1694 #ifdef NSAFS_TREE_DEBUG
1695     nsafs_tree_check(*rootP);
1696 #endif /* NSAFS_TREE_DEBUG */
1697 }
1698
1699 /*
1700  * Transmit the contents of the tree
1701  */
1702 int nsafs_tree_send(
1703     SYS_NETFD sd,
1704     struct nsafs_tree *root,
1705     char *outbuf,
1706     int *buflen,
1707     int bufsize)
1708 {
1709     int code;
1710     struct nsafs_tree *node;
1711     char *txtBuf;
1712     int txtLen;
1713     int len;
1714  
1715     /*
1716      * Recurse left, iterate right
1717      */
1718     node = root;
1719     while (node != NULL) {
1720         if (node->left != NULL) {
1721             code = nsafs_tree_send(sd, node->left, outbuf, buflen, bufsize);
1722             if (code == IO_ERROR) {
1723                 return IO_ERROR;
1724             }
1725         }
1726         txtLen = node->textLen;
1727         txtBuf = node->text;
1728         while (txtLen > 0) {
1729             if (*buflen == bufsize) {
1730                 code = net_write(sd, outbuf, bufsize);
1731                 if (code == IO_ERROR) {
1732                     return IO_ERROR;
1733                 }
1734                 *buflen = 0;
1735             }
1736             len = MIN(txtLen, bufsize - *buflen);
1737             memcpy((void *)(outbuf + *buflen), (void *)txtBuf, len);
1738             *buflen += len;
1739             txtBuf += len;
1740             txtLen -= len;
1741         }
1742         node = node->right;
1743     }
1744     return 0;
1745 }
1746
1747 /*
1748  * Free the binary tree and all data within
1749  */
1750 void nsafs_tree_free(
1751     struct nsafs_tree *root)
1752 {
1753     struct nsafs_tree *node, *next;
1754  
1755     /*
1756      * Iterate left, recurse right
1757      */
1758     node = root;
1759     while (node != NULL) {
1760         if (node->right != NULL) {
1761             nsafs_tree_free(node->right);
1762         }
1763         next = node->left;
1764         afs_osi_Free(node, sizeof(struct nsafs_tree) + strlen(node->name) +
1765                      strlen(node->text) + 2);
1766         node = next;
1767     }
1768 }
1769
1770 /*
1771  * Send the contents of an AFS directory, Simple directory format
1772  */
1773 int nsafs_send_directory(
1774     char *path,
1775     struct stat *stp,
1776     pblock *pb,
1777     Session *sn,
1778     Request *rq)
1779 {
1780     char *dirbuf;
1781     int buflen;
1782     char *filename;
1783     usr_DIR *dirp;
1784     struct usr_dirent *enp;
1785     int contentLength;
1786     int rc;
1787     int code;
1788     char *htmlHdr = "<HTML>\r\n<BODY>\r\n"
1789                     "<TITLE>Index of %s</TITLE>\r\n"
1790                     "<h1>Index of %s</h1>\r\n"
1791                     "<PRE><HR>\r\n";
1792     char *htmlTrl = "</PRE></BODY></HTML>\r\n";
1793     struct nsafs_tree *root;
1794
1795     /*
1796      * Set the content type to "text/html"
1797      */
1798     param_free(pblock_remove("content-type", rq->srvhdrs));
1799     pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
1800
1801     /*
1802      * Build a binary tree of directory entries, and calculate the
1803      * length of our response message
1804      */
1805     dirp = uafs_opendir(path);
1806     if (dirp == NULL) {
1807         code = errno;
1808         return nsafs_error_check(code, NULL, pb, sn, rq);
1809     }
1810     root = NULL;
1811     contentLength = 0;
1812     dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1813     while ((enp = uafs_readdir(dirp)) != NULL) {
1814         if (strcmp(enp->d_name, ".") == 0) {
1815             continue;
1816         } else if (strcmp(enp->d_name, "..") == 0) {
1817             filename = "Parent Directory";
1818         } else {
1819             filename = enp->d_name;
1820         }
1821         sprintf(dirbuf,
1822                 "<A HREF=\"%s%s\" NAME=\"%s\"> %s</A>\r\n",
1823                 path, enp->d_name, filename, filename);
1824         contentLength += strlen(dirbuf);
1825         nsafs_tree_insert(enp->d_name, dirbuf, &root);
1826     }
1827     if (errno != 0) {
1828         code = errno;
1829         uafs_closedir(dirp);
1830         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1831         nsafs_tree_free(root);
1832         return nsafs_error_check(code, NULL, pb, sn, rq);
1833     }
1834     rc = uafs_closedir(dirp);
1835     if (rc < 0) {
1836         code = errno;
1837         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1838         nsafs_tree_free(root);
1839         return nsafs_error_check(code, NULL, pb, sn, rq);
1840     }
1841
1842     /*
1843      * Calculate the length of the HTML headers and trailers,
1844      * set the content-length field in the reply header, and
1845      * start the reply message
1846      */
1847     sprintf(dirbuf, htmlHdr, path, path);
1848     contentLength += strlen(dirbuf) + strlen(htmlTrl);
1849     stp->st_size = contentLength;
1850     code = nsafs_set_finfo(sn, rq, stp);
1851     if (code != REQ_PROCEED) {
1852         nsafs_tree_free(root);
1853         protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL);
1854         return code;
1855     }
1856     protocol_status(sn, rq, PROTOCOL_OK, NULL);
1857     code = protocol_start_response(sn, rq);
1858     if (code != REQ_PROCEED) {
1859         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1860         nsafs_tree_free(root);
1861         return REQ_PROCEED;
1862     }
1863
1864     /*
1865      * Send the HTML header, file data and HTML trailer
1866      */
1867     if (net_write(sn->csd, dirbuf, strlen(dirbuf)) == IO_ERROR) {
1868         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1869         nsafs_tree_free(root);
1870         log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1871         return REQ_EXIT;
1872     }
1873     buflen = 0;
1874     code = nsafs_tree_send(sn->csd, root, dirbuf, &buflen, NSAFS_BUFFER_SIZE);
1875     if (code == IO_ERROR) {
1876         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1877         nsafs_tree_free(root);
1878         log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1879         return REQ_EXIT;
1880     }
1881     nsafs_tree_free(root);
1882     if (buflen != 0) {
1883         code = net_write(sn->csd, dirbuf, buflen);
1884         if (code == IO_ERROR) {
1885             afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1886             log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1887             return REQ_EXIT;
1888         }
1889     }
1890     afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1891     if (net_write(sn->csd, htmlTrl, strlen(htmlTrl)) == IO_ERROR) {
1892         return REQ_EXIT;
1893     }
1894
1895     return REQ_PROCEED;
1896 }
1897
1898 /*
1899  * Send the contents of an AFS file
1900  */
1901 int nsafs_send_file(
1902     char *path,
1903     struct stat *stp,
1904     pblock *pb,
1905     Session *sn,
1906     Request *rq)
1907 {
1908     char *filebuf;
1909     int i;
1910     int rc;
1911     int fd;
1912     int code;
1913
1914     /*
1915      * Make sure we can open the file before we send the response header
1916      */
1917     fd  = uafs_open(path, O_RDONLY, 0);
1918     if (fd < 0) {
1919         code = errno;
1920         return nsafs_error_check(code, NULL, pb, sn, rq);
1921     }
1922
1923     /*
1924      * Add a content-length field to the response header and
1925      * begin the response message. We have already checked the
1926      * preconditions, so a return code other than REQ_PROCEED
1927      * means that this is a HEAD command.
1928      */
1929     code = nsafs_set_finfo(sn, rq, stp);
1930     if (code != REQ_PROCEED) {
1931         return code;
1932     }
1933     protocol_status(sn, rq, PROTOCOL_OK, NULL);
1934     code = protocol_start_response(sn, rq);
1935     if (code != REQ_PROCEED) {
1936         return REQ_PROCEED;
1937     }
1938
1939     /*
1940      * Send the file contents
1941      */
1942     filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1943     while ((rc = uafs_read(fd, filebuf, NSAFS_BUFFER_SIZE)) > 0) {
1944         if (net_write(sn->csd, filebuf, rc) == IO_ERROR) {
1945             afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1946             uafs_close(fd);
1947             log_error(LOG_INFORM, "nsafs", sn, rq, "send_file IO_ERROR");
1948             return REQ_EXIT;
1949         }
1950     }
1951     if (rc < 0) {
1952         code = errno;
1953         uafs_close(fd);
1954         afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1955         log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_read, err=%d", code);
1956         return REQ_EXIT;
1957     }
1958     afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1959     rc = uafs_close(fd);
1960     if (rc < 0) {
1961         code = errno;
1962         log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_close, err=%d", code);
1963     }
1964     return REQ_PROCEED;
1965 }
1966
1967 /*
1968  * Service function for AFS files and directories
1969  */
1970 NSAPI_PUBLIC int nsafs_send(pblock *pb, Session *sn, Request *rq)
1971 {
1972     char *path;
1973     char *dirPath;
1974     char *viceid;
1975     char *group0;
1976     char *group1;
1977     char *rangeHdr;
1978     struct stat st;
1979     int rc;
1980     int len;
1981     int code;
1982
1983     if (nsafs_init_done == 0) {
1984         nsafs_init_once();
1985     }
1986
1987     /*
1988      * Only service paths that are in AFS
1989      */
1990     path = pblock_findval("path", rq->vars);
1991     if (path == NULL) {
1992         return REQ_NOACTION;
1993     }
1994     if (uafs_afsPathName(path) == NULL) {
1995         return REQ_NOACTION;
1996     }
1997
1998     /*
1999      * We do not support content-range headers
2000      */
2001     request_header("content-range", &rangeHdr, sn, rq);
2002     if (rangeHdr != NULL) {
2003         protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
2004         log_error(LOG_INFORM, "nsafs", sn, rq,
2005                   "content-range is not implemented");
2006     }
2007
2008     /*
2009      * Check for group numbers in the request vars, otherwise use the
2010      * defaults
2011      */
2012     viceid = pblock_findval("nsafs_viceid", rq->vars);
2013     group0 = pblock_findval("nsafs_group0", rq->vars);
2014     group1 = pblock_findval("nsafs_group1", rq->vars);
2015     nsafs_set_id_from_strings(viceid, group0, group1);
2016
2017     /*
2018      * Get the file attributes
2019      */
2020     rc = uafs_stat(path, &st);
2021     if (rc < 0) {
2022         code = errno;
2023         return nsafs_error_check(code, NULL, pb, sn, rq);
2024     }
2025
2026     /*
2027      * Check the request preconditions
2028      */
2029     code = nsafs_check_preconditions(&st, pb, sn, rq);
2030     if (code != REQ_PROCEED) {
2031         return code;
2032     }
2033
2034     /*
2035      * Send the contents of files, and a formatted index of directories
2036      */
2037     if ((st.st_mode & S_IFMT) == S_IFDIR) {
2038         len = strlen(path);
2039         dirPath = afs_osi_Alloc(len+2);
2040         strcpy(dirPath, path);
2041         if (dirPath[len-1] != '/') {
2042             dirPath[len] = '/';
2043             dirPath[len+1] = '\0';
2044         }
2045         if (util_uri_is_evil(dirPath)) {
2046             util_uri_parse(dirPath);
2047         }
2048         code = nsafs_send_directory(dirPath, &st, pb, sn, rq);
2049         afs_osi_Free(dirPath, len+2);
2050     } else {
2051         code = nsafs_send_file(path, &st, pb, sn, rq);
2052     }
2053     return code;
2054 }
2055
2056 /*
2057  * Service function to create new AFS files
2058  */
2059 NSAPI_PUBLIC int nsafs_put(pblock *pb, Session *sn, Request *rq)
2060 {
2061     char *path;
2062     char *viceid;
2063     char *group0;
2064     char *group1;
2065     char *rangeHdr;
2066     char *lengthHdr;
2067     char *filebuf;
2068     struct stat st;
2069     int i;
2070     int rc;
2071     int fd;
2072     int eof;
2073     int bytesRead;
2074     int bytesToRead;
2075     int code;
2076     int contentLength;
2077     int rspStatus;
2078
2079     if (nsafs_init_done == 0) {
2080         nsafs_init_once();
2081     }
2082
2083     /*
2084      * Only service paths that are in AFS
2085      */
2086     path = pblock_findval("path", rq->vars);
2087     if (path == NULL) {
2088         return REQ_NOACTION;
2089     }
2090     if (uafs_afsPathName(path) == NULL) {
2091         return REQ_NOACTION;
2092     }
2093
2094     /*
2095      * We do not support content-range headers
2096      */
2097     request_header("content-range", &rangeHdr, sn, rq);
2098     if (rangeHdr != NULL) {
2099         protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
2100         log_error(LOG_INFORM, "nsafs", sn, rq,
2101                   "content-range is not implemented");
2102     }
2103
2104     /*
2105      * Check for group numbers in the request vars, otherwise use the
2106      * defaults
2107      */
2108     viceid = pblock_findval("nsafs_viceid", rq->vars);
2109     group0 = pblock_findval("nsafs_group0", rq->vars);
2110     group1 = pblock_findval("nsafs_group1", rq->vars);
2111     nsafs_set_id_from_strings(viceid, group0, group1);
2112
2113     /*
2114      * Check for a content length header
2115      */
2116     request_header("content-length", &lengthHdr, sn, rq);
2117     if (lengthHdr != NULL) {
2118         contentLength = atoi(lengthHdr);
2119     } else {
2120         contentLength = -1;
2121     }
2122
2123     /*
2124      * Get the file attributes
2125      */
2126     rc = uafs_stat(path, &st);
2127     if (rc < 0) {
2128         code = errno;
2129         if (code == ENOENT) {
2130             rspStatus = PROTOCOL_CREATED;
2131         } else {
2132             return nsafs_error_check(code, NULL, pb, sn, rq);
2133         }
2134     } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2135         return nsafs_error_check(EISDIR, NULL, pb, sn, rq);
2136     } else {
2137         rspStatus = PROTOCOL_OK;
2138     }
2139
2140     fd  = uafs_open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
2141     if (fd < 0) {
2142         code = errno;
2143         return nsafs_error_check(code, NULL, pb, sn, rq);
2144     }
2145
2146     /*
2147      * Get the file contents
2148      */
2149     filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2150     eof = 0;
2151     while (!eof) {
2152         if (contentLength < 0) {
2153             bytesToRead = NSAFS_BUFFER_SIZE;
2154         } else {
2155             bytesToRead = MIN(contentLength, NSAFS_BUFFER_SIZE);
2156             if (bytesToRead == 0) {
2157                 eof = 1;
2158             }
2159         }
2160         for (bytesRead = 0 ; !eof && bytesRead < bytesToRead ; bytesRead++) {
2161             rc = netbuf_getc(sn->inbuf);
2162             if (rc == IO_EOF) {
2163                 eof = 1;
2164             } else if (rc == IO_ERROR) {
2165                 code = errno;
2166                 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2167                 uafs_close(fd);
2168                 return nsafs_error_check(EIO, NULL, pb, sn, rq);
2169             } else {
2170                 filebuf[bytesRead] = rc;
2171             }
2172         }
2173         if (bytesRead > 0) {
2174             if (contentLength > 0) {
2175                 contentLength -= bytesRead;
2176             }
2177             rc = uafs_write(fd, filebuf, bytesRead);
2178             if (rc < 0) {
2179                 code = errno;
2180                 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2181                 uafs_close(fd);
2182                 return nsafs_error_check(code, NULL, pb, sn, rq);
2183             }
2184         }
2185     }
2186     afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2187     rc = uafs_close(fd);
2188     if (rc < 0) {
2189         code = errno;
2190         return nsafs_error_check(code, NULL, pb, sn, rq);
2191     }
2192     if (contentLength > 0) {
2193         log_error(LOG_FAILURE, "nsafs", sn, rq, "received partial contents");
2194         return REQ_EXIT;
2195     }
2196     
2197     pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2198     protocol_status(sn, rq, rspStatus, NULL);
2199     code = protocol_start_response(sn, rq);
2200     return code;
2201 }
2202
2203 /*
2204  * Service function to delete AFS files
2205  */
2206 NSAPI_PUBLIC int nsafs_delete(pblock *pb, Session *sn, Request *rq)
2207 {
2208     char *path;
2209     char *viceid;
2210     char *group0;
2211     char *group1;
2212     struct stat st;
2213     int rc;
2214     int code;
2215
2216     if (nsafs_init_done == 0) {
2217         nsafs_init_once();
2218     }
2219
2220     /*
2221      * Only service paths that are in AFS
2222      */
2223     path = pblock_findval("path", rq->vars);
2224     if (path == NULL) {
2225         return REQ_NOACTION;
2226     }
2227     if (uafs_afsPathName(path) == NULL) {
2228         return REQ_NOACTION;
2229     }
2230
2231     /*
2232      * Check for group numbers in the request vars, otherwise use the
2233      * defaults
2234      */
2235     viceid = pblock_findval("nsafs_viceid", rq->vars);
2236     group0 = pblock_findval("nsafs_group0", rq->vars);
2237     group1 = pblock_findval("nsafs_group1", rq->vars);
2238     nsafs_set_id_from_strings(viceid, group0, group1);
2239
2240     /*
2241      * Get the file attributes
2242      */
2243     rc = uafs_lstat(path, &st);
2244     if (rc < 0) {
2245         code = errno;
2246         return nsafs_error_check(code, NULL, pb, sn, rq);
2247     } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2248         log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot DELETE directories");
2249         protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2250         pblock_nvinsert("Allow", NSAFS_DIR_ALLOW, rq->srvhdrs);
2251         return REQ_ABORTED;
2252     }
2253
2254     rc  = uafs_unlink(path);
2255     if (rc < 0) {
2256         code = errno;
2257         return nsafs_error_check(code, NULL, pb, sn, rq);
2258     }
2259
2260     pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2261     protocol_status(sn, rq, PROTOCOL_OK, NULL);
2262     code = protocol_start_response(sn, rq);
2263     return code;
2264 }
2265
2266 /*
2267  * Service function to create AFS directories
2268  */
2269 NSAPI_PUBLIC int nsafs_mkdir(pblock *pb, Session *sn, Request *rq)
2270 {
2271     char *path;
2272     char *viceid;
2273     char *group0;
2274     char *group1;
2275     struct stat st;
2276     int rc;
2277     int code;
2278
2279     if (nsafs_init_done == 0) {
2280         nsafs_init_once();
2281     }
2282
2283     /*
2284      * Only service paths that are in AFS
2285      */
2286     path = pblock_findval("path", rq->vars);
2287     if (path == NULL) {
2288         return REQ_NOACTION;
2289     }
2290     if (uafs_afsPathName(path) == NULL) {
2291         return REQ_NOACTION;
2292     }
2293
2294     /*
2295      * Check for group numbers in the request vars, otherwise use the
2296      * defaults
2297      */
2298     viceid = pblock_findval("nsafs_viceid", rq->vars);
2299     group0 = pblock_findval("nsafs_group0", rq->vars);
2300     group1 = pblock_findval("nsafs_group1", rq->vars);
2301     nsafs_set_id_from_strings(viceid, group0, group1);
2302
2303     /*
2304      * Create the directory
2305      */
2306     rc  = uafs_mkdir(path, 0755);
2307     if (rc < 0) {
2308         code = errno;
2309         return nsafs_error_check(code, NULL, pb, sn, rq);
2310     }
2311
2312     pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2313     protocol_status(sn, rq, PROTOCOL_OK, NULL);
2314     code = protocol_start_response(sn, rq);
2315     return code;
2316 }
2317
2318 /*
2319  * Service function to delete AFS directories
2320  */
2321 NSAPI_PUBLIC int nsafs_rmdir(pblock *pb, Session *sn, Request *rq)
2322 {
2323     char *path;
2324     char *viceid;
2325     char *group0;
2326     char *group1;
2327     struct stat st;
2328     int rc;
2329     int code;
2330
2331     if (nsafs_init_done == 0) {
2332         nsafs_init_once();
2333     }
2334
2335     /*
2336      * Only service paths that are in AFS
2337      */
2338     path = pblock_findval("path", rq->vars);
2339     if (path == NULL) {
2340         return REQ_NOACTION;
2341     }
2342     if (uafs_afsPathName(path) == NULL) {
2343         return REQ_NOACTION;
2344     }
2345
2346     /*
2347      * Check for group numbers in the request vars, otherwise use the
2348      * defaults
2349      */
2350     viceid = pblock_findval("nsafs_viceid", rq->vars);
2351     group0 = pblock_findval("nsafs_group0", rq->vars);
2352     group1 = pblock_findval("nsafs_group1", rq->vars);
2353     nsafs_set_id_from_strings(viceid, group0, group1);
2354
2355     /*
2356      * Get the file attributes, rmdir only work on directories.
2357      */
2358     rc = uafs_lstat(path, &st);
2359     if (rc < 0) {
2360         code = errno;
2361         return nsafs_error_check(code, NULL, pb, sn, rq);
2362     }
2363
2364     /*
2365      * Call unlink to delete symbolic links to directories, and rmdir
2366      * to to delete directories.
2367      */
2368     if ((st.st_mode & S_IFMT) == S_IFDIR) {
2369         rc  = uafs_rmdir(path);
2370     } else if ((st.st_mode & S_IFMT) == S_IFLNK) {
2371         log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot RMDIR links");
2372         protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2373         pblock_nvinsert("Allow", NSAFS_LINK_ALLOW, rq->srvhdrs);
2374         return REQ_ABORTED;
2375     } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
2376         log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot RMDIR files");
2377         protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2378         pblock_nvinsert("Allow", NSAFS_FILE_ALLOW, rq->srvhdrs);
2379         return REQ_ABORTED;
2380     }
2381     if (rc < 0) {
2382         code = errno;
2383         return nsafs_error_check(code, NULL, pb, sn, rq);
2384     }
2385
2386     pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2387     protocol_status(sn, rq, PROTOCOL_OK, NULL);
2388     code = protocol_start_response(sn, rq);
2389     return code;
2390 }
2391
2392 /*
2393  * Service function to rename AFS files and directories
2394  */
2395 NSAPI_PUBLIC int nsafs_move(pblock *pb, Session *sn, Request *rq)
2396 {
2397     char *path;
2398     char *newPath;
2399     char *viceid;
2400     char *group0;
2401     char *group1;
2402     struct stat st;
2403     int rc;
2404     int code;
2405
2406     if (nsafs_init_done == 0) {
2407         nsafs_init_once();
2408     }
2409
2410     /*
2411      * Only service paths that are in AFS
2412      */
2413     path = pblock_findval("path", rq->vars);
2414     if (path == NULL) {
2415         return REQ_NOACTION;
2416     }
2417     if (uafs_afsPathName(path) == NULL) {
2418         return REQ_NOACTION;
2419     }
2420     newPath = pblock_findval("newpath", rq->vars);
2421     if (newPath == NULL) {
2422         return REQ_NOACTION;
2423     }
2424     if (uafs_afsPathName(newPath) == NULL) {
2425         return REQ_NOACTION;
2426     }
2427
2428     /*
2429      * Check for group numbers in the request vars, otherwise use the
2430      * defaults
2431      */
2432     viceid = pblock_findval("nsafs_viceid", rq->vars);
2433     group0 = pblock_findval("nsafs_group0", rq->vars);
2434     group1 = pblock_findval("nsafs_group1", rq->vars);
2435     nsafs_set_id_from_strings(viceid, group0, group1);
2436
2437     /*
2438      * Rename the object
2439      */
2440     rc  = uafs_rename(path, newPath);
2441     if (rc < 0) {
2442         code = errno;
2443         return nsafs_error_check(code, NULL, pb, sn, rq);
2444     }
2445
2446     pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2447     protocol_status(sn, rq, PROTOCOL_OK, NULL);
2448     code = protocol_start_response(sn, rq);
2449     return code;
2450 }
2451
2452 /*
2453  * Send the index of an AFS directory
2454  */
2455 int nsafs_index(
2456     pblock *pb,
2457     Session *sn,
2458     Request *rq)
2459 {
2460     char *path;
2461     char *viceid;
2462     char *group0;
2463     char *group1;
2464     char *dirbuf;
2465     char *tmpPath;
2466     int buflen;
2467     usr_DIR *dirp;
2468     struct usr_dirent *enp;
2469     int contentLength;
2470     int rc;
2471     int code;
2472     struct stat st;
2473     struct nsafs_tree *root;
2474
2475     if (nsafs_init_done == 0) {
2476         nsafs_init_once();
2477     }
2478
2479     /*
2480      * Only service paths that are in AFS
2481      */
2482     path = pblock_findval("path", rq->vars);
2483     if (path == NULL) {
2484         return REQ_NOACTION;
2485     }
2486     if (uafs_afsPathName(path) == NULL) {
2487         return REQ_NOACTION;
2488     }
2489
2490     /*
2491      * Check for group numbers in the request vars, otherwise use the
2492      * defaults
2493      */
2494     viceid = pblock_findval("nsafs_viceid", rq->vars);
2495     group0 = pblock_findval("nsafs_group0", rq->vars);
2496     group1 = pblock_findval("nsafs_group1", rq->vars);
2497     nsafs_set_id_from_strings(viceid, group0, group1);
2498
2499     /*
2500      * Get the file attributes, index does not work on symbolic links.
2501      */
2502     rc = uafs_lstat(path, &st);
2503     if (rc < 0) {
2504         code = errno;
2505         return nsafs_error_check(code, NULL, pb, sn, rq);
2506     }
2507     if ((st.st_mode & S_IFMT) == S_IFLNK) {
2508         log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot INDEX links");
2509         protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2510         pblock_nvinsert("Allow", NSAFS_LINK_ALLOW, rq->srvhdrs);
2511         return REQ_ABORTED;
2512     } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
2513         log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot INDEX files");
2514         protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2515         pblock_nvinsert("Allow", NSAFS_FILE_ALLOW, rq->srvhdrs);
2516         return REQ_ABORTED;
2517     }
2518
2519     /*
2520      * Set the content type to "text/html"
2521      */
2522     param_free(pblock_remove("content-type", rq->srvhdrs));
2523     pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
2524
2525     /*
2526      * Build a binary tree of directory entries, and calculate the
2527      * length of our response message
2528      */
2529     dirp = uafs_opendir(path);
2530     if (dirp == NULL) {
2531         code = errno;
2532         return nsafs_error_check(code, NULL, pb, sn, rq);
2533     }
2534     dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2535     tmpPath = afs_osi_Alloc(NSAFS_MAX_PATH);
2536     root = NULL;
2537     contentLength = 0;
2538     while ((enp = uafs_readdir(dirp)) != NULL) {
2539         sprintf(tmpPath, "%s/%s", path, enp->d_name);
2540         rc = uafs_lstat(tmpPath, &st);
2541         if (rc < 0) {
2542             continue;
2543         } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2544             sprintf(dirbuf, "%s directory %u %u\r\n",
2545                     enp->d_name, st.st_size, st.st_mtime);
2546         } else if ((st.st_mode & S_IFMT) == S_IFLNK) {
2547             sprintf(dirbuf, "%s link %u %u\r\n",
2548                     enp->d_name, st.st_size, st.st_mtime);
2549         } else {
2550             sprintf(dirbuf, "%s unknown %u %u\r\n",
2551                     enp->d_name, st.st_size, st.st_mtime);
2552         }
2553         contentLength += strlen(dirbuf);
2554         nsafs_tree_insert(enp->d_name, dirbuf, &root);
2555     }
2556     if (errno != 0) {
2557         code = errno;
2558         uafs_closedir(dirp);
2559         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2560         afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2561         nsafs_tree_free(root);
2562         return nsafs_error_check(code, NULL, pb, sn, rq);
2563     }
2564     afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2565     rc = uafs_closedir(dirp);
2566     if (rc < 0) {
2567         code = errno;
2568         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2569         nsafs_tree_free(root);
2570         return nsafs_error_check(code, NULL, pb, sn, rq);
2571     }
2572
2573     /*
2574      * Set the content-length field in the reply header, and
2575      * start the reply message
2576      */
2577     pblock_nninsert("Content-Length", contentLength, rq->srvhdrs);
2578     protocol_status(sn, rq, PROTOCOL_OK, NULL);
2579     code = protocol_start_response(sn, rq);
2580     if (code != REQ_PROCEED) {
2581         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2582         nsafs_tree_free(root);
2583         return REQ_PROCEED;
2584     }
2585
2586     /*
2587      * Send the index
2588      */
2589     buflen = 0;
2590     code = nsafs_tree_send(sn->csd, root, dirbuf, &buflen, NSAFS_BUFFER_SIZE);
2591     if (code == IO_ERROR) {
2592         afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2593         nsafs_tree_free(root);
2594         log_error(LOG_INFORM, "nsafs", sn, rq, "send_index IO_ERROR");
2595         return REQ_EXIT;
2596     }
2597     nsafs_tree_free(root);
2598     if (buflen != 0) {
2599         code = net_write(sn->csd, dirbuf, buflen);
2600         if (code == IO_ERROR) {
2601             afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2602             log_error(LOG_INFORM, "nsafs", sn, rq, "send_index IO_ERROR");
2603             return REQ_EXIT;
2604         }
2605     }
2606     afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2607
2608     return REQ_PROCEED;
2609 }