2 * Copyright 2000, International Business Machines Corporation and others.
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
11 * User space client specific interface glue
14 #include <afsconfig.h>
15 #include "afs/param.h"
18 #include "afs/sysincludes.h" /* Standard vendor system headers */
20 #include "afsincludes.h" /* Afs-based standard headers */
21 #include "afs/afs_stats.h"
22 #include "afs_usrops.h"
24 #include "afs/cellconfig.h"
26 #include "afs/kautils.h"
27 #include "afs/nsafs.h"
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 */
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"
42 #define MAX(A,B) ((A)>(B)?(A):(B))
45 #define MIN(A,B) ((A)<(B)?(A):(B))
49 * Used by KA module to get local cell info
51 struct afsconf_dir *KA_conf;
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.
58 CRITICAL nsafs_init_lock;
64 char *cacheBaseDirParam;
67 int cacheStatEntriesParam;
73 long maxExpirationParam;
76 * Structure user to store entries in AFS login cache
79 afs_uint32 expiration;
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;
91 * Value used to initialize SHA checksums on username/password pairs
93 afs_uint32 nsafs_login_pad[SHA_HASH_INTS] = {
94 0x0D544971, 0x2281AC5B, 0x58B51218, 0x4085E08D, 0xB68C484B
100 CRITICAL nsafs_login_lock;
102 struct nsafs_login *head;
103 struct nsafs_login *tail;
104 } nsafs_login_cache[NSAFS_LOGIN_HASH_SIZE];
107 * Mapping from characters to 64 bit values
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
145 * Decode a base64 encoded buffer in place
148 nsafs_decode64(char *buf)
158 * Allow trailing blanks
160 for (len = strlen(buf); buf[len - 1] == ' ' && len > 0; len--);
163 * Valid encodings are multiples of four characters
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);
184 * Interface for pioctls - used for unlogging
186 #include "afs/venus.h"
188 do_pioctl(char *in_buffer, int in_size, char *out_buffer, int out_size,
189 int opcode, char *path, int followSymLinks)
191 struct ViceIoctl iob;
193 iob.in_size = in_size;
194 iob.out = out_buffer;
195 iob.out_size = out_size;
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 */
206 * unlog - invalidate any existing AFS tokens with the kernel cache
207 * manager. In case the server is started up with tokens
212 return do_pioctl(NULL, 0, NULL, 0, VIOCUNPAG, NULL, 0);
216 * Initialize the AFS client and the login cache
222 crit_enter(nsafs_init_lock);
223 if (nsafs_init_done == 0) {
226 /* printf("unlog from AFS failed: errno:%d\n", errno); */
228 uafs_Init("nsafs-init", mountDirParam, confDirParam,
229 cacheBaseDirParam, cacheBlocksParam, cacheFilesParam,
230 cacheStatEntriesParam, dCacheSizeParam, vCacheSizeParam,
231 chunkSizeParam, 0, debugParam, nDaemonsParam, -1,
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);
241 crit_exit(nsafs_init_lock);
245 * Hash function for the AFS login cache
248 nsafs_login_hash(char *name, char *cell)
252 for (val = *name, p = name; *p != '\0'; p++) {
253 val = (val << 2) ^ val ^ (afs_uint32) (*p);
255 for (p = cell; *p != '\0'; p++) {
256 val = (val << 2) ^ val ^ (afs_uint32) (*p);
258 return val & (NSAFS_LOGIN_HASH_SIZE - 1);
262 * Compute a SHA checksum on the username, cellname, and password
265 nsafs_login_checksum(char *user, char *cell, char *passwd, char *cksum)
274 * Compute SHA(username,SHA(password,pad))
276 passwdLen = strlen(passwd);
277 userLen = strlen(user);
278 cellLen = strlen(cell);
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]),
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);
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);
298 * Set the AFS identity given from the group0 and group1 strings
301 nsafs_set_id_from_ints(int viceid, int group0, int group1)
304 struct usr_ucred *crp;
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();
315 for (i = 3; i < NGROUPS; i++) {
316 crp->cr_groups[i] = NOGROUP;
321 * Set the AFS identity given from the viceid, group0 and group1 strings
324 nsafs_set_id_from_strings(char *viceid, char *group0, char *group1)
327 struct usr_ucred *crp;
329 if (viceid != NULL && group0 != NULL && group1 != NULL) {
330 nsafs_set_id_from_ints(atoi(viceid), atoi(group0), atoi(group1));
332 u.u_viceid = getuid();
334 set_cr_uid(crp, getuid());
335 set_cr_ruid(crp, getuid());
336 crp->cr_suid = getuid();
337 crp->cr_groups[0] = getgid();
339 for (i = 1; i < NGROUPS; i++) {
340 crp->cr_groups[i] = NOGROUP;
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.
351 nsafs_login_lookup(char *user, char *cell, char *cksum, int *viceid,
352 int *group0, int *group1)
356 struct nsafs_login *loginP, *tmpP, loginTmp;
359 * Search the hash chain for a matching entry, free
360 * expired entries as we search
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) {
370 nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
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));
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);
387 loginP = loginP->next;
389 crit_exit(nsafs_login_lock);
394 * Insert a login ID into the cache. If the user already has an entry,
395 * then overwrite the old entry.
398 nsafs_login_store(char *user, char *cell, char *cksum, int viceid, int group0,
399 int group1, afs_uint32 expiration)
403 struct nsafs_login *loginP, *tmpP, loginTmp;
406 * Search the hash chain for a matching entry, free
407 * expired entries as we search
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) {
418 if (loginP->expiration < curTime) {
421 nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
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));
428 loginP = loginP->next;
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);
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,
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);
455 * Extract a string parameter from the parameter block
458 nsafs_get_string(char **paramP, char *dflt, char *name, pblock * pb,
459 Session * sn, Request * rq)
464 tmpPtr = pblock_findval(name, pb);
465 if (tmpPtr == NULL) {
467 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
468 "nsafs_init: please supply a %s parameter", name);
474 *paramP = afs_osi_Alloc(strlen(tmpPtr) + 1);
475 strcpy(*paramP, tmpPtr);
480 * Extract a long integer parameter from the parameter block
483 nsafs_get_long(long *paramP, long dflt, char *name, pblock * pb, Session * sn,
489 start = pblock_findval(name, pb);
492 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
493 "nsafs_init: please supply a %s parameter", name);
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);
511 * Extract an integer parameter from the parameter block
514 nsafs_get_int(int *paramP, int dflt, char *name, pblock * pb, Session * sn,
520 code = nsafs_get_long(&val, (long)dflt, name, pb, sn, rq);
521 if (code == REQ_PROCEED) {
528 * Parse the authorization header for username and password
531 nsafs_parse_authhdr(char *authHdr, char *user, char *cell, char *passwd)
541 * Skip leading blanks, check for basic authentication
543 for (p = authHdr; *p == ' ' && *p != '\0'; p++);
544 if (strncasecmp(p, "basic ", 6) != 0) {
547 for (p += 6; *p == ' '; p++);
550 * Username and password are base64 encoded
555 * Format is user@cell:passwd. The user, cell or passwd may be missing
557 for (i = 0; *p != '@' && *p != ':' && *p != '\0'; p++, i++) {
562 for (i = 0, p++; *p != ':' && *p != '\0'; p++, i++) {
568 for (i = 0, p++; *p != '\0'; p++, i++) {
576 * Return an appropriate error given a system errno
579 nsafs_error_check(int code, char *text, pblock * pb, Session * sn,
588 sprintf(txtbuf, "%s: %s\n", text, strerror(code));
590 sprintf(txtbuf, "%s\n", strerror(code));
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
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)) {
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"))
614 * We overload EPERM (not super-user) to mean unauthenticated.
615 * We use the first subdirectory beneath the AFS mount point
618 log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
619 protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
620 path = pblock_findval("path", rq->vars);
623 path = uafs_afsPathName(path);
625 if (path != NULL && *path != '\0') {
626 realmBuf = strdup(path);
628 if (realmBuf == NULL) {
629 /* Don't dump core, just make AFS into one big realm */
630 sprintf(txtbuf, "Basic realm=\"%s\"", afs_mountDir);
632 /* extract the first subdirectory in AFS */
633 if ((tmp = strchr(realmBuf, '/')) != NULL)
635 sprintf(txtbuf, "Basic realm=\"%s/%s\"", afs_mountDir, realmBuf);
638 pblock_nvinsert("WWW-authenticate", txtbuf, rq->srvhdrs);
641 log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
642 protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
646 log_error(LOG_INFORM, "nsafs", sn, rq, txtbuf);
647 protocol_status(sn, rq, PROTOCOL_NOT_FOUND, NULL);
650 log_error(LOG_FAILURE, "nsafs", sn, rq, txtbuf);
651 protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
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.
663 nsafs_check_preconditions(struct stat *stp, pblock * pb, Session * sn,
668 struct tm tms, *tmsp;
671 mtime = stp->st_mtime;
672 tmsp = system_gmtime(&mtime, &tms);
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);
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);
690 * Set the content-length and last-modified response header
692 * We used to call protocol_set_finfo, but it wasn't handling
693 * if-unmodified-since headers correctly.
696 nsafs_set_finfo(Session * sn, Request * rq, struct stat *stp)
700 struct tm tms, *tmsp;
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"
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);
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.
725 nsafs_init(pblock * pb, Session * sn, Request * rq)
730 nsafs_init_lock = crit_init();
733 * Parse the startup parameters
735 code = nsafs_get_string(&mountDirParam, "/afs", "mount", pb, sn, rq);
736 if (code != REQ_PROCEED) {
739 code = nsafs_get_string(&cellNameParam, NULL, "cell", pb, sn, rq);
740 if (code != REQ_PROCEED) {
743 code = nsafs_get_string(&confDirParam, NULL, "confdir", pb, sn, rq);
744 if (code != REQ_PROCEED) {
747 code = nsafs_get_string(&cacheBaseDirParam, NULL, "cachedir", pb, sn, rq);
748 if (code != REQ_PROCEED) {
751 code = nsafs_get_int(&cacheBlocksParam, -1, "blocks", pb, sn, rq);
752 if (code != REQ_PROCEED) {
755 code = nsafs_get_int(&cacheFilesParam, 0, "files", pb, sn, rq);
756 if (code != REQ_PROCEED) {
759 code = nsafs_get_int(&cacheStatEntriesParam, -1, "stat", pb, sn, rq);
760 if (code != REQ_PROCEED) {
763 code = nsafs_get_int(&nDaemonsParam, -1, "daemons", pb, sn, rq);
764 if (code != REQ_PROCEED) {
767 code = nsafs_get_int(&dCacheSizeParam, 0, "dcache", pb, sn, rq);
768 if (code != REQ_PROCEED) {
771 code = nsafs_get_int(&vCacheSizeParam, 0, "volumes", pb, sn, rq);
772 if (code != REQ_PROCEED) {
775 code = nsafs_get_int(&chunkSizeParam, 0, "chunksize", pb, sn, rq);
776 if (code != REQ_PROCEED) {
779 code = nsafs_get_int(&debugParam, 0, "debug", pb, sn, rq);
780 if (code != REQ_PROCEED) {
783 code = nsafs_get_string(&logFileParam, NULL, "logfile", pb, sn, rq);
784 if (code != REQ_PROCEED) {
788 nsafs_get_long(&maxExpirationParam, LONG_MAX, "exp-max", pb, sn, rq);
789 if (code != REQ_PROCEED) {
797 * Extract name strings from a comma separated list
800 nsafs_NameFromNames(char *last, char *list, int *pos)
809 afs_osi_Free(last, strlen(last) + 1);
812 if (*start == '\0') {
815 for (len = 0; start[len] != ',' && start[len] != '\0'; len++);
817 if (list[*pos] == ',') {
820 retVal = afs_osi_Alloc(len + 1);
821 memcpy(retVal, start, len);
827 * Authcheck function for AFS
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.
836 nsafs_basic(pblock * pb, Session * sn, Request * rq)
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;
853 afs_uint32 expiration;
857 if (nsafs_init_done == 0) {
862 * Get the authorization header, if none found then continue,
863 * we check whether authorization is required later on.
865 request_header("authorization", &authHdr, sn, rq);
866 if (authHdr == NULL || strlen(authHdr) == 0) {
871 * Get the user name and password from the authorization header
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);
879 if (cell[0] == '\0') {
880 strcpy(cell, afs_LclCellName);
884 * Lookup the username and password in the login cache.
885 * If we find a match we reuse the existing identity.
887 nsafs_login_checksum(user, cell, passwd, &cksum[0]);
888 rc = nsafs_login_lookup(user, cell, cksum, &viceid, &group0, &group1);
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);
898 * Make sure the user is from one of the cells we are configured for
900 cellP = nsafs_NameFromNames(NULL, cellNameParam, &pos);
901 while (cellP != NULL) {
902 if (strcmp(cellP, cell) == 0) {
905 cellP = nsafs_NameFromNames(cellP, cellNameParam, &pos);
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);
912 afs_osi_Free(cellP, strlen(cellP) + 1);
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);
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);
927 expiration = u.u_expiration;
928 usr_assert(expiration != 0);
930 MIN(expiration, (afs_uint32) (time(NULL) + maxExpirationParam));
933 * Insert the credentials into the login cache
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);
947 * Name trans function for AFS
949 * Terminates the name translation step for files in AFS.
950 * Puts the AFS pathname into path and into ppath request vars.
953 nsafs_mount(pblock * pb, Session * sn, Request * rq)
960 * Get the URI from the request block
962 reqUri = pblock_findval("uri", rq->reqpb);
963 if (reqUri == NULL) {
967 if (uafs_afsPathName(reqUri) == NULL) {
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
977 request_header("new-url", &newReqUri, sn, rq);
978 if (newReqUri != NULL) {
979 if (util_uri_is_evil(newReqUri)) {
980 util_uri_parse(newReqUri);
982 if (uafs_afsPathName(newReqUri) != NULL) {
985 * We do not support moving files in or out of AFS
987 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
988 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
991 pblock_nvinsert("newpath", newReqUri, rq->vars);
993 } else if (isAfsPath) {
995 * We do not support moving files in or out of AFS
997 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
998 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1001 } else if (!isAfsPath) {
1002 return REQ_NOACTION;
1006 * This is an AFS request
1008 pblock_nvinsert("path", reqUri, rq->vars);
1009 pblock_nvinsert("ppath", reqUri, rq->vars);
1014 * Allow unauthorized users to access a specific directory in AFS
1017 nsafs_public(pblock * pb, Session * sn, Request * rq)
1023 path = pblock_findval("path", rq->vars);
1025 return REQ_NOACTION;
1027 public = pblock_findval("public", pb);
1028 if (public == NULL) {
1029 return REQ_NOACTION;
1033 * if the path is in AFS and is in the given directory then allow access
1035 if (util_uri_is_evil(path)) {
1036 util_uri_parse(path);
1038 if (uafs_afsPathName(path) == NULL) {
1039 return REQ_NOACTION;
1041 if (util_uri_is_evil(public)) {
1042 util_uri_parse(public);
1044 pubLen = strlen(public);
1045 if (strncmp(path, public, pubLen) != 0
1046 || (path[pubLen] != '/' && path[pubLen] != '\0')) {
1047 return REQ_NOACTION;
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);
1058 * Identify a path that should be an authentication realm.
1061 nsafs_realm(pblock * pb, Session * sn, Request * rq)
1068 * Ignore matches for the current path once we find a realm
1069 * line that matches this path.
1071 realm = pblock_findval("nsafs_realm", rq->vars);
1072 if (realm != NULL) {
1073 return REQ_NOACTION;
1076 path = pblock_findval("path", rq->vars);
1078 return REQ_NOACTION;
1080 if (util_uri_is_evil(path)) {
1081 util_uri_parse(path);
1083 if (uafs_afsPathName(path) == NULL) {
1084 return REQ_NOACTION;
1087 realm = pblock_findval("path", pb);
1088 if (realm == NULL) {
1089 return REQ_NOACTION;
1091 if (util_uri_is_evil(realm)) {
1092 util_uri_parse(realm);
1095 pathLen = strlen(realm);
1096 if (strncmp(path, realm, pathLen) != 0
1097 || (path[pathLen] != '/' && path[pathLen] != '\0')) {
1098 return REQ_NOACTION;
1100 pblock_nvinsert("nsafs_realm", realm, rq->vars);
1105 * Check whether any path elements beneath the nolinks directory
1106 * are symbolic links. Return REQ_PROCEED if no links are found.
1109 nsafs_check_for_links(char *path, char *nolinks, pblock * pb, Session * sn,
1121 * Check each component of the path below the nolinks directory.
1123 dirLen = strlen(nolinks);
1124 pathLen = strlen(path);
1125 if (pathLen < dirLen || strncmp(path, nolinks, dirLen) != 0
1126 || (path[dirLen] != '/' && path[dirLen] != '\0')) {
1129 if (path[dirLen] == '/') {
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';
1140 while (pathLen > dirLen) {
1141 rc = uafs_lstat(tmpPath, &st);
1144 afs_osi_Free(tmpPath, allocSize);
1145 return nsafs_error_check(code, NULL, pb, sn, rq);
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);
1151 while (tmpPath[pathLen] != '/' && pathLen > dirLen) {
1152 tmpPath[pathLen] = '\0';
1155 while (tmpPath[pathLen] == '/' && pathLen > dirLen) {
1156 tmpPath[pathLen] = '\0';
1160 afs_osi_Free(tmpPath, allocSize);
1165 * Deny access to symbolic links in a directory or its descendents.
1168 nsafs_nolinks(pblock * pb, Session * sn, Request * rq)
1178 path = pblock_findval("path", rq->vars);
1180 return REQ_NOACTION;
1182 nolinks = pblock_findval("nolinks", pb);
1183 if (nolinks == NULL) {
1184 return REQ_NOACTION;
1186 newPath = pblock_findval("newpath", rq->vars);
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.
1192 if (util_uri_is_evil(path)) {
1193 util_uri_parse(path);
1195 if (uafs_afsPathName(path) == NULL) {
1196 return REQ_NOACTION;
1198 if (util_uri_is_evil(nolinks)) {
1199 util_uri_parse(nolinks);
1203 * Check for group numbers in the request vars, otherwise use the
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);
1211 code = nsafs_check_for_links(path, nolinks, pb, sn, rq);
1212 if (code != REQ_PROCEED) {
1216 if (newPath != NULL) {
1217 if (util_uri_is_evil(newPath)) {
1218 util_uri_parse(newPath);
1220 if (uafs_afsPathName(newPath) == NULL) {
1221 return REQ_NOACTION;
1223 code = nsafs_check_for_links(newPath, nolinks, pb, sn, rq);
1224 if (code != REQ_PROCEED) {
1229 return REQ_NOACTION;
1233 * Set the MIME type for files in AFS.
1236 nsafs_force_type(pblock * pb, Session * sn, Request * rq)
1241 path = pblock_findval("path", rq->vars);
1243 return REQ_NOACTION;
1245 dflt = pblock_findval("type", pb);
1247 return REQ_NOACTION;
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.
1255 if (util_uri_is_evil(path)) {
1256 util_uri_parse(path);
1258 if (uafs_afsPathName(path) == NULL) {
1259 return REQ_NOACTION;
1262 if (pblock_findval("content-type", rq->srvhdrs) == NULL) {
1263 pblock_nvinsert("content-type", dflt, rq->srvhdrs);
1270 * Disable the Unauthorized response message so users never get
1271 * prompted for their name and password.
1274 nsafs_nocheck(pblock * pb, Session * sn, Request * rq)
1278 path = pblock_findval("path", rq->vars);
1280 return REQ_NOACTION;
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.
1288 if (util_uri_is_evil(path)) {
1289 util_uri_parse(path);
1291 if (uafs_afsPathName(path) == NULL) {
1292 return REQ_NOACTION;
1295 if (pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
1296 pblock_nvinsert("nsafs_nocheck", "TRUE", rq->vars);
1300 * If this is a public directory then proceed, otherwise access
1303 if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1307 return nsafs_error_check(EACCES, NULL, pb, sn, rq);
1311 * Require all requests for AFS files that are not explicitly made
1312 * public to be authenticated.
1315 nsafs_check(pblock * pb, Session * sn, Request * rq)
1319 path = pblock_findval("path", rq->vars);
1321 return REQ_NOACTION;
1325 * If the path is in AFS then require authentication
1327 if (util_uri_is_evil(path)) {
1328 util_uri_parse(path);
1330 if (uafs_afsPathName(path) == NULL) {
1331 return REQ_NOACTION;
1335 * If this is a public directory then proceed
1337 if (pblock_findval("nsafs_public", rq->vars) != NULL) {
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);
1351 * Find index files for directories
1354 nsafs_find_index(pblock * pb, Session * sn, Request * rq)
1368 path = pblock_findval("path", rq->vars);
1370 return REQ_NOACTION;
1373 indexNames = pblock_findval("index-names", pb);
1374 if (indexNames == NULL) {
1375 return REQ_NOACTION;
1379 * Skip pathnames that don't end in a slash
1381 if (*path == '\0' || path[strlen(path) - 1] != '/') {
1382 return REQ_NOACTION;
1386 * If the path is a directory then check if the directory has
1389 if (util_uri_is_evil(path)) {
1390 util_uri_parse(path);
1392 if (uafs_afsPathName(path) == NULL) {
1394 * Look for index files in local file system
1396 rc = stat(path, &st);
1399 return nsafs_error_check(code, NULL, pb, sn, rq);
1402 if ((st.st_mode & S_IFMT) != S_IFDIR) {
1403 return REQ_NOACTION;
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);
1418 afs_osi_Free(nameP, strlen(path) + strlen(indexP) + 2);
1419 indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1423 * Check for group numbers in the request vars, otherwise use the
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);
1432 * Look for index files in AFS
1434 rc = uafs_stat(path, &st);
1437 return nsafs_error_check(code, NULL, pb, sn, rq);
1440 if ((st.st_mode & S_IFMT) != S_IFDIR) {
1441 return REQ_NOACTION;
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);
1456 afs_osi_Free(nameP, strlen(path) + strlen(indexP) + 2);
1457 indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1460 return REQ_NOACTION;
1464 * Node in binary tree used to sort directory entries
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;
1476 #ifdef NSAFS_TREE_DEBUG
1478 * Validate that the given tree is a valid balanced binary tree
1481 nsafs_tree_check(struct nsafs_tree *root)
1487 if (root->left == NULL) {
1490 assert(strcmp(root->left->name, root->name) < 0);
1491 leftDepth = nsafs_tree_check(root->left);
1493 if (root->right == NULL) {
1496 assert(strcmp(root->right->name, root->name) >= 0);
1497 rightDepth = nsafs_tree_check(root->right);
1499 balance = rightDepth - leftDepth;
1500 assert(balance == root->balance);
1501 assert(balance >= -1);
1502 assert(balance <= 1);
1503 return (MAX(leftDepth, rightDepth) + 1);
1505 #endif /* NSAFS_TREE_DEBUG */
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)
1513 nsafs_node_insert(struct nsafs_tree *newNode, struct nsafs_tree **rootP)
1515 struct nsafs_tree *thisNode;
1519 if (strcmp(newNode->name, thisNode->name) < 0) {
1523 if (thisNode->left == NULL) {
1524 thisNode->left = newNode;
1525 if (thisNode->right == NULL) {
1526 thisNode->balance = -1;
1529 thisNode->balance = 0;
1533 delta = nsafs_node_insert(newNode, &thisNode->left);
1537 thisNode->balance -= delta;
1538 if (thisNode->balance == -2) {
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;
1552 thisNode->left->balance = 0;
1553 thisNode->balance = -(*rootP)->balance;
1554 (*rootP)->balance = 0;
1556 thisNode->left->right = (*rootP)->left;
1557 (*rootP)->left = thisNode->left;
1558 thisNode->left = (*rootP)->right;
1559 (*rootP)->right = thisNode;
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;
1571 } else if (thisNode->balance != 0) {
1581 if (thisNode->right == NULL) {
1582 thisNode->right = newNode;
1583 if (thisNode->left == NULL) {
1584 thisNode->balance = 1;
1587 thisNode->balance = 0;
1591 delta = nsafs_node_insert(newNode, &thisNode->right);
1595 thisNode->balance += delta;
1596 if (thisNode->balance == 2) {
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;
1610 thisNode->right->balance = 0;
1611 thisNode->balance = -(*rootP)->balance;
1612 (*rootP)->balance = 0;
1614 thisNode->right->left = (*rootP)->right;
1615 (*rootP)->right = thisNode->right;
1616 thisNode->right = (*rootP)->left;
1617 (*rootP)->left = thisNode;
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;
1629 } else if (thisNode->balance != 0) {
1639 * Allocate storage for a new directory entry, copy in the name and
1640 * text, and insert the entry into the balanced binary tree.
1643 nsafs_tree_insert(char *name, char *text, struct nsafs_tree **rootP)
1648 struct nsafs_tree *newNode;
1651 * allocate storage, initialize the fields, and copy in the data
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);
1669 * If this is the first node, insert it here, otherwise call
1670 * nsafs_node_insert to insert the node into the balanced
1673 if (*rootP == NULL) {
1676 nsafs_node_insert(newNode, rootP);
1678 #ifdef NSAFS_TREE_DEBUG
1679 nsafs_tree_check(*rootP);
1680 #endif /* NSAFS_TREE_DEBUG */
1684 * Transmit the contents of the tree
1687 nsafs_tree_send(SYS_NETFD sd, struct nsafs_tree *root, char *outbuf,
1688 int *buflen, int bufsize)
1691 struct nsafs_tree *node;
1697 * Recurse left, iterate right
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) {
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) {
1717 len = MIN(txtLen, bufsize - *buflen);
1718 memcpy((void *)(outbuf + *buflen), (void *)txtBuf, len);
1729 * Free the binary tree and all data within
1732 nsafs_tree_free(struct nsafs_tree *root)
1734 struct nsafs_tree *node, *next;
1737 * Iterate left, recurse right
1740 while (node != NULL) {
1741 if (node->right != NULL) {
1742 nsafs_tree_free(node->right);
1746 sizeof(struct nsafs_tree) + strlen(node->name) +
1747 strlen(node->text) + 2);
1753 * Send the contents of an AFS directory, Simple directory format
1756 nsafs_send_directory(char *path, struct stat *stp, pblock * pb, Session * sn,
1763 struct usr_dirent *enp;
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;
1774 * Set the content type to "text/html"
1776 param_free(pblock_remove("content-type", rq->srvhdrs));
1777 pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
1780 * Build a binary tree of directory entries, and calculate the
1781 * length of our response message
1783 dirp = uafs_opendir(path);
1786 return nsafs_error_check(code, NULL, pb, sn, rq);
1790 dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1791 while ((enp = uafs_readdir(dirp)) != NULL) {
1792 if (strcmp(enp->d_name, ".") == 0) {
1794 } else if (strcmp(enp->d_name, "..") == 0) {
1795 filename = "Parent Directory";
1797 filename = enp->d_name;
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);
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);
1811 rc = uafs_closedir(dirp);
1814 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1815 nsafs_tree_free(root);
1816 return nsafs_error_check(code, NULL, pb, sn, rq);
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
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);
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);
1842 * Send the HTML header, file data and HTML trailer
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");
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");
1858 nsafs_tree_free(root);
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");
1867 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1868 if (net_write(sn->csd, htmlTrl, strlen(htmlTrl)) == IO_ERROR) {
1876 * Send the contents of an AFS file
1879 nsafs_send_file(char *path, struct stat *stp, pblock * pb, Session * sn,
1889 * Make sure we can open the file before we send the response header
1891 fd = uafs_open(path, O_RDONLY, 0);
1894 return nsafs_error_check(code, NULL, pb, sn, rq);
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.
1903 code = nsafs_set_finfo(sn, rq, stp);
1904 if (code != REQ_PROCEED) {
1907 protocol_status(sn, rq, PROTOCOL_OK, NULL);
1908 code = protocol_start_response(sn, rq);
1909 if (code != REQ_PROCEED) {
1914 * Send the file contents
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);
1921 log_error(LOG_INFORM, "nsafs", sn, rq, "send_file IO_ERROR");
1928 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1929 log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_read, err=%d", code);
1932 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1933 rc = uafs_close(fd);
1936 log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_close, err=%d", code);
1942 * Service function for AFS files and directories
1945 nsafs_send(pblock * pb, Session * sn, Request * rq)
1958 if (nsafs_init_done == 0) {
1963 * Only service paths that are in AFS
1965 path = pblock_findval("path", rq->vars);
1967 return REQ_NOACTION;
1969 if (uafs_afsPathName(path) == NULL) {
1970 return REQ_NOACTION;
1974 * We do not support content-range headers
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");
1984 * Check for group numbers in the request vars, otherwise use the
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);
1993 * Get the file attributes
1995 rc = uafs_stat(path, &st);
1998 return nsafs_error_check(code, NULL, pb, sn, rq);
2002 * Check the request preconditions
2004 code = nsafs_check_preconditions(&st, pb, sn, rq);
2005 if (code != REQ_PROCEED) {
2010 * Send the contents of files, and a formatted index of directories
2012 if ((st.st_mode & S_IFMT) == S_IFDIR) {
2014 dirPath = afs_osi_Alloc(len + 2);
2015 strcpy(dirPath, path);
2016 if (dirPath[len - 1] != '/') {
2018 dirPath[len + 1] = '\0';
2020 if (util_uri_is_evil(dirPath)) {
2021 util_uri_parse(dirPath);
2023 code = nsafs_send_directory(dirPath, &st, pb, sn, rq);
2024 afs_osi_Free(dirPath, len + 2);
2026 code = nsafs_send_file(path, &st, pb, sn, rq);
2032 * Service function to create new AFS files
2035 nsafs_put(pblock * pb, Session * sn, Request * rq)
2055 if (nsafs_init_done == 0) {
2060 * Only service paths that are in AFS
2062 path = pblock_findval("path", rq->vars);
2064 return REQ_NOACTION;
2066 if (uafs_afsPathName(path) == NULL) {
2067 return REQ_NOACTION;
2071 * We do not support content-range headers
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");
2081 * Check for group numbers in the request vars, otherwise use the
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);
2090 * Check for a content length header
2092 request_header("content-length", &lengthHdr, sn, rq);
2093 if (lengthHdr != NULL) {
2094 contentLength = atoi(lengthHdr);
2100 * Get the file attributes
2102 rc = uafs_stat(path, &st);
2105 if (code == ENOENT) {
2106 rspStatus = PROTOCOL_CREATED;
2108 return nsafs_error_check(code, NULL, pb, sn, rq);
2110 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2111 return nsafs_error_check(EISDIR, NULL, pb, sn, rq);
2113 rspStatus = PROTOCOL_OK;
2116 fd = uafs_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
2119 return nsafs_error_check(code, NULL, pb, sn, rq);
2123 * Get the file contents
2125 filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2128 if (contentLength < 0) {
2129 bytesToRead = NSAFS_BUFFER_SIZE;
2131 bytesToRead = MIN(contentLength, NSAFS_BUFFER_SIZE);
2132 if (bytesToRead == 0) {
2136 for (bytesRead = 0; !eof && bytesRead < bytesToRead; bytesRead++) {
2137 rc = netbuf_getc(sn->inbuf);
2140 } else if (rc == IO_ERROR) {
2142 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2144 return nsafs_error_check(EIO, NULL, pb, sn, rq);
2146 filebuf[bytesRead] = rc;
2149 if (bytesRead > 0) {
2150 if (contentLength > 0) {
2151 contentLength -= bytesRead;
2153 rc = uafs_write(fd, filebuf, bytesRead);
2156 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2158 return nsafs_error_check(code, NULL, pb, sn, rq);
2162 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2163 rc = uafs_close(fd);
2166 return nsafs_error_check(code, NULL, pb, sn, rq);
2168 if (contentLength > 0) {
2169 log_error(LOG_FAILURE, "nsafs", sn, rq, "received partial contents");
2173 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2174 protocol_status(sn, rq, rspStatus, NULL);
2175 code = protocol_start_response(sn, rq);
2180 * Service function to delete AFS files
2183 nsafs_delete(pblock * pb, Session * sn, Request * rq)
2193 if (nsafs_init_done == 0) {
2198 * Only service paths that are in AFS
2200 path = pblock_findval("path", rq->vars);
2202 return REQ_NOACTION;
2204 if (uafs_afsPathName(path) == NULL) {
2205 return REQ_NOACTION;
2209 * Check for group numbers in the request vars, otherwise use the
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);
2218 * Get the file attributes
2220 rc = uafs_lstat(path, &st);
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);
2231 rc = uafs_unlink(path);
2234 return nsafs_error_check(code, NULL, pb, sn, rq);
2237 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2238 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2239 code = protocol_start_response(sn, rq);
2244 * Service function to create AFS directories
2247 nsafs_mkdir(pblock * pb, Session * sn, Request * rq)
2257 if (nsafs_init_done == 0) {
2262 * Only service paths that are in AFS
2264 path = pblock_findval("path", rq->vars);
2266 return REQ_NOACTION;
2268 if (uafs_afsPathName(path) == NULL) {
2269 return REQ_NOACTION;
2273 * Check for group numbers in the request vars, otherwise use the
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);
2282 * Create the directory
2284 rc = uafs_mkdir(path, 0755);
2287 return nsafs_error_check(code, NULL, pb, sn, rq);
2290 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2291 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2292 code = protocol_start_response(sn, rq);
2297 * Service function to delete AFS directories
2300 nsafs_rmdir(pblock * pb, Session * sn, Request * rq)
2310 if (nsafs_init_done == 0) {
2315 * Only service paths that are in AFS
2317 path = pblock_findval("path", rq->vars);
2319 return REQ_NOACTION;
2321 if (uafs_afsPathName(path) == NULL) {
2322 return REQ_NOACTION;
2326 * Check for group numbers in the request vars, otherwise use the
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);
2335 * Get the file attributes, rmdir only work on directories.
2337 rc = uafs_lstat(path, &st);
2340 return nsafs_error_check(code, NULL, pb, sn, rq);
2344 * Call unlink to delete symbolic links to directories, and rmdir
2345 * to to delete directories.
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);
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);
2362 return nsafs_error_check(code, NULL, pb, sn, rq);
2365 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2366 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2367 code = protocol_start_response(sn, rq);
2372 * Service function to rename AFS files and directories
2375 nsafs_move(pblock * pb, Session * sn, Request * rq)
2386 if (nsafs_init_done == 0) {
2391 * Only service paths that are in AFS
2393 path = pblock_findval("path", rq->vars);
2395 return REQ_NOACTION;
2397 if (uafs_afsPathName(path) == NULL) {
2398 return REQ_NOACTION;
2400 newPath = pblock_findval("newpath", rq->vars);
2401 if (newPath == NULL) {
2402 return REQ_NOACTION;
2404 if (uafs_afsPathName(newPath) == NULL) {
2405 return REQ_NOACTION;
2409 * Check for group numbers in the request vars, otherwise use the
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);
2420 rc = uafs_rename(path, newPath);
2423 return nsafs_error_check(code, NULL, pb, sn, rq);
2426 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2427 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2428 code = protocol_start_response(sn, rq);
2433 * Send the index of an AFS directory
2436 nsafs_index(pblock * pb, Session * sn, Request * rq)
2446 struct usr_dirent *enp;
2451 struct nsafs_tree *root;
2453 if (nsafs_init_done == 0) {
2458 * Only service paths that are in AFS
2460 path = pblock_findval("path", rq->vars);
2462 return REQ_NOACTION;
2464 if (uafs_afsPathName(path) == NULL) {
2465 return REQ_NOACTION;
2469 * Check for group numbers in the request vars, otherwise use the
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);
2478 * Get the file attributes, index does not work on symbolic links.
2480 rc = uafs_lstat(path, &st);
2483 return nsafs_error_check(code, NULL, pb, sn, rq);
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);
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);
2498 * Set the content type to "text/html"
2500 param_free(pblock_remove("content-type", rq->srvhdrs));
2501 pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
2504 * Build a binary tree of directory entries, and calculate the
2505 * length of our response message
2507 dirp = uafs_opendir(path);
2510 return nsafs_error_check(code, NULL, pb, sn, rq);
2512 dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2513 tmpPath = afs_osi_Alloc(NSAFS_MAX_PATH);
2516 while ((enp = uafs_readdir(dirp)) != NULL) {
2517 sprintf(tmpPath, "%s/%s", path, enp->d_name);
2518 rc = uafs_lstat(tmpPath, &st);
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,
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,
2528 sprintf(dirbuf, "%s unknown %u %u\r\n", enp->d_name, st.st_size,
2531 contentLength += strlen(dirbuf);
2532 nsafs_tree_insert(enp->d_name, dirbuf, &root);
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);
2542 afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2543 rc = uafs_closedir(dirp);
2546 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2547 nsafs_tree_free(root);
2548 return nsafs_error_check(code, NULL, pb, sn, rq);
2552 * Set the content-length field in the reply header, and
2553 * start the reply message
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);
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");
2575 nsafs_tree_free(root);
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");
2584 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);