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