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"
19 #include "../afs/sysincludes.h" /* Standard vendor system headers */
21 #include "../afs/afsincludes.h" /* Afs-based standard headers */
22 #include "../afs/afs_stats.h"
23 #include "../afs/afs_usrops.h"
24 #include "../afs/auth.h"
25 #include "../afs/cellconfig.h"
26 #include "../afs/vice.h"
27 #include "../afs/kautils.h"
28 #include "../afs/nsafs.h"
30 #define NSAFS_DFLT_RCVTHREADS 2 /* Dflt number recevice threads */
31 #define NSAFS_BUFFER_SIZE 4096 /* Send/Receive buffer size */
32 #define NSAFS_MAX_PATH 1024 /* Maximum path length */
33 #define NSAFS_USERNAME_MAX 64 /* Maximum username length */
34 #define NSAFS_PASSWORD_MAX 64 /* Maximum password length */
35 #define NSAFS_LOGIN_HASH_SIZE 1024 /* MUST be power of two */
36 #define TEN_MINUTES 600 /* 10 minutes = 600 seconds */
38 #define NSAFS_DIR_ALLOW "GET,HEAD,MOVE,INDEX,RMDIR"
39 #define NSAFS_LINK_ALLOW "GET,HEAD,MOVE,DELETE"
40 #define NSAFS_FILE_ALLOW "GET,HEAD,MOVE,PUT,DELETE"
43 #define MAX(A,B) ((A)>(B)?(A):(B))
46 #define MIN(A,B) ((A)<(B)?(A):(B))
50 * Used by KA module to get local cell info
52 struct afsconf_dir *KA_conf;
55 * Initialization parameters. The plugin is initialized in
56 * the Netscape parent process, but we don't initialize AFS
57 * until we are in the child process.
59 CRITICAL nsafs_init_lock;
65 char *cacheBaseDirParam;
68 int cacheStatEntriesParam;
74 long maxExpirationParam;
77 * Structure user to store entries in AFS login cache
80 afs_uint32 expiration;
84 char username[NSAFS_USERNAME_MAX];
85 char cellname[NSAFS_USERNAME_MAX];
86 char cksum[SHA_HASH_BYTES];
87 struct nsafs_login *next;
88 struct nsafs_login *prev;
92 * Value used to initialize SHA checksums on username/password pairs
94 afs_uint32 nsafs_login_pad[SHA_HASH_INTS] = {
95 0x0D544971, 0x2281AC5B, 0x58B51218, 0x4085E08D, 0xB68C484B
101 CRITICAL nsafs_login_lock;
103 struct nsafs_login *head;
104 struct nsafs_login *tail;
105 } nsafs_login_cache[NSAFS_LOGIN_HASH_SIZE];
108 * Mapping from characters to 64 bit values
110 static int base64_to_value[256] = {
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f,
117 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
118 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
120 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
121 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
122 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
124 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
125 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
126 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
146 * Decode a base64 encoded buffer in place
148 void 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"
187 int do_pioctl(char *in_buffer, int in_size,
188 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), &iob,
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
211 return do_pioctl(NULL, 0, NULL, 0, VIOCUNPAG, NULL, 0);
215 * Initialize the AFS client and the login cache
217 void nsafs_init_once()
220 crit_enter(nsafs_init_lock);
221 if (nsafs_init_done == 0) {
224 /* printf("unlog from AFS failed: errno:%d\n", errno); */
226 uafs_Init("nsafs-init", mountDirParam, confDirParam,
227 cacheBaseDirParam, cacheBlocksParam, cacheFilesParam,
228 cacheStatEntriesParam, dCacheSizeParam, vCacheSizeParam,
229 chunkSizeParam, 0, debugParam, nDaemonsParam, -1,
231 nsafs_login_lock = crit_init();
232 for (i = 0 ; i < NSAFS_LOGIN_HASH_SIZE ; i++) {
233 DLL_INIT_LIST(nsafs_login_cache[i].head,
234 nsafs_login_cache[i].tail);
239 crit_exit(nsafs_init_lock);
243 * Hash function for the AFS login cache
245 int nsafs_login_hash(char *name, char *cell)
249 for (val = *name , p = name ; *p != '\0' ; p++) {
250 val = (val << 2) ^ val ^ (afs_uint32)(*p);
252 for (p = cell ; *p != '\0' ; p++) {
253 val = (val << 2) ^ val ^ (afs_uint32)(*p);
255 return val & (NSAFS_LOGIN_HASH_SIZE-1);
259 * Compute a SHA checksum on the username, cellname, and password
261 void nsafs_login_checksum(
274 * Compute SHA(username,SHA(password,pad))
276 passwdLen = strlen(passwd);
277 userLen = strlen(user);
278 cellLen = strlen(cell);
279 shaBuffer = afs_osi_Alloc(MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
280 strcpy(shaBuffer, passwd);
281 memcpy((void *)(shaBuffer + passwdLen), (void *)(&nsafs_login_pad[0]),
284 sha_hash(&state, shaBuffer, passwdLen+SHA_HASH_BYTES);
285 memcpy(shaBuffer, user, userLen);
286 memcpy(shaBuffer+userLen, cell, cellLen);
287 sha_bytes(&state, shaBuffer+userLen+cellLen);
289 sha_hash(&state, shaBuffer, userLen+cellLen+SHA_HASH_BYTES);
290 sha_bytes(&state, &cksum[0]);
291 memset(shaBuffer, 0, MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
292 afs_osi_Free(shaBuffer, MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
296 * Set the AFS identity given from the group0 and group1 strings
298 void nsafs_set_id_from_ints(int viceid, int group0, int group1)
301 struct usr_ucred *crp;
305 crp->cr_uid = viceid;
306 crp->cr_ruid = viceid;
307 crp->cr_suid = viceid;
308 crp->cr_groups[0] = group0;
309 crp->cr_groups[1] = group1;
310 crp->cr_groups[2] = getgid();
312 for (i = 3 ; i < NGROUPS ; i++) {
313 crp->cr_groups[i] = NOGROUP;
318 * Set the AFS identity given from the viceid, group0 and group1 strings
320 void nsafs_set_id_from_strings(char *viceid, char *group0, char *group1)
323 struct usr_ucred *crp;
325 if (viceid != NULL && group0 != NULL && group1 != NULL) {
326 nsafs_set_id_from_ints(atoi(viceid), atoi(group0), atoi(group1));
328 u.u_viceid = getuid();
330 crp->cr_uid = getuid();
331 crp->cr_ruid = getuid();
332 crp->cr_suid = getuid();
333 crp->cr_groups[0] = getgid();
335 for (i = 1 ; i < NGROUPS ; i++) {
336 crp->cr_groups[i] = NOGROUP;
342 * Look up a login ID in the cache. If an entry name is found for the
343 * given username, and the SHA checksums match, then set the group0
344 * and group1 parameters and return 1, otherwise return 0.
346 int nsafs_login_lookup(
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.
397 int nsafs_login_store(
404 afs_uint32 expiration)
408 struct nsafs_login *loginP, *tmpP, loginTmp;
411 * Search the hash chain for a matching entry, free
412 * expired entries as we search
414 index = nsafs_login_hash(user, cell);
415 curTime = time(NULL);
416 crit_enter(nsafs_login_lock);
417 loginP = nsafs_login_cache[index].head;
418 while (loginP != NULL) {
419 if (strcmp(loginP->username, user) == 0 &&
420 strcmp(loginP->cellname, cell) == 0) {
423 if (loginP->expiration < curTime) {
426 nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
428 DLL_DELETE(tmpP, nsafs_login_cache[index].head,
429 nsafs_login_cache[index].tail, next, prev);
430 afs_osi_Free(tmpP, sizeof(struct nsafs_login));
433 loginP = loginP->next;
435 if (loginP == NULL) {
436 loginP = (struct nsafs_login *)
437 afs_osi_Alloc(sizeof(struct nsafs_login));
438 strcpy(&loginP->username[0], user);
439 strcpy(&loginP->cellname[0], cell);
441 DLL_DELETE(loginP, nsafs_login_cache[index].head,
442 nsafs_login_cache[index].tail, next, prev);
443 nsafs_set_id_from_ints(loginP->viceid, loginP->group0, loginP->group1);
446 nsafs_set_id_from_ints(viceid, group0, group1);
447 memcpy((void *)&loginP->cksum[0], (void *)cksum, SHA_HASH_BYTES);
448 loginP->viceid = viceid;
449 loginP->group0 = group0;
450 loginP->group1 = group1;
451 loginP->expiration = expiration;
452 DLL_INSERT_TAIL(loginP, nsafs_login_cache[index].head,
453 nsafs_login_cache[index].tail, next, prev);
454 crit_exit(nsafs_login_lock);
459 * Extract a string parameter from the parameter block
461 int nsafs_get_string(
472 tmpPtr = pblock_findval(name, pb);
475 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
476 "nsafs_init: please supply a %s parameter", name);
482 *paramP = afs_osi_Alloc(strlen(tmpPtr)+1);
483 strcpy(*paramP, tmpPtr);
488 * Extract a long integer parameter from the parameter block
501 start = pblock_findval(name, pb);
504 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
505 "nsafs_init: please supply a %s parameter", name);
512 val = strtol(start, &end, 10);
513 if (val <= 0 || end == start || *end != '\0') {
514 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
515 "nsafs_init: invalid %s parameter '%s'", name, start);
523 * Extract an integer parameter from the parameter block
536 code = nsafs_get_long(&val, (long)dflt, name, pb, sn, rq);
537 if (code == REQ_PROCEED) {
544 * Parse the authorization header for username and password
546 void nsafs_parse_authhdr(
560 * Skip leading blanks, check for basic authentication
562 for (p = authHdr ; *p == ' ' && *p != '\0' ; p++);
563 if (strncasecmp(p, "basic ", 6) != 0) {
566 for (p += 6 ; *p == ' ' ; p++);
569 * Username and password are base64 encoded
574 * Format is user@cell:passwd. The user, cell or passwd may be missing
576 for ( i = 0 ; *p != '@' && *p != ':' && *p != '\0' ; p++ , i++) {
581 for (i = 0 , p++ ; *p != ':' && *p != '\0' ; p++ , i++) {
587 for (i = 0 , p++ ; *p != '\0' ; p++ , i++) {
595 * Return an appropriate error given a system errno
597 int nsafs_error_check(
610 sprintf(txtbuf, "%s: %s\n", text, strerror(code));
612 sprintf(txtbuf, "%s\n", strerror(code));
616 * Special case, if we get EACCES inside a public directory
617 * and are unauthenticated, change the error to EPERM unless
618 * nsafs_nocheck is set. If nsafs_nocheck is set then change
621 if (code == EACCES &&
622 pblock_findval("nsafs_nocheck", rq->vars) == NULL &&
623 (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
624 pblock_findval("nsafs_group0", rq->vars) == NULL ||
625 pblock_findval("nsafs_group1", rq->vars) == NULL)) {
627 } else if (code == EPERM &&
628 pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
629 char *status=pblock_findval("status",rq->vars);
630 if (strcmp(status,"Login Failed"))
637 * We overload EPERM (not super-user) to mean unauthenticated.
638 * We use the first subdirectory beneath the AFS mount point
641 log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
642 protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
643 path = pblock_findval("path", rq->vars);
646 path = uafs_afsPathName(path);
648 if (path != NULL && *path != '\0') {
649 realmBuf = strdup(path);
651 if (realmBuf == NULL) {
652 /* Don't dump core, just make AFS into one big realm */
653 sprintf(txtbuf, "Basic realm=\"%s\"", afs_mountDir);
655 /* extract the first subdirectory in AFS */
656 if ((tmp = strchr(realmBuf, '/')) != NULL)
658 sprintf(txtbuf, "Basic realm=\"%s/%s\"", afs_mountDir, realmBuf);
661 pblock_nvinsert("WWW-authenticate", txtbuf, rq->srvhdrs);
664 log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
665 protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
669 log_error(LOG_INFORM, "nsafs", sn, rq, txtbuf);
670 protocol_status(sn, rq, PROTOCOL_NOT_FOUND, NULL);
673 log_error(LOG_FAILURE, "nsafs", sn, rq, txtbuf);
674 protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
681 * Check the preconditions on a request. Return REQ_PROCEED
682 * if the preconditions are met. Any other return value means
683 * that the request has been aborted.
685 int nsafs_check_preconditions(
693 struct tm tms, *tmsp;
696 mtime = stp->st_mtime;
697 tmsp = system_gmtime(&mtime, &tms);
699 request_header("if-modified-since", &reqhdr, sn, rq);
700 if (reqhdr != NULL && util_later_than(tmsp, reqhdr)) {
701 protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL);
705 request_header("if-unmodified-since", &reqhdr, sn, rq);
706 if (reqhdr != NULL && !util_later_than(tmsp, reqhdr)) {
707 protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL);
715 * Set the content-length and last-modified response header
717 * We used to call protocol_set_finfo, but it wasn't handling
718 * if-unmodified-since headers correctly.
727 struct tm tms, *tmsp;
730 char *days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
731 char *months[] = {"Jan","Feb","Mar","Apr","May","Jun",
732 "Jul","Aug","Sep","Oct","Nov","Dec"};
734 mtime = stp->st_mtime;
735 tmsp = system_gmtime(&mtime, &tms);
736 sprintf(&dateStr[0],"%s, %02d %s %d %02d:%02d:%02d GMT",
737 days[tmsp->tm_wday], tmsp->tm_mday,
738 months[tmsp->tm_mon], tmsp->tm_year+1900,
739 tmsp->tm_hour, tmsp->tm_min, tmsp->tm_sec);
740 pblock_nvinsert("Last-Modified", &dateStr[0], rq->srvhdrs);
741 pblock_nninsert("Content-Length", stp->st_size, rq->srvhdrs);
747 * Initialize the AFS plugin. We do not initialize the AFS client
748 * here because we are still in the parent process. We don't
749 * initialize AFS until we get the first service request.
751 NSAPI_PUBLIC int nsafs_init(pblock *pb, Session *sn, Request *rq)
756 nsafs_init_lock = crit_init();
759 * Parse the startup parameters
761 code = nsafs_get_string(&mountDirParam, "/afs", "mount", pb, sn, rq);
762 if (code != REQ_PROCEED) {
765 code = nsafs_get_string(&cellNameParam, NULL, "cell", pb, sn, rq);
766 if (code != REQ_PROCEED) {
769 code = nsafs_get_string(&confDirParam, NULL, "confdir", pb, sn, rq);
770 if (code != REQ_PROCEED) {
773 code = nsafs_get_string(&cacheBaseDirParam, NULL, "cachedir", pb, sn, rq);
774 if (code != REQ_PROCEED) {
777 code = nsafs_get_int(&cacheBlocksParam, -1, "blocks", pb, sn, rq);
778 if (code != REQ_PROCEED) {
781 code = nsafs_get_int(&cacheFilesParam, 0, "files", pb, sn, rq);
782 if (code != REQ_PROCEED) {
785 code = nsafs_get_int(&cacheStatEntriesParam, -1, "stat", pb, sn, rq);
786 if (code != REQ_PROCEED) {
789 code = nsafs_get_int(&nDaemonsParam, -1, "daemons", pb, sn, rq);
790 if (code != REQ_PROCEED) {
793 code = nsafs_get_int(&dCacheSizeParam, 0, "dcache", pb, sn, rq);
794 if (code != REQ_PROCEED) {
797 code = nsafs_get_int(&vCacheSizeParam, 0, "volumes", pb, sn, rq);
798 if (code != REQ_PROCEED) {
801 code = nsafs_get_int(&chunkSizeParam, 0, "chunksize", pb, sn, rq);
802 if (code != REQ_PROCEED) {
805 code = nsafs_get_int(&debugParam, 0, "debug", pb, sn, rq);
806 if (code != REQ_PROCEED) {
809 code = nsafs_get_string(&logFileParam, NULL, "logfile", pb, sn, rq);
810 if (code != REQ_PROCEED) {
813 code = nsafs_get_long(&maxExpirationParam, LONG_MAX, "exp-max", pb, sn, rq);
814 if (code != REQ_PROCEED) {
822 * Extract name strings from a comma separated list
824 char *nsafs_NameFromNames(char *last, char *list, int *pos)
833 afs_osi_Free(last, strlen(last)+1);
836 if (*start == '\0') {
839 for (len = 0 ; start[len] != ',' && start[len] != '\0' ; len++);
841 if (list[*pos] == ',') {
844 retVal = afs_osi_Alloc(len+1);
845 memcpy(retVal, start, len);
851 * Authcheck function for AFS
853 * Check for an Authorization header. If there is one then do the
854 * AFS login and place the first two groups in the user's creds
855 * into the nsafs-group1 and nsafs-group2 request variables.
856 * Send an Unauthorized response if login fails to prompt the user
857 * to reenter the username and password.
859 NSAPI_PUBLIC int nsafs_basic(pblock *pb, Session *sn, Request *rq)
867 char user[NSAFS_USERNAME_MAX];
868 char cell[NSAFS_USERNAME_MAX];
869 char passwd[NSAFS_PASSWORD_MAX];
870 char cksum[SHA_HASH_BYTES];
871 struct usr_ucred *crp;
872 afs_int32 password_expires = -1;
876 afs_uint32 expiration;
880 if (nsafs_init_done == 0) {
885 * Get the authorization header, if none found then continue,
886 * we check whether authorization is required later on.
888 request_header("authorization", &authHdr, sn, rq);
889 if (authHdr == NULL || strlen(authHdr) == 0) {
894 * Get the user name and password from the authorization header
896 nsafs_parse_authhdr(authHdr, &user[0], &cell[0], &passwd[0]);
897 if (user[0] == '\0' || passwd[0] == '\0') {
898 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
899 pblock_nvinsert("status","Login Failed",rq->vars);
900 return nsafs_error_check(EPERM, "Invalid auth header", pb, sn, rq);
902 if (cell[0] == '\0') {
903 strcpy(cell, afs_LclCellName);
907 * Lookup the username and password in the login cache.
908 * If we find a match we reuse the existing identity.
910 nsafs_login_checksum(user, cell, passwd, &cksum[0]);
911 rc = nsafs_login_lookup(user, cell, cksum, &viceid, &group0, &group1);
913 pblock_nninsert("nsafs_viceid", viceid, rq->vars);
914 pblock_nninsert("nsafs_group0", group0, rq->vars);
915 pblock_nninsert("nsafs_group1", group1, rq->vars);
916 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
921 * Make sure the user is from one of the cells we are configured for
923 cellP = nsafs_NameFromNames(NULL, cellNameParam, &pos);
924 while (cellP != NULL) {
925 if (strcmp(cellP, cell) == 0) {
928 cellP = nsafs_NameFromNames(cellP, cellNameParam, &pos);
931 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
932 pblock_nvinsert("status","Login Failed",rq->vars);
933 return nsafs_error_check(EPERM, "Invalid cell", pb, sn, rq);
935 afs_osi_Free(cellP, strlen(cellP)+1);
938 nsafs_set_id_from_strings(NULL, NULL, NULL);
939 code = uafs_klog(user, cell, passwd, &reason);
940 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
943 sprintf(txtbuf, "%s@%s: %s\n", user, cell, reason);
944 pblock_nvinsert("status","Login Failed",rq->vars);
945 return nsafs_error_check(EPERM, txtbuf, pb, sn, rq);
950 expiration = u.u_expiration;
951 usr_assert(expiration != 0);
952 expiration = MIN(expiration, (afs_uint32)(time(NULL) + maxExpirationParam));
955 * Insert the credentials into the login cache
957 pblock_nvinsert("auth-type", "basic", rq->vars);
958 pblock_nvinsert("auth-user", user, rq->vars);
959 pblock_nninsert("nsafs_viceid", u.u_viceid, rq->vars);
960 pblock_nninsert("nsafs_group0", u.u_cred->cr_groups[0], rq->vars);
961 pblock_nninsert("nsafs_group1", u.u_cred->cr_groups[1], rq->vars);
962 nsafs_login_store(user, cell, cksum, u.u_viceid, u.u_cred->cr_groups[0],
963 u.u_cred->cr_groups[1], expiration);
969 * Name trans function for AFS
971 * Terminates the name translation step for files in AFS.
972 * Puts the AFS pathname into path and into ppath request vars.
974 NSAPI_PUBLIC int nsafs_mount(pblock *pb, Session *sn, Request *rq)
981 * Get the URI from the request block
983 reqUri = pblock_findval("uri", rq->reqpb);
984 if (reqUri == NULL) {
988 if (uafs_afsPathName(reqUri) == NULL) {
995 * If we have a new-url parameter then the new-url may be in AFS
996 * if and only if the path is in AFS
998 request_header("new-url", &newReqUri, sn, rq);
999 if (newReqUri != NULL) {
1000 if (util_uri_is_evil(newReqUri)) {
1001 util_uri_parse(newReqUri);
1003 if (uafs_afsPathName(newReqUri) != NULL) {
1006 * We do not support moving files in or out of AFS
1008 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
1009 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1012 pblock_nvinsert("newpath", newReqUri, rq->vars);
1014 } else if (isAfsPath) {
1016 * We do not support moving files in or out of AFS
1018 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
1019 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1022 } else if (!isAfsPath) {
1023 return REQ_NOACTION;
1027 * This is an AFS request
1029 pblock_nvinsert("path", reqUri, rq->vars);
1030 pblock_nvinsert("ppath", reqUri, rq->vars);
1035 * Allow unauthorized users to access a specific directory in AFS
1037 NSAPI_PUBLIC int nsafs_public(pblock *pb, Session *sn, Request *rq)
1043 path = pblock_findval("path", rq->vars);
1045 return REQ_NOACTION;
1047 public = pblock_findval("public", pb);
1048 if (public == NULL) {
1049 return REQ_NOACTION;
1053 * if the path is in AFS and is in the given directory then allow access
1055 if (util_uri_is_evil(path)) {
1056 util_uri_parse(path);
1058 if (uafs_afsPathName(path) == NULL) {
1059 return REQ_NOACTION;
1061 if (util_uri_is_evil(public)) {
1062 util_uri_parse(public);
1064 pubLen = strlen(public);
1065 if (strncmp(path, public, pubLen) != 0 ||
1066 (path[pubLen] != '/' && path[pubLen] != '\0')) {
1067 return REQ_NOACTION;
1069 if (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
1070 pblock_findval("nsafs_group0", rq->vars) == NULL ||
1071 pblock_findval("nsafs_group1", rq->vars) == NULL) {
1072 pblock_nvinsert("nsafs_public", "TRUE", rq->vars);
1078 * Identify a path that should be an authentication realm.
1080 NSAPI_PUBLIC int nsafs_realm(pblock *pb, Session *sn, Request *rq)
1087 * Ignore matches for the current path once we find a realm
1088 * line that matches this path.
1090 realm = pblock_findval("nsafs_realm", rq->vars);
1091 if (realm != NULL) {
1092 return REQ_NOACTION;
1095 path = pblock_findval("path", rq->vars);
1097 return REQ_NOACTION;
1099 if (util_uri_is_evil(path)) {
1100 util_uri_parse(path);
1102 if (uafs_afsPathName(path) == NULL) {
1103 return REQ_NOACTION;
1106 realm = pblock_findval("path", pb);
1107 if (realm == NULL) {
1108 return REQ_NOACTION;
1110 if (util_uri_is_evil(realm)) {
1111 util_uri_parse(realm);
1114 pathLen = strlen(realm);
1115 if (strncmp(path, realm, pathLen) != 0 ||
1116 (path[pathLen] != '/' && path[pathLen] != '\0')) {
1117 return REQ_NOACTION;
1119 pblock_nvinsert("nsafs_realm", realm, rq->vars);
1124 * Check whether any path elements beneath the nolinks directory
1125 * are symbolic links. Return REQ_PROCEED if no links are found.
1127 int nsafs_check_for_links(
1143 * Check each component of the path below the nolinks directory.
1145 dirLen = strlen(nolinks);
1146 pathLen = strlen(path);
1147 if (pathLen < dirLen ||
1148 strncmp(path, nolinks, dirLen) != 0 ||
1149 (path[dirLen] != '/' && path[dirLen] != '\0')) {
1152 if (path[dirLen] == '/') {
1156 allocSize = pathLen+1;
1157 tmpPath = (char *)afs_osi_Alloc(allocSize);
1158 strcpy(tmpPath, path);
1159 while(tmpPath[pathLen] == '/' && pathLen > dirLen) {
1160 tmpPath[pathLen] = '\0';
1163 while (pathLen > dirLen) {
1164 rc = uafs_lstat(tmpPath, &st);
1167 afs_osi_Free(tmpPath, allocSize);
1168 return nsafs_error_check(code, NULL, pb, sn, rq);
1170 if ((st.st_mode & S_IFMT) == S_IFLNK) {
1171 afs_osi_Free(tmpPath, allocSize);
1172 return nsafs_error_check(ENOENT, NULL, pb, sn, rq);
1174 while(tmpPath[pathLen] != '/' && pathLen > dirLen) {
1175 tmpPath[pathLen] = '\0';
1178 while(tmpPath[pathLen] == '/' && pathLen > dirLen) {
1179 tmpPath[pathLen] = '\0';
1183 afs_osi_Free(tmpPath, allocSize);
1188 * Deny access to symbolic links in a directory or its descendents.
1190 NSAPI_PUBLIC int nsafs_nolinks(pblock *pb, Session *sn, Request *rq)
1200 path = pblock_findval("path", rq->vars);
1202 return REQ_NOACTION;
1204 nolinks = pblock_findval("nolinks", pb);
1205 if (nolinks == NULL) {
1206 return REQ_NOACTION;
1208 newPath = pblock_findval("newpath", rq->vars);
1211 * if the path is in AFS and is in the nolinks directory then deny access
1212 * to any symbolic links below the nolinks directory.
1214 if (util_uri_is_evil(path)) {
1215 util_uri_parse(path);
1217 if (uafs_afsPathName(path) == NULL) {
1218 return REQ_NOACTION;
1220 if (util_uri_is_evil(nolinks)) {
1221 util_uri_parse(nolinks);
1225 * Check for group numbers in the request vars, otherwise use the
1228 viceid = pblock_findval("nsafs_viceid", rq->vars);
1229 group0 = pblock_findval("nsafs_group0", rq->vars);
1230 group1 = pblock_findval("nsafs_group1", rq->vars);
1231 nsafs_set_id_from_strings(viceid, group0, group1);
1233 code = nsafs_check_for_links(path, nolinks, pb, sn, rq);
1234 if (code != REQ_PROCEED) {
1238 if (newPath != NULL) {
1239 if (util_uri_is_evil(newPath)) {
1240 util_uri_parse(newPath);
1242 if (uafs_afsPathName(newPath) == NULL) {
1243 return REQ_NOACTION;
1245 code = nsafs_check_for_links(newPath, nolinks, pb, sn, rq);
1246 if (code != REQ_PROCEED) {
1251 return REQ_NOACTION;
1255 * Set the MIME type for files in AFS.
1257 NSAPI_PUBLIC int nsafs_force_type(pblock *pb, Session *sn, Request *rq)
1262 path = pblock_findval("path", rq->vars);
1264 return REQ_NOACTION;
1266 dflt = pblock_findval("type", pb);
1268 return REQ_NOACTION;
1272 * If the path is in AFS then set the nsafs_variable in the
1273 * request variables. The nsafs_error_check routine only
1274 * sends an Unauthorized error if this variable is not set.
1276 if (util_uri_is_evil(path)) {
1277 util_uri_parse(path);
1279 if (uafs_afsPathName(path) == NULL) {
1280 return REQ_NOACTION;
1283 if (pblock_findval("content-type", rq->srvhdrs) == NULL) {
1284 pblock_nvinsert("content-type", dflt, rq->srvhdrs);
1291 * Disable the Unauthorized response message so users never get
1292 * prompted for their name and password.
1294 NSAPI_PUBLIC int nsafs_nocheck(pblock *pb, Session *sn, Request *rq)
1298 path = pblock_findval("path", rq->vars);
1300 return REQ_NOACTION;
1304 * If the path is in AFS then set the nsafs_variable in the
1305 * request variables. The nsafs_error_check routine only
1306 * sends an Unauthorized error if this variable is not set.
1308 if (util_uri_is_evil(path)) {
1309 util_uri_parse(path);
1311 if (uafs_afsPathName(path) == NULL) {
1312 return REQ_NOACTION;
1315 if (pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
1316 pblock_nvinsert("nsafs_nocheck", "TRUE", rq->vars);
1320 * If this is a public directory then proceed, otherwise access
1323 if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1327 return nsafs_error_check(EACCES, NULL, pb, sn, rq);
1331 * Require all requests for AFS files that are not explicitly made
1332 * public to be authenticated.
1334 NSAPI_PUBLIC int nsafs_check(pblock *pb, Session *sn, Request *rq)
1338 path = pblock_findval("path", rq->vars);
1340 return REQ_NOACTION;
1344 * If the path is in AFS then require authentication
1346 if (util_uri_is_evil(path)) {
1347 util_uri_parse(path);
1349 if (uafs_afsPathName(path) == NULL) {
1350 return REQ_NOACTION;
1354 * If this is a public directory then proceed
1356 if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1360 if (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
1361 pblock_findval("nsafs_group0", rq->vars) == NULL ||
1362 pblock_findval("nsafs_group1", rq->vars) == NULL) {
1363 return nsafs_error_check(EPERM, NULL, pb, sn, rq);
1370 * Find index files for directories
1372 NSAPI_PUBLIC int nsafs_find_index(pblock *pb, Session *sn, Request *rq)
1386 path = pblock_findval("path", rq->vars);
1388 return REQ_NOACTION;
1391 indexNames = pblock_findval("index-names", pb);
1392 if (indexNames == NULL) {
1393 return REQ_NOACTION;
1397 * Skip pathnames that don't end in a slash
1399 if (*path == '\0' || path[strlen(path)-1] != '/') {
1400 return REQ_NOACTION;
1404 * If the path is a directory then check if the directory has
1407 if (util_uri_is_evil(path)) {
1408 util_uri_parse(path);
1410 if (uafs_afsPathName(path) == NULL) {
1412 * Look for index files in local file system
1414 rc = stat(path, &st);
1417 return nsafs_error_check(code, NULL, pb, sn, rq);
1420 if ((st.st_mode & S_IFMT) != S_IFDIR) {
1421 return REQ_NOACTION;
1424 indexP = nsafs_NameFromNames(NULL, indexNames, &pos);
1425 while (indexP != NULL) {
1426 nameP = afs_osi_Alloc(strlen(path)+strlen(indexP)+2);
1427 sprintf(nameP, "%s/%s", path, indexP);
1428 rc = stat(nameP, &st);
1429 if (rc == 0 && (st.st_mode & S_IFMT) != S_IFDIR) {
1430 param_free(pblock_remove("path", rq->vars));
1431 pblock_nvinsert("path", nameP, rq->vars);
1432 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1433 afs_osi_Free(indexP, strlen(indexP)+1);
1436 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1437 indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1441 * Check for group numbers in the request vars, otherwise use the
1444 viceid = pblock_findval("nsafs_viceid", rq->vars);
1445 group0 = pblock_findval("nsafs_group0", rq->vars);
1446 group1 = pblock_findval("nsafs_group1", rq->vars);
1447 nsafs_set_id_from_strings(viceid, group0, group1);
1450 * Look for index files in AFS
1452 rc = uafs_stat(path, &st);
1455 return nsafs_error_check(code, NULL, pb, sn, rq);
1458 if ((st.st_mode & S_IFMT) != S_IFDIR) {
1459 return REQ_NOACTION;
1462 indexP = nsafs_NameFromNames(NULL, indexNames, &pos);
1463 while (indexP != NULL) {
1464 nameP = afs_osi_Alloc(strlen(path)+strlen(indexP)+2);
1465 sprintf(nameP, "%s/%s", path, indexP);
1466 rc = uafs_stat(nameP, &st);
1467 if (rc == 0 && (st.st_mode & S_IFMT) != S_IFDIR) {
1468 param_free(pblock_remove("path", rq->vars));
1469 pblock_nvinsert("path", nameP, rq->vars);
1470 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1471 afs_osi_Free(indexP, strlen(indexP)+1);
1474 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1475 indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1478 return REQ_NOACTION;
1482 * Node in binary tree used to sort directory entries
1485 char *name; /* directory member name */
1486 char *text; /* directory index text */
1487 int allocLen; /* Size of this allocated block */
1488 int textLen; /* Size of the text string */
1489 int balance; /* used to balance binary trees */
1490 struct nsafs_tree *left;
1491 struct nsafs_tree *right;
1494 #ifdef NSAFS_TREE_DEBUG
1496 * Validate that the given tree is a valid balanced binary tree
1498 int nsafs_tree_check(struct nsafs_tree *root)
1504 if (root->left == NULL) {
1507 assert(strcmp(root->left->name, root->name) < 0);
1508 leftDepth = nsafs_tree_check(root->left);
1510 if (root->right == NULL) {
1513 assert(strcmp(root->right->name, root->name) >= 0);
1514 rightDepth = nsafs_tree_check(root->right);
1516 balance = rightDepth - leftDepth;
1517 assert(balance == root->balance);
1518 assert(balance >= -1);
1519 assert(balance <= 1);
1520 return (MAX(leftDepth, rightDepth)+1);
1522 #endif /* NSAFS_TREE_DEBUG */
1525 * Insert a node into the balanced binary tree of directory entries.
1526 * rootP is the address of the parent's pointer to this node.
1527 * Returns the change in depth of this tree (0 or 1)
1529 int nsafs_node_insert(
1530 struct nsafs_tree *newNode,
1531 struct nsafs_tree **rootP)
1533 struct nsafs_tree *thisNode;
1537 if (strcmp(newNode->name, thisNode->name) < 0) {
1541 if (thisNode->left == NULL) {
1542 thisNode->left = newNode;
1543 if (thisNode->right == NULL) {
1544 thisNode->balance = -1;
1547 thisNode->balance = 0;
1551 delta = nsafs_node_insert(newNode, &thisNode->left);
1555 thisNode->balance -= delta;
1556 if (thisNode->balance == -2) {
1560 if (thisNode->left->balance > 0) {
1561 #ifdef NSAFS_TREE_DEBUG
1562 printf("rotate left, line %d\n", __LINE__);
1563 #endif /* NSAFS_TREE_DEBUG */
1564 *rootP = thisNode->left->right;
1565 if ((*rootP)->balance > 0) {
1566 thisNode->left->balance = -(*rootP)->balance;
1567 (*rootP)->balance = 0;
1568 thisNode->balance = 0;
1570 thisNode->left->balance = 0;
1571 thisNode->balance = -(*rootP)->balance;
1572 (*rootP)->balance = 0;
1574 thisNode->left->right = (*rootP)->left;
1575 (*rootP)->left = thisNode->left;
1576 thisNode->left = (*rootP)->right;
1577 (*rootP)->right = thisNode;
1579 #ifdef NSAFS_TREE_DEBUG
1580 printf("rotate left, line %d\n", __LINE__);
1581 #endif /* NSAFS_TREE_DEBUG */
1582 *rootP = thisNode->left;
1583 (*rootP)->balance = 0;
1584 thisNode->balance = 0;
1585 thisNode->left = (*rootP)->right;
1586 (*rootP)->right = thisNode;
1589 } else if (thisNode->balance != 0) {
1599 if (thisNode->right == NULL) {
1600 thisNode->right = newNode;
1601 if (thisNode->left == NULL) {
1602 thisNode->balance = 1;
1605 thisNode->balance = 0;
1609 delta = nsafs_node_insert(newNode, &thisNode->right);
1613 thisNode->balance += delta;
1614 if (thisNode->balance == 2) {
1618 if (thisNode->right->balance < 0) {
1619 #ifdef NSAFS_TREE_DEBUG
1620 printf("rotate right, line %d\n", __LINE__);
1621 #endif /* NSAFS_TREE_DEBUG */
1622 *rootP = thisNode->right->left;
1623 if ((*rootP)->balance < 0) {
1624 thisNode->right->balance = -(*rootP)->balance;
1625 (*rootP)->balance = 0;
1626 thisNode->balance = 0;
1628 thisNode->right->balance = 0;
1629 thisNode->balance = -(*rootP)->balance;
1630 (*rootP)->balance = 0;
1632 thisNode->right->left = (*rootP)->right;
1633 (*rootP)->right = thisNode->right;
1634 thisNode->right = (*rootP)->left;
1635 (*rootP)->left = thisNode;
1637 #ifdef NSAFS_TREE_DEBUG
1638 printf("rotate right, line %d\n", __LINE__);
1639 #endif /* NSAFS_TREE_DEBUG */
1640 *rootP = thisNode->right;
1641 (*rootP)->balance = 0;
1642 thisNode->balance = 0;
1643 thisNode->right = (*rootP)->left;
1644 (*rootP)->left = thisNode;
1647 } else if (thisNode->balance != 0) {
1657 * Allocate storage for a new directory entry, copy in the name and
1658 * text, and insert the entry into the balanced binary tree.
1660 void nsafs_tree_insert(
1663 struct nsafs_tree **rootP)
1668 struct nsafs_tree *newNode;
1671 * allocate storage, initialize the fields, and copy in the data
1673 nameLen = strlen(name);
1674 textLen = strlen(text);
1675 allocLen = sizeof(struct nsafs_tree) + nameLen + textLen + 2;
1676 newNode = (struct nsafs_tree *)afs_osi_Alloc(allocLen);
1677 usr_assert(newNode != NULL);
1678 newNode->name = (char *)(newNode+1);
1679 newNode->text = newNode->name + nameLen + 1;
1680 newNode->textLen = textLen;
1681 newNode->allocLen = allocLen;
1682 newNode->balance = 0;
1683 newNode->left = NULL;
1684 newNode->right = NULL;
1685 strcpy(newNode->name, name);
1686 strcpy(newNode->text, text);
1689 * If this is the first node, insert it here, otherwise call
1690 * nsafs_node_insert to insert the node into the balanced
1693 if (*rootP == NULL) {
1696 nsafs_node_insert(newNode, rootP);
1698 #ifdef NSAFS_TREE_DEBUG
1699 nsafs_tree_check(*rootP);
1700 #endif /* NSAFS_TREE_DEBUG */
1704 * Transmit the contents of the tree
1706 int nsafs_tree_send(
1708 struct nsafs_tree *root,
1714 struct nsafs_tree *node;
1720 * Recurse left, iterate right
1723 while (node != NULL) {
1724 if (node->left != NULL) {
1725 code = nsafs_tree_send(sd, node->left, outbuf, buflen, bufsize);
1726 if (code == IO_ERROR) {
1730 txtLen = node->textLen;
1731 txtBuf = node->text;
1732 while (txtLen > 0) {
1733 if (*buflen == bufsize) {
1734 code = net_write(sd, outbuf, bufsize);
1735 if (code == IO_ERROR) {
1740 len = MIN(txtLen, bufsize - *buflen);
1741 memcpy((void *)(outbuf + *buflen), (void *)txtBuf, len);
1752 * Free the binary tree and all data within
1754 void nsafs_tree_free(
1755 struct nsafs_tree *root)
1757 struct nsafs_tree *node, *next;
1760 * Iterate left, recurse right
1763 while (node != NULL) {
1764 if (node->right != NULL) {
1765 nsafs_tree_free(node->right);
1768 afs_osi_Free(node, sizeof(struct nsafs_tree) + strlen(node->name) +
1769 strlen(node->text) + 2);
1775 * Send the contents of an AFS directory, Simple directory format
1777 int nsafs_send_directory(
1788 struct usr_dirent *enp;
1792 char *htmlHdr = "<HTML>\r\n<BODY>\r\n"
1793 "<TITLE>Index of %s</TITLE>\r\n"
1794 "<h1>Index of %s</h1>\r\n"
1796 char *htmlTrl = "</PRE></BODY></HTML>\r\n";
1797 struct nsafs_tree *root;
1800 * Set the content type to "text/html"
1802 param_free(pblock_remove("content-type", rq->srvhdrs));
1803 pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
1806 * Build a binary tree of directory entries, and calculate the
1807 * length of our response message
1809 dirp = uafs_opendir(path);
1812 return nsafs_error_check(code, NULL, pb, sn, rq);
1816 dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1817 while ((enp = uafs_readdir(dirp)) != NULL) {
1818 if (strcmp(enp->d_name, ".") == 0) {
1820 } else if (strcmp(enp->d_name, "..") == 0) {
1821 filename = "Parent Directory";
1823 filename = enp->d_name;
1826 "<A HREF=\"%s%s\" NAME=\"%s\"> %s</A>\r\n",
1827 path, enp->d_name, filename, filename);
1828 contentLength += strlen(dirbuf);
1829 nsafs_tree_insert(enp->d_name, dirbuf, &root);
1833 uafs_closedir(dirp);
1834 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1835 nsafs_tree_free(root);
1836 return nsafs_error_check(code, NULL, pb, sn, rq);
1838 rc = uafs_closedir(dirp);
1841 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1842 nsafs_tree_free(root);
1843 return nsafs_error_check(code, NULL, pb, sn, rq);
1847 * Calculate the length of the HTML headers and trailers,
1848 * set the content-length field in the reply header, and
1849 * start the reply message
1851 sprintf(dirbuf, htmlHdr, path, path);
1852 contentLength += strlen(dirbuf) + strlen(htmlTrl);
1853 stp->st_size = contentLength;
1854 code = nsafs_set_finfo(sn, rq, stp);
1855 if (code != REQ_PROCEED) {
1856 nsafs_tree_free(root);
1857 protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL);
1860 protocol_status(sn, rq, PROTOCOL_OK, NULL);
1861 code = protocol_start_response(sn, rq);
1862 if (code != REQ_PROCEED) {
1863 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1864 nsafs_tree_free(root);
1869 * Send the HTML header, file data and HTML trailer
1871 if (net_write(sn->csd, dirbuf, strlen(dirbuf)) == IO_ERROR) {
1872 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1873 nsafs_tree_free(root);
1874 log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1878 code = nsafs_tree_send(sn->csd, root, dirbuf, &buflen, NSAFS_BUFFER_SIZE);
1879 if (code == IO_ERROR) {
1880 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1881 nsafs_tree_free(root);
1882 log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1885 nsafs_tree_free(root);
1887 code = net_write(sn->csd, dirbuf, buflen);
1888 if (code == IO_ERROR) {
1889 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1890 log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1894 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1895 if (net_write(sn->csd, htmlTrl, strlen(htmlTrl)) == IO_ERROR) {
1903 * Send the contents of an AFS file
1905 int nsafs_send_file(
1919 * Make sure we can open the file before we send the response header
1921 fd = uafs_open(path, O_RDONLY, 0);
1924 return nsafs_error_check(code, NULL, pb, sn, rq);
1928 * Add a content-length field to the response header and
1929 * begin the response message. We have already checked the
1930 * preconditions, so a return code other than REQ_PROCEED
1931 * means that this is a HEAD command.
1933 code = nsafs_set_finfo(sn, rq, stp);
1934 if (code != REQ_PROCEED) {
1937 protocol_status(sn, rq, PROTOCOL_OK, NULL);
1938 code = protocol_start_response(sn, rq);
1939 if (code != REQ_PROCEED) {
1944 * Send the file contents
1946 filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1947 while ((rc = uafs_read(fd, filebuf, NSAFS_BUFFER_SIZE)) > 0) {
1948 if (net_write(sn->csd, filebuf, rc) == IO_ERROR) {
1949 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1951 log_error(LOG_INFORM, "nsafs", sn, rq, "send_file IO_ERROR");
1958 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1959 log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_read, err=%d", code);
1962 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1963 rc = uafs_close(fd);
1966 log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_close, err=%d", code);
1972 * Service function for AFS files and directories
1974 NSAPI_PUBLIC int nsafs_send(pblock *pb, Session *sn, Request *rq)
1987 if (nsafs_init_done == 0) {
1992 * Only service paths that are in AFS
1994 path = pblock_findval("path", rq->vars);
1996 return REQ_NOACTION;
1998 if (uafs_afsPathName(path) == NULL) {
1999 return REQ_NOACTION;
2003 * We do not support content-range headers
2005 request_header("content-range", &rangeHdr, sn, rq);
2006 if (rangeHdr != NULL) {
2007 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
2008 log_error(LOG_INFORM, "nsafs", sn, rq,
2009 "content-range is not implemented");
2013 * Check for group numbers in the request vars, otherwise use the
2016 viceid = pblock_findval("nsafs_viceid", rq->vars);
2017 group0 = pblock_findval("nsafs_group0", rq->vars);
2018 group1 = pblock_findval("nsafs_group1", rq->vars);
2019 nsafs_set_id_from_strings(viceid, group0, group1);
2022 * Get the file attributes
2024 rc = uafs_stat(path, &st);
2027 return nsafs_error_check(code, NULL, pb, sn, rq);
2031 * Check the request preconditions
2033 code = nsafs_check_preconditions(&st, pb, sn, rq);
2034 if (code != REQ_PROCEED) {
2039 * Send the contents of files, and a formatted index of directories
2041 if ((st.st_mode & S_IFMT) == S_IFDIR) {
2043 dirPath = afs_osi_Alloc(len+2);
2044 strcpy(dirPath, path);
2045 if (dirPath[len-1] != '/') {
2047 dirPath[len+1] = '\0';
2049 if (util_uri_is_evil(dirPath)) {
2050 util_uri_parse(dirPath);
2052 code = nsafs_send_directory(dirPath, &st, pb, sn, rq);
2053 afs_osi_Free(dirPath, len+2);
2055 code = nsafs_send_file(path, &st, pb, sn, rq);
2061 * Service function to create new AFS files
2063 NSAPI_PUBLIC int nsafs_put(pblock *pb, Session *sn, Request *rq)
2083 if (nsafs_init_done == 0) {
2088 * Only service paths that are in AFS
2090 path = pblock_findval("path", rq->vars);
2092 return REQ_NOACTION;
2094 if (uafs_afsPathName(path) == NULL) {
2095 return REQ_NOACTION;
2099 * We do not support content-range headers
2101 request_header("content-range", &rangeHdr, sn, rq);
2102 if (rangeHdr != NULL) {
2103 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
2104 log_error(LOG_INFORM, "nsafs", sn, rq,
2105 "content-range is not implemented");
2109 * Check for group numbers in the request vars, otherwise use the
2112 viceid = pblock_findval("nsafs_viceid", rq->vars);
2113 group0 = pblock_findval("nsafs_group0", rq->vars);
2114 group1 = pblock_findval("nsafs_group1", rq->vars);
2115 nsafs_set_id_from_strings(viceid, group0, group1);
2118 * Check for a content length header
2120 request_header("content-length", &lengthHdr, sn, rq);
2121 if (lengthHdr != NULL) {
2122 contentLength = atoi(lengthHdr);
2128 * Get the file attributes
2130 rc = uafs_stat(path, &st);
2133 if (code == ENOENT) {
2134 rspStatus = PROTOCOL_CREATED;
2136 return nsafs_error_check(code, NULL, pb, sn, rq);
2138 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2139 return nsafs_error_check(EISDIR, NULL, pb, sn, rq);
2141 rspStatus = PROTOCOL_OK;
2144 fd = uafs_open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
2147 return nsafs_error_check(code, NULL, pb, sn, rq);
2151 * Get the file contents
2153 filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2156 if (contentLength < 0) {
2157 bytesToRead = NSAFS_BUFFER_SIZE;
2159 bytesToRead = MIN(contentLength, NSAFS_BUFFER_SIZE);
2160 if (bytesToRead == 0) {
2164 for (bytesRead = 0 ; !eof && bytesRead < bytesToRead ; bytesRead++) {
2165 rc = netbuf_getc(sn->inbuf);
2168 } else if (rc == IO_ERROR) {
2170 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2172 return nsafs_error_check(EIO, NULL, pb, sn, rq);
2174 filebuf[bytesRead] = rc;
2177 if (bytesRead > 0) {
2178 if (contentLength > 0) {
2179 contentLength -= bytesRead;
2181 rc = uafs_write(fd, filebuf, bytesRead);
2184 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2186 return nsafs_error_check(code, NULL, pb, sn, rq);
2190 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2191 rc = uafs_close(fd);
2194 return nsafs_error_check(code, NULL, pb, sn, rq);
2196 if (contentLength > 0) {
2197 log_error(LOG_FAILURE, "nsafs", sn, rq, "received partial contents");
2201 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2202 protocol_status(sn, rq, rspStatus, NULL);
2203 code = protocol_start_response(sn, rq);
2208 * Service function to delete AFS files
2210 NSAPI_PUBLIC int nsafs_delete(pblock *pb, Session *sn, Request *rq)
2220 if (nsafs_init_done == 0) {
2225 * Only service paths that are in AFS
2227 path = pblock_findval("path", rq->vars);
2229 return REQ_NOACTION;
2231 if (uafs_afsPathName(path) == NULL) {
2232 return REQ_NOACTION;
2236 * Check for group numbers in the request vars, otherwise use the
2239 viceid = pblock_findval("nsafs_viceid", rq->vars);
2240 group0 = pblock_findval("nsafs_group0", rq->vars);
2241 group1 = pblock_findval("nsafs_group1", rq->vars);
2242 nsafs_set_id_from_strings(viceid, group0, group1);
2245 * Get the file attributes
2247 rc = uafs_lstat(path, &st);
2250 return nsafs_error_check(code, NULL, pb, sn, rq);
2251 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2252 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot DELETE directories");
2253 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2254 pblock_nvinsert("Allow", NSAFS_DIR_ALLOW, rq->srvhdrs);
2258 rc = uafs_unlink(path);
2261 return nsafs_error_check(code, NULL, pb, sn, rq);
2264 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2265 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2266 code = protocol_start_response(sn, rq);
2271 * Service function to create AFS directories
2273 NSAPI_PUBLIC int nsafs_mkdir(pblock *pb, Session *sn, Request *rq)
2283 if (nsafs_init_done == 0) {
2288 * Only service paths that are in AFS
2290 path = pblock_findval("path", rq->vars);
2292 return REQ_NOACTION;
2294 if (uafs_afsPathName(path) == NULL) {
2295 return REQ_NOACTION;
2299 * Check for group numbers in the request vars, otherwise use the
2302 viceid = pblock_findval("nsafs_viceid", rq->vars);
2303 group0 = pblock_findval("nsafs_group0", rq->vars);
2304 group1 = pblock_findval("nsafs_group1", rq->vars);
2305 nsafs_set_id_from_strings(viceid, group0, group1);
2308 * Create the directory
2310 rc = uafs_mkdir(path, 0755);
2313 return nsafs_error_check(code, NULL, pb, sn, rq);
2316 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2317 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2318 code = protocol_start_response(sn, rq);
2323 * Service function to delete AFS directories
2325 NSAPI_PUBLIC int nsafs_rmdir(pblock *pb, Session *sn, Request *rq)
2335 if (nsafs_init_done == 0) {
2340 * Only service paths that are in AFS
2342 path = pblock_findval("path", rq->vars);
2344 return REQ_NOACTION;
2346 if (uafs_afsPathName(path) == NULL) {
2347 return REQ_NOACTION;
2351 * Check for group numbers in the request vars, otherwise use the
2354 viceid = pblock_findval("nsafs_viceid", rq->vars);
2355 group0 = pblock_findval("nsafs_group0", rq->vars);
2356 group1 = pblock_findval("nsafs_group1", rq->vars);
2357 nsafs_set_id_from_strings(viceid, group0, group1);
2360 * Get the file attributes, rmdir only work on directories.
2362 rc = uafs_lstat(path, &st);
2365 return nsafs_error_check(code, NULL, pb, sn, rq);
2369 * Call unlink to delete symbolic links to directories, and rmdir
2370 * to to delete directories.
2372 if ((st.st_mode & S_IFMT) == S_IFDIR) {
2373 rc = uafs_rmdir(path);
2374 } else if ((st.st_mode & S_IFMT) == S_IFLNK) {
2375 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot RMDIR links");
2376 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2377 pblock_nvinsert("Allow", NSAFS_LINK_ALLOW, rq->srvhdrs);
2379 } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
2380 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot RMDIR files");
2381 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2382 pblock_nvinsert("Allow", NSAFS_FILE_ALLOW, rq->srvhdrs);
2387 return nsafs_error_check(code, NULL, pb, sn, rq);
2390 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2391 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2392 code = protocol_start_response(sn, rq);
2397 * Service function to rename AFS files and directories
2399 NSAPI_PUBLIC int nsafs_move(pblock *pb, Session *sn, Request *rq)
2410 if (nsafs_init_done == 0) {
2415 * Only service paths that are in AFS
2417 path = pblock_findval("path", rq->vars);
2419 return REQ_NOACTION;
2421 if (uafs_afsPathName(path) == NULL) {
2422 return REQ_NOACTION;
2424 newPath = pblock_findval("newpath", rq->vars);
2425 if (newPath == NULL) {
2426 return REQ_NOACTION;
2428 if (uafs_afsPathName(newPath) == NULL) {
2429 return REQ_NOACTION;
2433 * Check for group numbers in the request vars, otherwise use the
2436 viceid = pblock_findval("nsafs_viceid", rq->vars);
2437 group0 = pblock_findval("nsafs_group0", rq->vars);
2438 group1 = pblock_findval("nsafs_group1", rq->vars);
2439 nsafs_set_id_from_strings(viceid, group0, group1);
2444 rc = uafs_rename(path, newPath);
2447 return nsafs_error_check(code, NULL, pb, sn, rq);
2450 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2451 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2452 code = protocol_start_response(sn, rq);
2457 * Send the index of an AFS directory
2472 struct usr_dirent *enp;
2477 struct nsafs_tree *root;
2479 if (nsafs_init_done == 0) {
2484 * Only service paths that are in AFS
2486 path = pblock_findval("path", rq->vars);
2488 return REQ_NOACTION;
2490 if (uafs_afsPathName(path) == NULL) {
2491 return REQ_NOACTION;
2495 * Check for group numbers in the request vars, otherwise use the
2498 viceid = pblock_findval("nsafs_viceid", rq->vars);
2499 group0 = pblock_findval("nsafs_group0", rq->vars);
2500 group1 = pblock_findval("nsafs_group1", rq->vars);
2501 nsafs_set_id_from_strings(viceid, group0, group1);
2504 * Get the file attributes, index does not work on symbolic links.
2506 rc = uafs_lstat(path, &st);
2509 return nsafs_error_check(code, NULL, pb, sn, rq);
2511 if ((st.st_mode & S_IFMT) == S_IFLNK) {
2512 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot INDEX links");
2513 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2514 pblock_nvinsert("Allow", NSAFS_LINK_ALLOW, rq->srvhdrs);
2516 } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
2517 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot INDEX files");
2518 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2519 pblock_nvinsert("Allow", NSAFS_FILE_ALLOW, rq->srvhdrs);
2524 * Set the content type to "text/html"
2526 param_free(pblock_remove("content-type", rq->srvhdrs));
2527 pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
2530 * Build a binary tree of directory entries, and calculate the
2531 * length of our response message
2533 dirp = uafs_opendir(path);
2536 return nsafs_error_check(code, NULL, pb, sn, rq);
2538 dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2539 tmpPath = afs_osi_Alloc(NSAFS_MAX_PATH);
2542 while ((enp = uafs_readdir(dirp)) != NULL) {
2543 sprintf(tmpPath, "%s/%s", path, enp->d_name);
2544 rc = uafs_lstat(tmpPath, &st);
2547 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2548 sprintf(dirbuf, "%s directory %u %u\r\n",
2549 enp->d_name, st.st_size, st.st_mtime);
2550 } else if ((st.st_mode & S_IFMT) == S_IFLNK) {
2551 sprintf(dirbuf, "%s link %u %u\r\n",
2552 enp->d_name, st.st_size, st.st_mtime);
2554 sprintf(dirbuf, "%s unknown %u %u\r\n",
2555 enp->d_name, st.st_size, st.st_mtime);
2557 contentLength += strlen(dirbuf);
2558 nsafs_tree_insert(enp->d_name, dirbuf, &root);
2562 uafs_closedir(dirp);
2563 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2564 afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2565 nsafs_tree_free(root);
2566 return nsafs_error_check(code, NULL, pb, sn, rq);
2568 afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2569 rc = uafs_closedir(dirp);
2572 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2573 nsafs_tree_free(root);
2574 return nsafs_error_check(code, NULL, pb, sn, rq);
2578 * Set the content-length field in the reply header, and
2579 * start the reply message
2581 pblock_nninsert("Content-Length", contentLength, rq->srvhdrs);
2582 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2583 code = protocol_start_response(sn, rq);
2584 if (code != REQ_PROCEED) {
2585 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2586 nsafs_tree_free(root);
2594 code = nsafs_tree_send(sn->csd, root, dirbuf, &buflen, NSAFS_BUFFER_SIZE);
2595 if (code == IO_ERROR) {
2596 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2597 nsafs_tree_free(root);
2598 log_error(LOG_INFORM, "nsafs", sn, rq, "send_index IO_ERROR");
2601 nsafs_tree_free(root);
2603 code = net_write(sn->csd, dirbuf, buflen);
2604 if (code == IO_ERROR) {
2605 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2606 log_error(LOG_INFORM, "nsafs", sn, rq, "send_index IO_ERROR");
2610 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);