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 "../afs/param.h" /* Should be always first */
15 #include "../afs/sysincludes.h" /* Standard vendor system headers */
17 #include "../afs/afsincludes.h" /* Afs-based standard headers */
18 #include "../afs/afs_stats.h"
19 #include "../afs/afs_usrops.h"
20 #include "../afs/auth.h"
21 #include "../afs/cellconfig.h"
22 #include "../afs/vice.h"
23 #include "../afs/kautils.h"
24 #include "../afs/nsafs.h"
26 #define NSAFS_DFLT_RCVTHREADS 2 /* Dflt number recevice threads */
27 #define NSAFS_BUFFER_SIZE 4096 /* Send/Receive buffer size */
28 #define NSAFS_MAX_PATH 1024 /* Maximum path length */
29 #define NSAFS_USERNAME_MAX 64 /* Maximum username length */
30 #define NSAFS_PASSWORD_MAX 64 /* Maximum password length */
31 #define NSAFS_LOGIN_HASH_SIZE 1024 /* MUST be power of two */
32 #define TEN_MINUTES 600 /* 10 minutes = 600 seconds */
34 #define NSAFS_DIR_ALLOW "GET,HEAD,MOVE,INDEX,RMDIR"
35 #define NSAFS_LINK_ALLOW "GET,HEAD,MOVE,DELETE"
36 #define NSAFS_FILE_ALLOW "GET,HEAD,MOVE,PUT,DELETE"
39 #define MAX(A,B) ((A)>(B)?(A):(B))
42 #define MIN(A,B) ((A)<(B)?(A):(B))
46 * Used by KA module to get local cell info
48 struct afsconf_dir *KA_conf;
51 * Initialization parameters. The plugin is initialized in
52 * the Netscape parent process, but we don't initialize AFS
53 * until we are in the child process.
55 CRITICAL nsafs_init_lock;
61 char *cacheBaseDirParam;
64 int cacheStatEntriesParam;
70 long maxExpirationParam;
73 * Structure user to store entries in AFS login cache
76 afs_uint32 expiration;
80 char username[NSAFS_USERNAME_MAX];
81 char cellname[NSAFS_USERNAME_MAX];
82 char cksum[SHA_HASH_BYTES];
83 struct nsafs_login *next;
84 struct nsafs_login *prev;
88 * Value used to initialize SHA checksums on username/password pairs
90 afs_uint32 nsafs_login_pad[SHA_HASH_INTS] = {
91 0x0D544971, 0x2281AC5B, 0x58B51218, 0x4085E08D, 0xB68C484B
97 CRITICAL nsafs_login_lock;
99 struct nsafs_login *head;
100 struct nsafs_login *tail;
101 } nsafs_login_cache[NSAFS_LOGIN_HASH_SIZE];
104 * Mapping from characters to 64 bit values
106 static int base64_to_value[256] = {
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f,
113 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
114 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
116 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
117 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
118 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
120 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
121 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
122 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
142 * Decode a base64 encoded buffer in place
144 void nsafs_decode64(char *buf)
154 * Allow trailing blanks
156 for (len = strlen(buf) ; buf[len-1] == ' ' && len > 0 ; len--);
159 * Valid encodings are multiples of four characters
166 for (i = 0, j = 0 ; i < len ; i += 4, j += 3) {
167 val1 = base64_to_value[buf[i]];
168 val2 = base64_to_value[buf[i+1]];
169 val3 = base64_to_value[buf[i+2]];
170 val4 = base64_to_value[buf[i+3]];
171 buf[j] = ((val1<<2)&0xfc) | ((val2>>4)&0x3);
172 buf[j+1] = ((val2<<4)&0xf0) | ((val3>>2)&0xf);
173 buf[j+2] = ((val3<<6)&0xc0) | (val4&0x3f);
180 * Interface for pioctls - used for unlogging
182 #include "../afs/venus.h"
183 int do_pioctl(char *in_buffer, int in_size,
184 char *out_buffer, int out_size,
185 int opcode, char *path, int followSymLinks)
187 struct ViceIoctl iob;
189 iob.in_size = in_size;
190 iob.out = out_buffer;
191 iob.out_size = out_size;
193 #ifdef AFS_USR_SUN5_ENV
194 return syscall(AFS_SYSCALL, AFSCALL_PIOCTL, path, _VICEIOCTL(opcode), &iob,
196 #else /* AFS_USR_SUN5_ENV */
197 return lpioctl(path, _VICEIOCTL(opcode), &iob, followSymLinks);
198 #endif /* AFS_USR_SUN5_ENV */
202 * unlog - invalidate any existing AFS tokens with the kernel cache
203 * manager. In case the server is started up with tokens
207 return do_pioctl(NULL, 0, NULL, 0, VIOCUNPAG, NULL, 0);
211 * Initialize the AFS client and the login cache
213 void nsafs_init_once()
216 crit_enter(nsafs_init_lock);
217 if (nsafs_init_done == 0) {
220 /* printf("unlog from AFS failed: errno:%d\n", errno); */
222 uafs_Init("nsafs-init", mountDirParam, confDirParam,
223 cacheBaseDirParam, cacheBlocksParam, cacheFilesParam,
224 cacheStatEntriesParam, dCacheSizeParam, vCacheSizeParam,
225 chunkSizeParam, 0, debugParam, nDaemonsParam, -1,
227 nsafs_login_lock = crit_init();
228 for (i = 0 ; i < NSAFS_LOGIN_HASH_SIZE ; i++) {
229 DLL_INIT_LIST(nsafs_login_cache[i].head,
230 nsafs_login_cache[i].tail);
235 crit_exit(nsafs_init_lock);
239 * Hash function for the AFS login cache
241 int nsafs_login_hash(char *name, char *cell)
245 for (val = *name , p = name ; *p != '\0' ; p++) {
246 val = (val << 2) ^ val ^ (afs_uint32)(*p);
248 for (p = cell ; *p != '\0' ; p++) {
249 val = (val << 2) ^ val ^ (afs_uint32)(*p);
251 return val & (NSAFS_LOGIN_HASH_SIZE-1);
255 * Compute a SHA checksum on the username, cellname, and password
257 void nsafs_login_checksum(
270 * Compute SHA(username,SHA(password,pad))
272 passwdLen = strlen(passwd);
273 userLen = strlen(user);
274 cellLen = strlen(cell);
275 shaBuffer = afs_osi_Alloc(MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
276 strcpy(shaBuffer, passwd);
277 memcpy((void *)(shaBuffer + passwdLen), (void *)(&nsafs_login_pad[0]),
280 sha_hash(&state, shaBuffer, passwdLen+SHA_HASH_BYTES);
281 memcpy(shaBuffer, user, userLen);
282 memcpy(shaBuffer+userLen, cell, cellLen);
283 sha_bytes(&state, shaBuffer+userLen+cellLen);
285 sha_hash(&state, shaBuffer, userLen+cellLen+SHA_HASH_BYTES);
286 sha_bytes(&state, &cksum[0]);
287 memset(shaBuffer, 0, MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
288 afs_osi_Free(shaBuffer, MAX(userLen+cellLen,passwdLen)+SHA_HASH_BYTES);
292 * Set the AFS identity given from the group0 and group1 strings
294 void nsafs_set_id_from_ints(int viceid, int group0, int group1)
297 struct usr_ucred *crp;
301 crp->cr_uid = viceid;
302 crp->cr_ruid = viceid;
303 crp->cr_suid = viceid;
304 crp->cr_groups[0] = group0;
305 crp->cr_groups[1] = group1;
306 crp->cr_groups[2] = getgid();
308 for (i = 3 ; i < NGROUPS ; i++) {
309 crp->cr_groups[i] = NOGROUP;
314 * Set the AFS identity given from the viceid, group0 and group1 strings
316 void nsafs_set_id_from_strings(char *viceid, char *group0, char *group1)
319 struct usr_ucred *crp;
321 if (viceid != NULL && group0 != NULL && group1 != NULL) {
322 nsafs_set_id_from_ints(atoi(viceid), atoi(group0), atoi(group1));
324 u.u_viceid = getuid();
326 crp->cr_uid = getuid();
327 crp->cr_ruid = getuid();
328 crp->cr_suid = getuid();
329 crp->cr_groups[0] = getgid();
331 for (i = 1 ; i < NGROUPS ; i++) {
332 crp->cr_groups[i] = NOGROUP;
338 * Look up a login ID in the cache. If an entry name is found for the
339 * given username, and the SHA checksums match, then set the group0
340 * and group1 parameters and return 1, otherwise return 0.
342 int nsafs_login_lookup(
352 struct nsafs_login *loginP, *tmpP, loginTmp;
355 * Search the hash chain for a matching entry, free
356 * expired entries as we search
358 index = nsafs_login_hash(user, cell);
359 curTime = time(NULL);
360 crit_enter(nsafs_login_lock);
361 loginP = nsafs_login_cache[index].head;
362 while (loginP != NULL) {
363 if (loginP->expiration < curTime) {
366 nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
368 DLL_DELETE(tmpP, nsafs_login_cache[index].head,
369 nsafs_login_cache[index].tail, next, prev);
370 afs_osi_Free(tmpP, sizeof(struct nsafs_login));
373 if (strcmp(loginP->username, user) == 0 &&
374 strcmp(loginP->cellname, cell) == 0 &&
375 memcmp((void *)&loginP->cksum[0], (void *)cksum,
376 SHA_HASH_BYTES) == 0) {
377 *viceid = loginP->viceid;
378 *group0 = loginP->group0;
379 *group1 = loginP->group1;
380 crit_exit(nsafs_login_lock);
383 loginP = loginP->next;
385 crit_exit(nsafs_login_lock);
390 * Insert a login ID into the cache. If the user already has an entry,
391 * then overwrite the old entry.
393 int nsafs_login_store(
400 afs_uint32 expiration)
404 struct nsafs_login *loginP, *tmpP, loginTmp;
407 * Search the hash chain for a matching entry, free
408 * expired entries as we search
410 index = nsafs_login_hash(user, cell);
411 curTime = time(NULL);
412 crit_enter(nsafs_login_lock);
413 loginP = nsafs_login_cache[index].head;
414 while (loginP != NULL) {
415 if (strcmp(loginP->username, user) == 0 &&
416 strcmp(loginP->cellname, cell) == 0) {
419 if (loginP->expiration < curTime) {
422 nsafs_set_id_from_ints(tmpP->viceid, tmpP->group0, tmpP->group1);
424 DLL_DELETE(tmpP, nsafs_login_cache[index].head,
425 nsafs_login_cache[index].tail, next, prev);
426 afs_osi_Free(tmpP, sizeof(struct nsafs_login));
429 loginP = loginP->next;
431 if (loginP == NULL) {
432 loginP = (struct nsafs_login *)
433 afs_osi_Alloc(sizeof(struct nsafs_login));
434 strcpy(&loginP->username[0], user);
435 strcpy(&loginP->cellname[0], cell);
437 DLL_DELETE(loginP, nsafs_login_cache[index].head,
438 nsafs_login_cache[index].tail, next, prev);
439 nsafs_set_id_from_ints(loginP->viceid, loginP->group0, loginP->group1);
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
457 int nsafs_get_string(
468 tmpPtr = pblock_findval(name, pb);
471 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
472 "nsafs_init: please supply a %s parameter", name);
478 *paramP = afs_osi_Alloc(strlen(tmpPtr)+1);
479 strcpy(*paramP, tmpPtr);
484 * Extract a long integer parameter from the parameter block
497 start = pblock_findval(name, pb);
500 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
501 "nsafs_init: please supply a %s parameter", name);
508 val = strtol(start, &end, 10);
509 if (val <= 0 || end == start || *end != '\0') {
510 log_error(LOG_MISCONFIG, "nsafs", sn, rq,
511 "nsafs_init: invalid %s parameter '%s'", name, start);
519 * Extract an integer parameter from the parameter block
532 code = nsafs_get_long(&val, (long)dflt, name, pb, sn, rq);
533 if (code == REQ_PROCEED) {
540 * Parse the authorization header for username and password
542 void nsafs_parse_authhdr(
556 * Skip leading blanks, check for basic authentication
558 for (p = authHdr ; *p == ' ' && *p != '\0' ; p++);
559 if (strncasecmp(p, "basic ", 6) != 0) {
562 for (p += 6 ; *p == ' ' ; p++);
565 * Username and password are base64 encoded
570 * Format is user@cell:passwd. The user, cell or passwd may be missing
572 for ( i = 0 ; *p != '@' && *p != ':' && *p != '\0' ; p++ , i++) {
577 for (i = 0 , p++ ; *p != ':' && *p != '\0' ; p++ , i++) {
583 for (i = 0 , p++ ; *p != '\0' ; p++ , i++) {
591 * Return an appropriate error given a system errno
593 int nsafs_error_check(
606 sprintf(txtbuf, "%s: %s\n", text, strerror(code));
608 sprintf(txtbuf, "%s\n", strerror(code));
612 * Special case, if we get EACCES inside a public directory
613 * and are unauthenticated, change the error to EPERM unless
614 * nsafs_nocheck is set. If nsafs_nocheck is set then change
617 if (code == EACCES &&
618 pblock_findval("nsafs_nocheck", rq->vars) == NULL &&
619 (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
620 pblock_findval("nsafs_group0", rq->vars) == NULL ||
621 pblock_findval("nsafs_group1", rq->vars) == NULL)) {
623 } else if (code == EPERM &&
624 pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
625 char *status=pblock_findval("status",rq->vars);
626 if (strcmp(status,"Login Failed"))
633 * We overload EPERM (not super-user) to mean unauthenticated.
634 * We use the first subdirectory beneath the AFS mount point
637 log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
638 protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
639 path = pblock_findval("path", rq->vars);
642 path = uafs_afsPathName(path);
644 if (path != NULL && *path != '\0') {
645 realmBuf = strdup(path);
647 if (realmBuf == NULL) {
648 /* Don't dump core, just make AFS into one big realm */
649 sprintf(txtbuf, "Basic realm=\"%s\"", afs_mountDir);
651 /* extract the first subdirectory in AFS */
652 if ((tmp = strchr(realmBuf, '/')) != NULL)
654 sprintf(txtbuf, "Basic realm=\"%s/%s\"", afs_mountDir, realmBuf);
657 pblock_nvinsert("WWW-authenticate", txtbuf, rq->srvhdrs);
660 log_error(LOG_SECURITY, "nsafs", sn, rq, txtbuf);
661 protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
665 log_error(LOG_INFORM, "nsafs", sn, rq, txtbuf);
666 protocol_status(sn, rq, PROTOCOL_NOT_FOUND, NULL);
669 log_error(LOG_FAILURE, "nsafs", sn, rq, txtbuf);
670 protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL);
677 * Check the preconditions on a request. Return REQ_PROCEED
678 * if the preconditions are met. Any other return value means
679 * that the request has been aborted.
681 int nsafs_check_preconditions(
689 struct tm tms, *tmsp;
692 mtime = stp->st_mtime;
693 tmsp = system_gmtime(&mtime, &tms);
695 request_header("if-modified-since", &reqhdr, sn, rq);
696 if (reqhdr != NULL && util_later_than(tmsp, reqhdr)) {
697 protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL);
701 request_header("if-unmodified-since", &reqhdr, sn, rq);
702 if (reqhdr != NULL && !util_later_than(tmsp, reqhdr)) {
703 protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL);
711 * Set the content-length and last-modified response header
713 * We used to call protocol_set_finfo, but it wasn't handling
714 * if-unmodified-since headers correctly.
723 struct tm tms, *tmsp;
726 char *days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
727 char *months[] = {"Jan","Feb","Mar","Apr","May","Jun",
728 "Jul","Aug","Sep","Oct","Nov","Dec"};
730 mtime = stp->st_mtime;
731 tmsp = system_gmtime(&mtime, &tms);
732 sprintf(&dateStr[0],"%s, %02d %s %d %02d:%02d:%02d GMT",
733 days[tmsp->tm_wday], tmsp->tm_mday,
734 months[tmsp->tm_mon], tmsp->tm_year+1900,
735 tmsp->tm_hour, tmsp->tm_min, tmsp->tm_sec);
736 pblock_nvinsert("Last-Modified", &dateStr[0], rq->srvhdrs);
737 pblock_nninsert("Content-Length", stp->st_size, rq->srvhdrs);
743 * Initialize the AFS plugin. We do not initialize the AFS client
744 * here because we are still in the parent process. We don't
745 * initialize AFS until we get the first service request.
747 NSAPI_PUBLIC int nsafs_init(pblock *pb, Session *sn, Request *rq)
752 nsafs_init_lock = crit_init();
755 * Parse the startup parameters
757 code = nsafs_get_string(&mountDirParam, "/afs", "mount", pb, sn, rq);
758 if (code != REQ_PROCEED) {
761 code = nsafs_get_string(&cellNameParam, NULL, "cell", pb, sn, rq);
762 if (code != REQ_PROCEED) {
765 code = nsafs_get_string(&confDirParam, NULL, "confdir", pb, sn, rq);
766 if (code != REQ_PROCEED) {
769 code = nsafs_get_string(&cacheBaseDirParam, NULL, "cachedir", pb, sn, rq);
770 if (code != REQ_PROCEED) {
773 code = nsafs_get_int(&cacheBlocksParam, -1, "blocks", pb, sn, rq);
774 if (code != REQ_PROCEED) {
777 code = nsafs_get_int(&cacheFilesParam, 0, "files", pb, sn, rq);
778 if (code != REQ_PROCEED) {
781 code = nsafs_get_int(&cacheStatEntriesParam, -1, "stat", pb, sn, rq);
782 if (code != REQ_PROCEED) {
785 code = nsafs_get_int(&nDaemonsParam, -1, "daemons", pb, sn, rq);
786 if (code != REQ_PROCEED) {
789 code = nsafs_get_int(&dCacheSizeParam, 0, "dcache", pb, sn, rq);
790 if (code != REQ_PROCEED) {
793 code = nsafs_get_int(&vCacheSizeParam, 0, "volumes", pb, sn, rq);
794 if (code != REQ_PROCEED) {
797 code = nsafs_get_int(&chunkSizeParam, 0, "chunksize", pb, sn, rq);
798 if (code != REQ_PROCEED) {
801 code = nsafs_get_int(&debugParam, 0, "debug", pb, sn, rq);
802 if (code != REQ_PROCEED) {
805 code = nsafs_get_string(&logFileParam, NULL, "logfile", pb, sn, rq);
806 if (code != REQ_PROCEED) {
809 code = nsafs_get_long(&maxExpirationParam, LONG_MAX, "exp-max", pb, sn, rq);
810 if (code != REQ_PROCEED) {
818 * Extract name strings from a comma separated list
820 char *nsafs_NameFromNames(char *last, char *list, int *pos)
829 afs_osi_Free(last, strlen(last)+1);
832 if (*start == '\0') {
835 for (len = 0 ; start[len] != ',' && start[len] != '\0' ; len++);
837 if (list[*pos] == ',') {
840 retVal = afs_osi_Alloc(len+1);
841 memcpy(retVal, start, len);
847 * Authcheck function for AFS
849 * Check for an Authorization header. If there is one then do the
850 * AFS login and place the first two groups in the user's creds
851 * into the nsafs-group1 and nsafs-group2 request variables.
852 * Send an Unauthorized response if login fails to prompt the user
853 * to reenter the username and password.
855 NSAPI_PUBLIC int nsafs_basic(pblock *pb, Session *sn, Request *rq)
863 char user[NSAFS_USERNAME_MAX];
864 char cell[NSAFS_USERNAME_MAX];
865 char passwd[NSAFS_PASSWORD_MAX];
866 char cksum[SHA_HASH_BYTES];
867 struct usr_ucred *crp;
868 afs_int32 password_expires = -1;
872 afs_uint32 expiration;
876 if (nsafs_init_done == 0) {
881 * Get the authorization header, if none found then continue,
882 * we check whether authorization is required later on.
884 request_header("authorization", &authHdr, sn, rq);
885 if (authHdr == NULL || strlen(authHdr) == 0) {
890 * Get the user name and password from the authorization header
892 nsafs_parse_authhdr(authHdr, &user[0], &cell[0], &passwd[0]);
893 if (user[0] == '\0' || passwd[0] == '\0') {
894 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
895 pblock_nvinsert("status","Login Failed",rq->vars);
896 return nsafs_error_check(EPERM, "Invalid auth header", pb, sn, rq);
898 if (cell[0] == '\0') {
899 strcpy(cell, afs_LclCellName);
903 * Lookup the username and password in the login cache.
904 * If we find a match we reuse the existing identity.
906 nsafs_login_checksum(user, cell, passwd, &cksum[0]);
907 rc = nsafs_login_lookup(user, cell, cksum, &viceid, &group0, &group1);
909 pblock_nninsert("nsafs_viceid", viceid, rq->vars);
910 pblock_nninsert("nsafs_group0", group0, rq->vars);
911 pblock_nninsert("nsafs_group1", group1, rq->vars);
912 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
917 * Make sure the user is from one of the cells we are configured for
919 cellP = nsafs_NameFromNames(NULL, cellNameParam, &pos);
920 while (cellP != NULL) {
921 if (strcmp(cellP, cell) == 0) {
924 cellP = nsafs_NameFromNames(cellP, cellNameParam, &pos);
927 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
928 pblock_nvinsert("status","Login Failed",rq->vars);
929 return nsafs_error_check(EPERM, "Invalid cell", pb, sn, rq);
931 afs_osi_Free(cellP, strlen(cellP)+1);
934 nsafs_set_id_from_strings(NULL, NULL, NULL);
935 code = uafs_klog(user, cell, passwd, &reason);
936 memset((void *)&passwd[0], 0, NSAFS_PASSWORD_MAX);
939 sprintf(txtbuf, "%s@%s: %s\n", user, cell, reason);
940 pblock_nvinsert("status","Login Failed",rq->vars);
941 return nsafs_error_check(EPERM, txtbuf, pb, sn, rq);
946 expiration = u.u_expiration;
947 usr_assert(expiration != 0);
948 expiration = MIN(expiration, (afs_uint32)(time(NULL) + maxExpirationParam));
951 * Insert the credentials into the login cache
953 pblock_nvinsert("auth-type", "basic", rq->vars);
954 pblock_nvinsert("auth-user", user, rq->vars);
955 pblock_nninsert("nsafs_viceid", u.u_viceid, rq->vars);
956 pblock_nninsert("nsafs_group0", u.u_cred->cr_groups[0], rq->vars);
957 pblock_nninsert("nsafs_group1", u.u_cred->cr_groups[1], rq->vars);
958 nsafs_login_store(user, cell, cksum, u.u_viceid, u.u_cred->cr_groups[0],
959 u.u_cred->cr_groups[1], expiration);
965 * Name trans function for AFS
967 * Terminates the name translation step for files in AFS.
968 * Puts the AFS pathname into path and into ppath request vars.
970 NSAPI_PUBLIC int nsafs_mount(pblock *pb, Session *sn, Request *rq)
977 * Get the URI from the request block
979 reqUri = pblock_findval("uri", rq->reqpb);
980 if (reqUri == NULL) {
984 if (uafs_afsPathName(reqUri) == NULL) {
991 * If we have a new-url parameter then the new-url may be in AFS
992 * if and only if the path is in AFS
994 request_header("new-url", &newReqUri, sn, rq);
995 if (newReqUri != NULL) {
996 if (util_uri_is_evil(newReqUri)) {
997 util_uri_parse(newReqUri);
999 if (uafs_afsPathName(newReqUri) != NULL) {
1002 * We do not support moving files in or out of AFS
1004 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
1005 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1008 pblock_nvinsert("newpath", newReqUri, rq->vars);
1010 } else if (isAfsPath) {
1012 * We do not support moving files in or out of AFS
1014 log_error(LOG_INFORM, "nsafs", sn, rq, "new-URL not in AFS");
1015 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
1018 } else if (!isAfsPath) {
1019 return REQ_NOACTION;
1023 * This is an AFS request
1025 pblock_nvinsert("path", reqUri, rq->vars);
1026 pblock_nvinsert("ppath", reqUri, rq->vars);
1031 * Allow unauthorized users to access a specific directory in AFS
1033 NSAPI_PUBLIC int nsafs_public(pblock *pb, Session *sn, Request *rq)
1039 path = pblock_findval("path", rq->vars);
1041 return REQ_NOACTION;
1043 public = pblock_findval("public", pb);
1044 if (public == NULL) {
1045 return REQ_NOACTION;
1049 * if the path is in AFS and is in the given directory then allow access
1051 if (util_uri_is_evil(path)) {
1052 util_uri_parse(path);
1054 if (uafs_afsPathName(path) == NULL) {
1055 return REQ_NOACTION;
1057 if (util_uri_is_evil(public)) {
1058 util_uri_parse(public);
1060 pubLen = strlen(public);
1061 if (strncmp(path, public, pubLen) != 0 ||
1062 (path[pubLen] != '/' && path[pubLen] != '\0')) {
1063 return REQ_NOACTION;
1065 if (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
1066 pblock_findval("nsafs_group0", rq->vars) == NULL ||
1067 pblock_findval("nsafs_group1", rq->vars) == NULL) {
1068 pblock_nvinsert("nsafs_public", "TRUE", rq->vars);
1074 * Identify a path that should be an authentication realm.
1076 NSAPI_PUBLIC int nsafs_realm(pblock *pb, Session *sn, Request *rq)
1083 * Ignore matches for the current path once we find a realm
1084 * line that matches this path.
1086 realm = pblock_findval("nsafs_realm", rq->vars);
1087 if (realm != NULL) {
1088 return REQ_NOACTION;
1091 path = pblock_findval("path", rq->vars);
1093 return REQ_NOACTION;
1095 if (util_uri_is_evil(path)) {
1096 util_uri_parse(path);
1098 if (uafs_afsPathName(path) == NULL) {
1099 return REQ_NOACTION;
1102 realm = pblock_findval("path", pb);
1103 if (realm == NULL) {
1104 return REQ_NOACTION;
1106 if (util_uri_is_evil(realm)) {
1107 util_uri_parse(realm);
1110 pathLen = strlen(realm);
1111 if (strncmp(path, realm, pathLen) != 0 ||
1112 (path[pathLen] != '/' && path[pathLen] != '\0')) {
1113 return REQ_NOACTION;
1115 pblock_nvinsert("nsafs_realm", realm, rq->vars);
1120 * Check whether any path elements beneath the nolinks directory
1121 * are symbolic links. Return REQ_PROCEED if no links are found.
1123 int nsafs_check_for_links(
1139 * Check each component of the path below the nolinks directory.
1141 dirLen = strlen(nolinks);
1142 pathLen = strlen(path);
1143 if (pathLen < dirLen ||
1144 strncmp(path, nolinks, dirLen) != 0 ||
1145 (path[dirLen] != '/' && path[dirLen] != '\0')) {
1148 if (path[dirLen] == '/') {
1152 allocSize = pathLen+1;
1153 tmpPath = (char *)afs_osi_Alloc(allocSize);
1154 strcpy(tmpPath, path);
1155 while(tmpPath[pathLen] == '/' && pathLen > dirLen) {
1156 tmpPath[pathLen] = '\0';
1159 while (pathLen > dirLen) {
1160 rc = uafs_lstat(tmpPath, &st);
1163 afs_osi_Free(tmpPath, allocSize);
1164 return nsafs_error_check(code, NULL, pb, sn, rq);
1166 if ((st.st_mode & S_IFMT) == S_IFLNK) {
1167 afs_osi_Free(tmpPath, allocSize);
1168 return nsafs_error_check(ENOENT, NULL, pb, sn, rq);
1170 while(tmpPath[pathLen] != '/' && pathLen > dirLen) {
1171 tmpPath[pathLen] = '\0';
1174 while(tmpPath[pathLen] == '/' && pathLen > dirLen) {
1175 tmpPath[pathLen] = '\0';
1179 afs_osi_Free(tmpPath, allocSize);
1184 * Deny access to symbolic links in a directory or its descendents.
1186 NSAPI_PUBLIC int nsafs_nolinks(pblock *pb, Session *sn, Request *rq)
1196 path = pblock_findval("path", rq->vars);
1198 return REQ_NOACTION;
1200 nolinks = pblock_findval("nolinks", pb);
1201 if (nolinks == NULL) {
1202 return REQ_NOACTION;
1204 newPath = pblock_findval("newpath", rq->vars);
1207 * if the path is in AFS and is in the nolinks directory then deny access
1208 * to any symbolic links below the nolinks directory.
1210 if (util_uri_is_evil(path)) {
1211 util_uri_parse(path);
1213 if (uafs_afsPathName(path) == NULL) {
1214 return REQ_NOACTION;
1216 if (util_uri_is_evil(nolinks)) {
1217 util_uri_parse(nolinks);
1221 * Check for group numbers in the request vars, otherwise use the
1224 viceid = pblock_findval("nsafs_viceid", rq->vars);
1225 group0 = pblock_findval("nsafs_group0", rq->vars);
1226 group1 = pblock_findval("nsafs_group1", rq->vars);
1227 nsafs_set_id_from_strings(viceid, group0, group1);
1229 code = nsafs_check_for_links(path, nolinks, pb, sn, rq);
1230 if (code != REQ_PROCEED) {
1234 if (newPath != NULL) {
1235 if (util_uri_is_evil(newPath)) {
1236 util_uri_parse(newPath);
1238 if (uafs_afsPathName(newPath) == NULL) {
1239 return REQ_NOACTION;
1241 code = nsafs_check_for_links(newPath, nolinks, pb, sn, rq);
1242 if (code != REQ_PROCEED) {
1247 return REQ_NOACTION;
1251 * Set the MIME type for files in AFS.
1253 NSAPI_PUBLIC int nsafs_force_type(pblock *pb, Session *sn, Request *rq)
1258 path = pblock_findval("path", rq->vars);
1260 return REQ_NOACTION;
1262 dflt = pblock_findval("type", pb);
1264 return REQ_NOACTION;
1268 * If the path is in AFS then set the nsafs_variable in the
1269 * request variables. The nsafs_error_check routine only
1270 * sends an Unauthorized error if this variable is not set.
1272 if (util_uri_is_evil(path)) {
1273 util_uri_parse(path);
1275 if (uafs_afsPathName(path) == NULL) {
1276 return REQ_NOACTION;
1279 if (pblock_findval("content-type", rq->srvhdrs) == NULL) {
1280 pblock_nvinsert("content-type", dflt, rq->srvhdrs);
1287 * Disable the Unauthorized response message so users never get
1288 * prompted for their name and password.
1290 NSAPI_PUBLIC int nsafs_nocheck(pblock *pb, Session *sn, Request *rq)
1294 path = pblock_findval("path", rq->vars);
1296 return REQ_NOACTION;
1300 * If the path is in AFS then set the nsafs_variable in the
1301 * request variables. The nsafs_error_check routine only
1302 * sends an Unauthorized error if this variable is not set.
1304 if (util_uri_is_evil(path)) {
1305 util_uri_parse(path);
1307 if (uafs_afsPathName(path) == NULL) {
1308 return REQ_NOACTION;
1311 if (pblock_findval("nsafs_nocheck", rq->vars) == NULL) {
1312 pblock_nvinsert("nsafs_nocheck", "TRUE", rq->vars);
1316 * If this is a public directory then proceed, otherwise access
1319 if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1323 return nsafs_error_check(EACCES, NULL, pb, sn, rq);
1327 * Require all requests for AFS files that are not explicitly made
1328 * public to be authenticated.
1330 NSAPI_PUBLIC int nsafs_check(pblock *pb, Session *sn, Request *rq)
1334 path = pblock_findval("path", rq->vars);
1336 return REQ_NOACTION;
1340 * If the path is in AFS then require authentication
1342 if (util_uri_is_evil(path)) {
1343 util_uri_parse(path);
1345 if (uafs_afsPathName(path) == NULL) {
1346 return REQ_NOACTION;
1350 * If this is a public directory then proceed
1352 if (pblock_findval("nsafs_public", rq->vars) != NULL) {
1356 if (pblock_findval("nsafs_viceid", rq->vars) == NULL ||
1357 pblock_findval("nsafs_group0", rq->vars) == NULL ||
1358 pblock_findval("nsafs_group1", rq->vars) == NULL) {
1359 return nsafs_error_check(EPERM, NULL, pb, sn, rq);
1366 * Find index files for directories
1368 NSAPI_PUBLIC int nsafs_find_index(pblock *pb, Session *sn, Request *rq)
1382 path = pblock_findval("path", rq->vars);
1384 return REQ_NOACTION;
1387 indexNames = pblock_findval("index-names", pb);
1388 if (indexNames == NULL) {
1389 return REQ_NOACTION;
1393 * Skip pathnames that don't end in a slash
1395 if (*path == '\0' || path[strlen(path)-1] != '/') {
1396 return REQ_NOACTION;
1400 * If the path is a directory then check if the directory has
1403 if (util_uri_is_evil(path)) {
1404 util_uri_parse(path);
1406 if (uafs_afsPathName(path) == NULL) {
1408 * Look for index files in local file system
1410 rc = stat(path, &st);
1413 return nsafs_error_check(code, NULL, pb, sn, rq);
1416 if ((st.st_mode & S_IFMT) != S_IFDIR) {
1417 return REQ_NOACTION;
1420 indexP = nsafs_NameFromNames(NULL, indexNames, &pos);
1421 while (indexP != NULL) {
1422 nameP = afs_osi_Alloc(strlen(path)+strlen(indexP)+2);
1423 sprintf(nameP, "%s/%s", path, indexP);
1424 rc = stat(nameP, &st);
1425 if (rc == 0 && (st.st_mode & S_IFMT) != S_IFDIR) {
1426 param_free(pblock_remove("path", rq->vars));
1427 pblock_nvinsert("path", nameP, rq->vars);
1428 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1429 afs_osi_Free(indexP, strlen(indexP)+1);
1432 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1433 indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1437 * Check for group numbers in the request vars, otherwise use the
1440 viceid = pblock_findval("nsafs_viceid", rq->vars);
1441 group0 = pblock_findval("nsafs_group0", rq->vars);
1442 group1 = pblock_findval("nsafs_group1", rq->vars);
1443 nsafs_set_id_from_strings(viceid, group0, group1);
1446 * Look for index files in AFS
1448 rc = uafs_stat(path, &st);
1451 return nsafs_error_check(code, NULL, pb, sn, rq);
1454 if ((st.st_mode & S_IFMT) != S_IFDIR) {
1455 return REQ_NOACTION;
1458 indexP = nsafs_NameFromNames(NULL, indexNames, &pos);
1459 while (indexP != NULL) {
1460 nameP = afs_osi_Alloc(strlen(path)+strlen(indexP)+2);
1461 sprintf(nameP, "%s/%s", path, indexP);
1462 rc = uafs_stat(nameP, &st);
1463 if (rc == 0 && (st.st_mode & S_IFMT) != S_IFDIR) {
1464 param_free(pblock_remove("path", rq->vars));
1465 pblock_nvinsert("path", nameP, rq->vars);
1466 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1467 afs_osi_Free(indexP, strlen(indexP)+1);
1470 afs_osi_Free(nameP, strlen(path)+strlen(indexP)+2);
1471 indexP = nsafs_NameFromNames(indexP, indexNames, &pos);
1474 return REQ_NOACTION;
1478 * Node in binary tree used to sort directory entries
1481 char *name; /* directory member name */
1482 char *text; /* directory index text */
1483 int allocLen; /* Size of this allocated block */
1484 int textLen; /* Size of the text string */
1485 int balance; /* used to balance binary trees */
1486 struct nsafs_tree *left;
1487 struct nsafs_tree *right;
1490 #ifdef NSAFS_TREE_DEBUG
1492 * Validate that the given tree is a valid balanced binary tree
1494 int nsafs_tree_check(struct nsafs_tree *root)
1500 if (root->left == NULL) {
1503 assert(strcmp(root->left->name, root->name) < 0);
1504 leftDepth = nsafs_tree_check(root->left);
1506 if (root->right == NULL) {
1509 assert(strcmp(root->right->name, root->name) >= 0);
1510 rightDepth = nsafs_tree_check(root->right);
1512 balance = rightDepth - leftDepth;
1513 assert(balance == root->balance);
1514 assert(balance >= -1);
1515 assert(balance <= 1);
1516 return (MAX(leftDepth, rightDepth)+1);
1518 #endif /* NSAFS_TREE_DEBUG */
1521 * Insert a node into the balanced binary tree of directory entries.
1522 * rootP is the address of the parent's pointer to this node.
1523 * Returns the change in depth of this tree (0 or 1)
1525 int nsafs_node_insert(
1526 struct nsafs_tree *newNode,
1527 struct nsafs_tree **rootP)
1529 struct nsafs_tree *thisNode;
1533 if (strcmp(newNode->name, thisNode->name) < 0) {
1537 if (thisNode->left == NULL) {
1538 thisNode->left = newNode;
1539 if (thisNode->right == NULL) {
1540 thisNode->balance = -1;
1543 thisNode->balance = 0;
1547 delta = nsafs_node_insert(newNode, &thisNode->left);
1551 thisNode->balance -= delta;
1552 if (thisNode->balance == -2) {
1556 if (thisNode->left->balance > 0) {
1557 #ifdef NSAFS_TREE_DEBUG
1558 printf("rotate left, line %d\n", __LINE__);
1559 #endif /* NSAFS_TREE_DEBUG */
1560 *rootP = thisNode->left->right;
1561 if ((*rootP)->balance > 0) {
1562 thisNode->left->balance = -(*rootP)->balance;
1563 (*rootP)->balance = 0;
1564 thisNode->balance = 0;
1566 thisNode->left->balance = 0;
1567 thisNode->balance = -(*rootP)->balance;
1568 (*rootP)->balance = 0;
1570 thisNode->left->right = (*rootP)->left;
1571 (*rootP)->left = thisNode->left;
1572 thisNode->left = (*rootP)->right;
1573 (*rootP)->right = thisNode;
1575 #ifdef NSAFS_TREE_DEBUG
1576 printf("rotate left, line %d\n", __LINE__);
1577 #endif /* NSAFS_TREE_DEBUG */
1578 *rootP = thisNode->left;
1579 (*rootP)->balance = 0;
1580 thisNode->balance = 0;
1581 thisNode->left = (*rootP)->right;
1582 (*rootP)->right = thisNode;
1585 } else if (thisNode->balance != 0) {
1595 if (thisNode->right == NULL) {
1596 thisNode->right = newNode;
1597 if (thisNode->left == NULL) {
1598 thisNode->balance = 1;
1601 thisNode->balance = 0;
1605 delta = nsafs_node_insert(newNode, &thisNode->right);
1609 thisNode->balance += delta;
1610 if (thisNode->balance == 2) {
1614 if (thisNode->right->balance < 0) {
1615 #ifdef NSAFS_TREE_DEBUG
1616 printf("rotate right, line %d\n", __LINE__);
1617 #endif /* NSAFS_TREE_DEBUG */
1618 *rootP = thisNode->right->left;
1619 if ((*rootP)->balance < 0) {
1620 thisNode->right->balance = -(*rootP)->balance;
1621 (*rootP)->balance = 0;
1622 thisNode->balance = 0;
1624 thisNode->right->balance = 0;
1625 thisNode->balance = -(*rootP)->balance;
1626 (*rootP)->balance = 0;
1628 thisNode->right->left = (*rootP)->right;
1629 (*rootP)->right = thisNode->right;
1630 thisNode->right = (*rootP)->left;
1631 (*rootP)->left = thisNode;
1633 #ifdef NSAFS_TREE_DEBUG
1634 printf("rotate right, line %d\n", __LINE__);
1635 #endif /* NSAFS_TREE_DEBUG */
1636 *rootP = thisNode->right;
1637 (*rootP)->balance = 0;
1638 thisNode->balance = 0;
1639 thisNode->right = (*rootP)->left;
1640 (*rootP)->left = thisNode;
1643 } else if (thisNode->balance != 0) {
1653 * Allocate storage for a new directory entry, copy in the name and
1654 * text, and insert the entry into the balanced binary tree.
1656 void nsafs_tree_insert(
1659 struct nsafs_tree **rootP)
1664 struct nsafs_tree *newNode;
1667 * allocate storage, initialize the fields, and copy in the data
1669 nameLen = strlen(name);
1670 textLen = strlen(text);
1671 allocLen = sizeof(struct nsafs_tree) + nameLen + textLen + 2;
1672 newNode = (struct nsafs_tree *)afs_osi_Alloc(allocLen);
1673 usr_assert(newNode != NULL);
1674 newNode->name = (char *)(newNode+1);
1675 newNode->text = newNode->name + nameLen + 1;
1676 newNode->textLen = textLen;
1677 newNode->allocLen = allocLen;
1678 newNode->balance = 0;
1679 newNode->left = NULL;
1680 newNode->right = NULL;
1681 strcpy(newNode->name, name);
1682 strcpy(newNode->text, text);
1685 * If this is the first node, insert it here, otherwise call
1686 * nsafs_node_insert to insert the node into the balanced
1689 if (*rootP == NULL) {
1692 nsafs_node_insert(newNode, rootP);
1694 #ifdef NSAFS_TREE_DEBUG
1695 nsafs_tree_check(*rootP);
1696 #endif /* NSAFS_TREE_DEBUG */
1700 * Transmit the contents of the tree
1702 int nsafs_tree_send(
1704 struct nsafs_tree *root,
1710 struct nsafs_tree *node;
1716 * Recurse left, iterate right
1719 while (node != NULL) {
1720 if (node->left != NULL) {
1721 code = nsafs_tree_send(sd, node->left, outbuf, buflen, bufsize);
1722 if (code == IO_ERROR) {
1726 txtLen = node->textLen;
1727 txtBuf = node->text;
1728 while (txtLen > 0) {
1729 if (*buflen == bufsize) {
1730 code = net_write(sd, outbuf, bufsize);
1731 if (code == IO_ERROR) {
1736 len = MIN(txtLen, bufsize - *buflen);
1737 memcpy((void *)(outbuf + *buflen), (void *)txtBuf, len);
1748 * Free the binary tree and all data within
1750 void nsafs_tree_free(
1751 struct nsafs_tree *root)
1753 struct nsafs_tree *node, *next;
1756 * Iterate left, recurse right
1759 while (node != NULL) {
1760 if (node->right != NULL) {
1761 nsafs_tree_free(node->right);
1764 afs_osi_Free(node, sizeof(struct nsafs_tree) + strlen(node->name) +
1765 strlen(node->text) + 2);
1771 * Send the contents of an AFS directory, Simple directory format
1773 int nsafs_send_directory(
1784 struct usr_dirent *enp;
1788 char *htmlHdr = "<HTML>\r\n<BODY>\r\n"
1789 "<TITLE>Index of %s</TITLE>\r\n"
1790 "<h1>Index of %s</h1>\r\n"
1792 char *htmlTrl = "</PRE></BODY></HTML>\r\n";
1793 struct nsafs_tree *root;
1796 * Set the content type to "text/html"
1798 param_free(pblock_remove("content-type", rq->srvhdrs));
1799 pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
1802 * Build a binary tree of directory entries, and calculate the
1803 * length of our response message
1805 dirp = uafs_opendir(path);
1808 return nsafs_error_check(code, NULL, pb, sn, rq);
1812 dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1813 while ((enp = uafs_readdir(dirp)) != NULL) {
1814 if (strcmp(enp->d_name, ".") == 0) {
1816 } else if (strcmp(enp->d_name, "..") == 0) {
1817 filename = "Parent Directory";
1819 filename = enp->d_name;
1822 "<A HREF=\"%s%s\" NAME=\"%s\"> %s</A>\r\n",
1823 path, enp->d_name, filename, filename);
1824 contentLength += strlen(dirbuf);
1825 nsafs_tree_insert(enp->d_name, dirbuf, &root);
1829 uafs_closedir(dirp);
1830 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1831 nsafs_tree_free(root);
1832 return nsafs_error_check(code, NULL, pb, sn, rq);
1834 rc = uafs_closedir(dirp);
1837 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1838 nsafs_tree_free(root);
1839 return nsafs_error_check(code, NULL, pb, sn, rq);
1843 * Calculate the length of the HTML headers and trailers,
1844 * set the content-length field in the reply header, and
1845 * start the reply message
1847 sprintf(dirbuf, htmlHdr, path, path);
1848 contentLength += strlen(dirbuf) + strlen(htmlTrl);
1849 stp->st_size = contentLength;
1850 code = nsafs_set_finfo(sn, rq, stp);
1851 if (code != REQ_PROCEED) {
1852 nsafs_tree_free(root);
1853 protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL);
1856 protocol_status(sn, rq, PROTOCOL_OK, NULL);
1857 code = protocol_start_response(sn, rq);
1858 if (code != REQ_PROCEED) {
1859 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1860 nsafs_tree_free(root);
1865 * Send the HTML header, file data and HTML trailer
1867 if (net_write(sn->csd, dirbuf, strlen(dirbuf)) == IO_ERROR) {
1868 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1869 nsafs_tree_free(root);
1870 log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1874 code = nsafs_tree_send(sn->csd, root, dirbuf, &buflen, NSAFS_BUFFER_SIZE);
1875 if (code == IO_ERROR) {
1876 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1877 nsafs_tree_free(root);
1878 log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1881 nsafs_tree_free(root);
1883 code = net_write(sn->csd, dirbuf, buflen);
1884 if (code == IO_ERROR) {
1885 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1886 log_error(LOG_INFORM, "nsafs", sn, rq, "send_directory IO_ERROR");
1890 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
1891 if (net_write(sn->csd, htmlTrl, strlen(htmlTrl)) == IO_ERROR) {
1899 * Send the contents of an AFS file
1901 int nsafs_send_file(
1915 * Make sure we can open the file before we send the response header
1917 fd = uafs_open(path, O_RDONLY, 0);
1920 return nsafs_error_check(code, NULL, pb, sn, rq);
1924 * Add a content-length field to the response header and
1925 * begin the response message. We have already checked the
1926 * preconditions, so a return code other than REQ_PROCEED
1927 * means that this is a HEAD command.
1929 code = nsafs_set_finfo(sn, rq, stp);
1930 if (code != REQ_PROCEED) {
1933 protocol_status(sn, rq, PROTOCOL_OK, NULL);
1934 code = protocol_start_response(sn, rq);
1935 if (code != REQ_PROCEED) {
1940 * Send the file contents
1942 filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
1943 while ((rc = uafs_read(fd, filebuf, NSAFS_BUFFER_SIZE)) > 0) {
1944 if (net_write(sn->csd, filebuf, rc) == IO_ERROR) {
1945 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1947 log_error(LOG_INFORM, "nsafs", sn, rq, "send_file IO_ERROR");
1954 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1955 log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_read, err=%d", code);
1958 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
1959 rc = uafs_close(fd);
1962 log_error(LOG_FAILURE, "nsafs", sn, rq, "uafs_close, err=%d", code);
1968 * Service function for AFS files and directories
1970 NSAPI_PUBLIC int nsafs_send(pblock *pb, Session *sn, Request *rq)
1983 if (nsafs_init_done == 0) {
1988 * Only service paths that are in AFS
1990 path = pblock_findval("path", rq->vars);
1992 return REQ_NOACTION;
1994 if (uafs_afsPathName(path) == NULL) {
1995 return REQ_NOACTION;
1999 * We do not support content-range headers
2001 request_header("content-range", &rangeHdr, sn, rq);
2002 if (rangeHdr != NULL) {
2003 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
2004 log_error(LOG_INFORM, "nsafs", sn, rq,
2005 "content-range is not implemented");
2009 * Check for group numbers in the request vars, otherwise use the
2012 viceid = pblock_findval("nsafs_viceid", rq->vars);
2013 group0 = pblock_findval("nsafs_group0", rq->vars);
2014 group1 = pblock_findval("nsafs_group1", rq->vars);
2015 nsafs_set_id_from_strings(viceid, group0, group1);
2018 * Get the file attributes
2020 rc = uafs_stat(path, &st);
2023 return nsafs_error_check(code, NULL, pb, sn, rq);
2027 * Check the request preconditions
2029 code = nsafs_check_preconditions(&st, pb, sn, rq);
2030 if (code != REQ_PROCEED) {
2035 * Send the contents of files, and a formatted index of directories
2037 if ((st.st_mode & S_IFMT) == S_IFDIR) {
2039 dirPath = afs_osi_Alloc(len+2);
2040 strcpy(dirPath, path);
2041 if (dirPath[len-1] != '/') {
2043 dirPath[len+1] = '\0';
2045 if (util_uri_is_evil(dirPath)) {
2046 util_uri_parse(dirPath);
2048 code = nsafs_send_directory(dirPath, &st, pb, sn, rq);
2049 afs_osi_Free(dirPath, len+2);
2051 code = nsafs_send_file(path, &st, pb, sn, rq);
2057 * Service function to create new AFS files
2059 NSAPI_PUBLIC int nsafs_put(pblock *pb, Session *sn, Request *rq)
2079 if (nsafs_init_done == 0) {
2084 * Only service paths that are in AFS
2086 path = pblock_findval("path", rq->vars);
2088 return REQ_NOACTION;
2090 if (uafs_afsPathName(path) == NULL) {
2091 return REQ_NOACTION;
2095 * We do not support content-range headers
2097 request_header("content-range", &rangeHdr, sn, rq);
2098 if (rangeHdr != NULL) {
2099 protocol_status(sn, rq, PROTOCOL_NOT_IMPLEMENTED, NULL);
2100 log_error(LOG_INFORM, "nsafs", sn, rq,
2101 "content-range is not implemented");
2105 * Check for group numbers in the request vars, otherwise use the
2108 viceid = pblock_findval("nsafs_viceid", rq->vars);
2109 group0 = pblock_findval("nsafs_group0", rq->vars);
2110 group1 = pblock_findval("nsafs_group1", rq->vars);
2111 nsafs_set_id_from_strings(viceid, group0, group1);
2114 * Check for a content length header
2116 request_header("content-length", &lengthHdr, sn, rq);
2117 if (lengthHdr != NULL) {
2118 contentLength = atoi(lengthHdr);
2124 * Get the file attributes
2126 rc = uafs_stat(path, &st);
2129 if (code == ENOENT) {
2130 rspStatus = PROTOCOL_CREATED;
2132 return nsafs_error_check(code, NULL, pb, sn, rq);
2134 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2135 return nsafs_error_check(EISDIR, NULL, pb, sn, rq);
2137 rspStatus = PROTOCOL_OK;
2140 fd = uafs_open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
2143 return nsafs_error_check(code, NULL, pb, sn, rq);
2147 * Get the file contents
2149 filebuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2152 if (contentLength < 0) {
2153 bytesToRead = NSAFS_BUFFER_SIZE;
2155 bytesToRead = MIN(contentLength, NSAFS_BUFFER_SIZE);
2156 if (bytesToRead == 0) {
2160 for (bytesRead = 0 ; !eof && bytesRead < bytesToRead ; bytesRead++) {
2161 rc = netbuf_getc(sn->inbuf);
2164 } else if (rc == IO_ERROR) {
2166 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2168 return nsafs_error_check(EIO, NULL, pb, sn, rq);
2170 filebuf[bytesRead] = rc;
2173 if (bytesRead > 0) {
2174 if (contentLength > 0) {
2175 contentLength -= bytesRead;
2177 rc = uafs_write(fd, filebuf, bytesRead);
2180 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2182 return nsafs_error_check(code, NULL, pb, sn, rq);
2186 afs_osi_Free(filebuf, NSAFS_BUFFER_SIZE);
2187 rc = uafs_close(fd);
2190 return nsafs_error_check(code, NULL, pb, sn, rq);
2192 if (contentLength > 0) {
2193 log_error(LOG_FAILURE, "nsafs", sn, rq, "received partial contents");
2197 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2198 protocol_status(sn, rq, rspStatus, NULL);
2199 code = protocol_start_response(sn, rq);
2204 * Service function to delete AFS files
2206 NSAPI_PUBLIC int nsafs_delete(pblock *pb, Session *sn, Request *rq)
2216 if (nsafs_init_done == 0) {
2221 * Only service paths that are in AFS
2223 path = pblock_findval("path", rq->vars);
2225 return REQ_NOACTION;
2227 if (uafs_afsPathName(path) == NULL) {
2228 return REQ_NOACTION;
2232 * Check for group numbers in the request vars, otherwise use the
2235 viceid = pblock_findval("nsafs_viceid", rq->vars);
2236 group0 = pblock_findval("nsafs_group0", rq->vars);
2237 group1 = pblock_findval("nsafs_group1", rq->vars);
2238 nsafs_set_id_from_strings(viceid, group0, group1);
2241 * Get the file attributes
2243 rc = uafs_lstat(path, &st);
2246 return nsafs_error_check(code, NULL, pb, sn, rq);
2247 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2248 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot DELETE directories");
2249 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2250 pblock_nvinsert("Allow", NSAFS_DIR_ALLOW, rq->srvhdrs);
2254 rc = uafs_unlink(path);
2257 return nsafs_error_check(code, NULL, pb, sn, rq);
2260 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2261 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2262 code = protocol_start_response(sn, rq);
2267 * Service function to create AFS directories
2269 NSAPI_PUBLIC int nsafs_mkdir(pblock *pb, Session *sn, Request *rq)
2279 if (nsafs_init_done == 0) {
2284 * Only service paths that are in AFS
2286 path = pblock_findval("path", rq->vars);
2288 return REQ_NOACTION;
2290 if (uafs_afsPathName(path) == NULL) {
2291 return REQ_NOACTION;
2295 * Check for group numbers in the request vars, otherwise use the
2298 viceid = pblock_findval("nsafs_viceid", rq->vars);
2299 group0 = pblock_findval("nsafs_group0", rq->vars);
2300 group1 = pblock_findval("nsafs_group1", rq->vars);
2301 nsafs_set_id_from_strings(viceid, group0, group1);
2304 * Create the directory
2306 rc = uafs_mkdir(path, 0755);
2309 return nsafs_error_check(code, NULL, pb, sn, rq);
2312 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2313 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2314 code = protocol_start_response(sn, rq);
2319 * Service function to delete AFS directories
2321 NSAPI_PUBLIC int nsafs_rmdir(pblock *pb, Session *sn, Request *rq)
2331 if (nsafs_init_done == 0) {
2336 * Only service paths that are in AFS
2338 path = pblock_findval("path", rq->vars);
2340 return REQ_NOACTION;
2342 if (uafs_afsPathName(path) == NULL) {
2343 return REQ_NOACTION;
2347 * Check for group numbers in the request vars, otherwise use the
2350 viceid = pblock_findval("nsafs_viceid", rq->vars);
2351 group0 = pblock_findval("nsafs_group0", rq->vars);
2352 group1 = pblock_findval("nsafs_group1", rq->vars);
2353 nsafs_set_id_from_strings(viceid, group0, group1);
2356 * Get the file attributes, rmdir only work on directories.
2358 rc = uafs_lstat(path, &st);
2361 return nsafs_error_check(code, NULL, pb, sn, rq);
2365 * Call unlink to delete symbolic links to directories, and rmdir
2366 * to to delete directories.
2368 if ((st.st_mode & S_IFMT) == S_IFDIR) {
2369 rc = uafs_rmdir(path);
2370 } else if ((st.st_mode & S_IFMT) == S_IFLNK) {
2371 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot RMDIR links");
2372 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2373 pblock_nvinsert("Allow", NSAFS_LINK_ALLOW, rq->srvhdrs);
2375 } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
2376 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot RMDIR files");
2377 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2378 pblock_nvinsert("Allow", NSAFS_FILE_ALLOW, rq->srvhdrs);
2383 return nsafs_error_check(code, NULL, pb, sn, rq);
2386 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2387 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2388 code = protocol_start_response(sn, rq);
2393 * Service function to rename AFS files and directories
2395 NSAPI_PUBLIC int nsafs_move(pblock *pb, Session *sn, Request *rq)
2406 if (nsafs_init_done == 0) {
2411 * Only service paths that are in AFS
2413 path = pblock_findval("path", rq->vars);
2415 return REQ_NOACTION;
2417 if (uafs_afsPathName(path) == NULL) {
2418 return REQ_NOACTION;
2420 newPath = pblock_findval("newpath", rq->vars);
2421 if (newPath == NULL) {
2422 return REQ_NOACTION;
2424 if (uafs_afsPathName(newPath) == NULL) {
2425 return REQ_NOACTION;
2429 * Check for group numbers in the request vars, otherwise use the
2432 viceid = pblock_findval("nsafs_viceid", rq->vars);
2433 group0 = pblock_findval("nsafs_group0", rq->vars);
2434 group1 = pblock_findval("nsafs_group1", rq->vars);
2435 nsafs_set_id_from_strings(viceid, group0, group1);
2440 rc = uafs_rename(path, newPath);
2443 return nsafs_error_check(code, NULL, pb, sn, rq);
2446 pblock_nninsert("Content-Length", 0, rq->srvhdrs);
2447 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2448 code = protocol_start_response(sn, rq);
2453 * Send the index of an AFS directory
2468 struct usr_dirent *enp;
2473 struct nsafs_tree *root;
2475 if (nsafs_init_done == 0) {
2480 * Only service paths that are in AFS
2482 path = pblock_findval("path", rq->vars);
2484 return REQ_NOACTION;
2486 if (uafs_afsPathName(path) == NULL) {
2487 return REQ_NOACTION;
2491 * Check for group numbers in the request vars, otherwise use the
2494 viceid = pblock_findval("nsafs_viceid", rq->vars);
2495 group0 = pblock_findval("nsafs_group0", rq->vars);
2496 group1 = pblock_findval("nsafs_group1", rq->vars);
2497 nsafs_set_id_from_strings(viceid, group0, group1);
2500 * Get the file attributes, index does not work on symbolic links.
2502 rc = uafs_lstat(path, &st);
2505 return nsafs_error_check(code, NULL, pb, sn, rq);
2507 if ((st.st_mode & S_IFMT) == S_IFLNK) {
2508 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot INDEX links");
2509 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2510 pblock_nvinsert("Allow", NSAFS_LINK_ALLOW, rq->srvhdrs);
2512 } else if ((st.st_mode & S_IFMT) != S_IFDIR) {
2513 log_error(LOG_INFORM, "nsafs", sn, rq, "Cannot INDEX files");
2514 protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL);
2515 pblock_nvinsert("Allow", NSAFS_FILE_ALLOW, rq->srvhdrs);
2520 * Set the content type to "text/html"
2522 param_free(pblock_remove("content-type", rq->srvhdrs));
2523 pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
2526 * Build a binary tree of directory entries, and calculate the
2527 * length of our response message
2529 dirp = uafs_opendir(path);
2532 return nsafs_error_check(code, NULL, pb, sn, rq);
2534 dirbuf = afs_osi_Alloc(NSAFS_BUFFER_SIZE);
2535 tmpPath = afs_osi_Alloc(NSAFS_MAX_PATH);
2538 while ((enp = uafs_readdir(dirp)) != NULL) {
2539 sprintf(tmpPath, "%s/%s", path, enp->d_name);
2540 rc = uafs_lstat(tmpPath, &st);
2543 } else if ((st.st_mode & S_IFMT) == S_IFDIR) {
2544 sprintf(dirbuf, "%s directory %u %u\r\n",
2545 enp->d_name, st.st_size, st.st_mtime);
2546 } else if ((st.st_mode & S_IFMT) == S_IFLNK) {
2547 sprintf(dirbuf, "%s link %u %u\r\n",
2548 enp->d_name, st.st_size, st.st_mtime);
2550 sprintf(dirbuf, "%s unknown %u %u\r\n",
2551 enp->d_name, st.st_size, st.st_mtime);
2553 contentLength += strlen(dirbuf);
2554 nsafs_tree_insert(enp->d_name, dirbuf, &root);
2558 uafs_closedir(dirp);
2559 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2560 afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2561 nsafs_tree_free(root);
2562 return nsafs_error_check(code, NULL, pb, sn, rq);
2564 afs_osi_Free(tmpPath, NSAFS_MAX_PATH);
2565 rc = uafs_closedir(dirp);
2568 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2569 nsafs_tree_free(root);
2570 return nsafs_error_check(code, NULL, pb, sn, rq);
2574 * Set the content-length field in the reply header, and
2575 * start the reply message
2577 pblock_nninsert("Content-Length", contentLength, rq->srvhdrs);
2578 protocol_status(sn, rq, PROTOCOL_OK, NULL);
2579 code = protocol_start_response(sn, rq);
2580 if (code != REQ_PROCEED) {
2581 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2582 nsafs_tree_free(root);
2590 code = nsafs_tree_send(sn->csd, root, dirbuf, &buflen, NSAFS_BUFFER_SIZE);
2591 if (code == IO_ERROR) {
2592 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2593 nsafs_tree_free(root);
2594 log_error(LOG_INFORM, "nsafs", sn, rq, "send_index IO_ERROR");
2597 nsafs_tree_free(root);
2599 code = net_write(sn->csd, dirbuf, buflen);
2600 if (code == IO_ERROR) {
2601 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);
2602 log_error(LOG_INFORM, "nsafs", sn, rq, "send_index IO_ERROR");
2606 afs_osi_Free(dirbuf, NSAFS_BUFFER_SIZE);