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