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