Commit | Line | Data |
---|---|---|
805e021f CE |
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 | } |