1 /* vi:ai:et:ts=8 sw=2
2 */
3 /*
4 * wzdftpd - a modular and cool ftp server
5 * Copyright (C) 2002-2004 Pierre Chifflier
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * As a special exemption, Pierre Chifflier
22 * and other respective copyright holders give permission to link this program
23 * with OpenSSL, and distribute the resulting executable, without including
24 * the source code for OpenSSL in the source distribution.
25 */
26
27 #include "wzd_all.h"
28
29 #ifndef WZD_USE_PCH
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36
37 #include <sys/types.h>
38
39 #ifdef WIN32
40 #include <winsock2.h>
41 #include <direct.h> /* _getcwd */
42 #else
43 #include <unistd.h>
44
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #endif
49
50 #ifndef HAVE_STRTOK_R
51 # include "libwzd-base/wzd_strtok_r.h"
52 #endif
53
54 #include "wzd_structs.h"
55
56 #include "wzd_vfs.h"
57 #include "wzd_dir.h"
58 #include "wzd_file.h"
59 #include "wzd_fs.h"
60 #include "wzd_group.h"
61 #include "wzd_log.h"
62 #include "wzd_misc.h"
63 #include "wzd_user.h"
64
65 #include "wzd_debug.h"
66
67 #endif /* WZD_USE_PCH */
68
69 /** remove a vfs from list */
vfs_remove(wzd_vfs_t ** vfs_list,const char * vpath)70 int vfs_remove( wzd_vfs_t **vfs_list, const char *vpath )
71 {
72 wzd_vfs_t * current_vfs, * next_vfs;
73 wzd_vfs_t * previous_vfs = NULL;
74
75 current_vfs = *vfs_list;
76 while(current_vfs)
77 {
78 next_vfs = current_vfs->next_vfs;
79
80 if ( (DIRCMP( current_vfs->virtual_dir, vpath) == 0) )
81 {
82 if (current_vfs == *vfs_list)
83 {
84 *vfs_list = next_vfs;
85 wzd_free (current_vfs);
86 } else {
87 wzd_free (current_vfs);
88 previous_vfs->next_vfs = next_vfs;
89 }
90 return 0;
91 }
92
93 previous_vfs = current_vfs;
94 current_vfs = next_vfs;
95 }
96
97 return 2;
98 }
99
100 /** free vfs list */
vfs_free(wzd_vfs_t ** vfs_list)101 int vfs_free(wzd_vfs_t **vfs_list)
102 {
103 wzd_vfs_t * current_vfs, * next_vfs;
104
105 current_vfs = *vfs_list;
106
107 while (current_vfs) {
108 next_vfs = current_vfs->next_vfs;
109
110 wzd_free(current_vfs->virtual_dir);
111 wzd_free(current_vfs->physical_dir);
112 if (current_vfs->target) wzd_free(current_vfs->target);
113
114 #ifdef DEBUG
115 current_vfs->virtual_dir = NULL;
116 current_vfs->physical_dir = NULL;
117 current_vfs->target = NULL;
118 current_vfs->next_vfs = NULL;
119 #endif /* DEBUG */
120 wzd_free(current_vfs);
121
122 current_vfs = next_vfs;
123 }
124
125 *vfs_list = NULL;
126 return 0;
127 }
128
129 /** register a new vfs entry, with a condition */
vfs_add_restricted(wzd_vfs_t ** vfs_list,const char * vpath,const char * path,const char * target)130 int vfs_add_restricted(wzd_vfs_t ** vfs_list, const char *vpath, const char *path, const char *target)
131 {
132 wzd_vfs_t * current_vfs, * new_vfs;
133 fs_filestat_t s;
134
135 current_vfs = *vfs_list;
136 while (current_vfs)
137 {
138 if( (DIRCMP(vpath, current_vfs->virtual_dir)==0) )
139 {
140 /* virtual path already set */
141 return 2;
142 }
143 current_vfs = current_vfs->next_vfs;
144 }
145
146 if (fs_file_stat(path,&s)) {
147 /* destination does not exist */
148 return 1;
149 }
150
151 new_vfs = wzd_malloc(sizeof(wzd_vfs_t));
152 if (!new_vfs) return 1;
153
154 DIRNORM((char*)vpath,strlen(vpath),0);
155 DIRNORM((char*)path,strlen(path),0);
156 new_vfs->virtual_dir = strdup(vpath);
157 new_vfs->physical_dir = strdup(path);
158 if (target)
159 new_vfs->target = strdup(target);
160 else
161 new_vfs->target = NULL;
162 new_vfs->next_vfs = NULL;
163 new_vfs->prev_vfs = NULL;
164
165 current_vfs = *vfs_list;
166
167 if (!current_vfs) {
168 *vfs_list = new_vfs;
169 return 0;
170 }
171
172 while (current_vfs->next_vfs) {
173 current_vfs = current_vfs->next_vfs;
174 }
175
176 current_vfs->next_vfs = new_vfs;
177 new_vfs->prev_vfs = current_vfs;
178
179 return 0;
180 }
181
182 /** register a new vfs entry */
vfs_add(wzd_vfs_t ** vfs_list,const char * vpath,const char * path)183 int vfs_add(wzd_vfs_t ** vfs_list, const char *vpath, const char *path)
184 {
185 return vfs_add_restricted (vfs_list,vpath,path,NULL);
186 }
187
188 /** \return 1 if user match corresponding line */
vfs_match_perm(const char * perms,wzd_user_t * user)189 int vfs_match_perm(const char *perms,wzd_user_t *user)
190 {
191 char * buffer, *token, *ptr;
192 char c;
193 unsigned int i;
194 short negate;
195 wzd_group_t * group;
196
197 if (!perms) return 1;
198 buffer=strdup(perms);
199 ptr=buffer;
200 token = strtok_r(buffer," \t\r\n",&ptr);
201
202 while (token) {
203 negate=0;
204 /* FIXME split token to find entry type : user, group, flag */
205 c = *token++;
206 if (c == '!') {
207 negate = 1;
208 c = *token++;
209 }
210 switch (c) {
211 case '=':
212 if (strcasecmp(token,user->username)==0) { free(buffer); return (negate) ? 0 : 1; }
213 break;
214 case '-':
215 for (i=0; i<user->group_num; i++) {
216 group = GetGroupByID(user->groups[i]);
217 if (strcasecmp(token,group->groupname)==0) { free(buffer); return (negate) ? 0 : 1; }
218 }
219 break;
220 case '+':
221 if (user->flags && strchr(user->flags,*token)) { free(buffer); return (negate) ? 0 : 1; }
222 break;
223 case '*':
224 free(buffer);
225 return !negate;
226 break;
227 default:
228 continue;
229 }
230 if (negate)
231 *(--token)='!';
232
233 token = strtok_r(NULL," \t\r\n",&ptr);
234 }
235
236
237 wzd_free(buffer);
238 return 0;
239 }
240
241 /** if needed, replace the vfs in the path */
vfs_replace(wzd_vfs_t * vfs_list,char * buffer,unsigned int maxlen,wzd_context_t * context)242 int vfs_replace(wzd_vfs_t *vfs_list, char *buffer, unsigned int maxlen, wzd_context_t * context)
243 {
244 char buffer_vfs[2*WZD_MAX_PATH];
245 char * ptr_out;
246 wzd_user_t *user;
247
248 user=GetUserByID(context->userid);
249 if (!user) return -1;
250
251 /* FIXME test length of strings */
252 while (vfs_list)
253 {
254 /* strcpy(buffer_vfs,vfs_list->virtual_dir);*/
255 ptr_out = vfs_replace_cookies(vfs_list->virtual_dir,context);
256 if (!ptr_out) {
257 out_log(LEVEL_CRITICAL,"vfs_replace_cookies returned NULL for %s\n",vfs_list->virtual_dir);
258 vfs_list = vfs_list->next_vfs;
259 continue;
260 }
261 strncpy(buffer_vfs,ptr_out,2*WZD_MAX_PATH); /* FIXME this is slow ! replace by memcpy */
262 wzd_free(ptr_out);
263
264 if (DIRNCMP(buffer_vfs,buffer,strlen(buffer_vfs))==0
265 &&
266 (buffer[strlen(buffer_vfs)] == '/' || /* without this test, vfs will always match before vfs1 */
267 DIRCMP(buffer_vfs,buffer)==0) ) /* without this test, 'cd vfs' will not match */
268 {
269 char buf[2*WZD_MAX_PATH];
270 /* test perm */
271 if (vfs_list->target) {
272 if (!vfs_match_perm(vfs_list->target,user)) { vfs_list = vfs_list->next_vfs; continue; }
273 }
274 #ifdef WZD_DBG_VFS
275 out_err(LEVEL_HIGH,"VPATH match : %s / %s\n",buffer,vfs_list->virtual_dir);
276 #endif
277 strcpy(buf,vfs_list->physical_dir);
278 strcpy(buf+strlen(vfs_list->physical_dir),buffer+strlen(buffer_vfs));
279 #ifdef WZD_DBG_VFS
280 out_err(LEVEL_HIGH,"converted to %s\n",buf);
281 #endif
282 strcpy(buffer,buf);
283 }
284 vfs_list = vfs_list->next_vfs;
285 }
286 return 0;
287 }
288
289 /** parse vfs entry and replace cookies by their value
290 * \return a newly allocated string with the interpreted path
291 * \todo TODO it would REALLY be nice to use the function defined in
292 * wzd_cookie_lex.l ... problem: it automatically prints the result !
293 */
vfs_replace_cookies(const char * path,wzd_context_t * context)294 char * vfs_replace_cookies(const char * path, wzd_context_t * context)
295 {
296 char buffer[2*WZD_MAX_PATH];
297 size_t length, needed;
298 char * out=NULL;
299 const char * ptr_in;
300 char * ptr_out;
301 wzd_user_t * user;
302 wzd_group_t * group;
303
304 user = GetUserByID(context->userid);
305
306 if (!user) return NULL;
307
308 if (user->group_num > 0) {
309 group = GetGroupByID(user->groups[0]);
310 } else
311 group = NULL;
312
313 length = 0;
314 ptr_in = path; ptr_out = buffer;
315 while ( (*ptr_in) ){
316 if (length >= 2*WZD_MAX_PATH) {
317 out_log(LEVEL_CRITICAL,"buffer size exceeded in vfs_replace_cookies for virtual_dir %s\n",path);
318 return NULL;
319 }
320 if (*ptr_in == '%') {
321 if (strncmp(ptr_in,"%username",9)==0) { /* 9 == strlen(%username) */
322 needed = strlen(user->username);
323 length += needed;
324 if (length >= 2*WZD_MAX_PATH) {
325 out_log(LEVEL_CRITICAL,"buffer size exceeded in vfs_replace_cookies for virtual_dir %s\n",path);
326 return NULL;
327 }
328 memcpy(ptr_out,user->username,needed);
329 ptr_in += 9; /* 9 == strlen(%username) */
330 ptr_out += needed;
331 } else if (strncmp(ptr_in,"%usergroup",10)==0) { /* 10 == strlen(%usergroup) */
332 if (group) {
333 needed = strlen(group->groupname);
334 length += needed;
335 if (length >= 2*WZD_MAX_PATH) {
336 out_log(LEVEL_CRITICAL,"buffer size exceeded in vfs_replace_cookies for virtual_dir %s\n",path);
337 return NULL;
338 }
339 memcpy(ptr_out,group->groupname,needed);
340 ptr_in += 10; /* 10 == strlen(%usergroup) */
341 ptr_out += needed;
342 } else { /* ! group */
343 return NULL; /* we want user's main group and he has no one ... */
344 }
345 } else if (strncmp(ptr_in,"%userhome",9)==0) { /* 9 == strlen(%userhome) */
346 /* TODO XXX FIXME only print iff homedir exists !! */
347 #if 0
348 if (home) {
349 #endif /* 0 */
350 needed = strlen(user->rootpath);
351 length += needed;
352 if (length >= 2*WZD_MAX_PATH) {
353 out_log(LEVEL_CRITICAL,"buffer size exceeded in vfs_replace_cookies for virtual_dir %s\n",path);
354 return NULL;
355 }
356 memcpy(ptr_out,user->rootpath,needed);
357 ptr_in += 9; /* 9 == strlen(%userhome) */
358 ptr_out += needed;
359 } else { /* ! home */
360 return NULL; /* we want user's main home and he has no one ... */
361 }
362 #if 0
363 } else {
364 *ptr_out++ = *ptr_in++;
365 length++;
366 }
367 #endif /* 0 */
368 } else {
369 *ptr_out++ = *ptr_in++;
370 length++;
371 }
372 }
373 *ptr_out = '\0';
374
375 out = wzd_malloc(length+1);
376 strncpy(out,buffer,length+1);
377
378 return out;
379 }
380
381 /*************** checkpath ***************************/
382
stripdir(const char * dir,char * buf,int maxlen)383 char *stripdir(const char * dir, char *buf, int maxlen)
384 {
385 const char * in;
386 char * out;
387 char * last;
388 int ldots;
389
390 in = dir;
391 out = buf;
392 last = buf + maxlen;
393 ldots = 0;
394 *out = 0;
395
396 #ifndef WIN32
397 if (*in != '/')
398 #else
399 if (*in != '/' && *(in+1) != ':')
400 #endif
401 {
402 if (getcwd(buf, maxlen - 2) ) {
403 out = buf + strlen(buf) - 1;
404 if (*out != '/') *(++out) = '/';
405 out++;
406 }
407 else
408 return NULL;
409 }
410
411 while (out < last) {
412 *out = *in;
413
414 if (*in == '/')
415 {
416 while (*(++in) == '/') ;
417 in--;
418 }
419
420 if (*in == '/' || !*in)
421 {
422 if (ldots == 1 || ldots == 2) {
423 if (!*in) {
424 if (out-ldots<=dir || *(out-ldots-1) != '/') /** \bug XXX FIXME pointers out and dir are NOT in the same buffers */
425 ldots = 0;
426 }
427 while (ldots > 0 && --out > buf)
428 {
429 if (*out == '/')
430 ldots--;
431 }
432 *(out+1) = 0;
433 }
434 ldots = 0;
435
436 } else if (*in == '.') {
437 ldots++;
438 } else {
439 ldots = 0;
440 }
441
442 out++;
443
444 if (!*in)
445 break;
446
447 in++;
448 }
449
450 if (*in) {
451 errno = ENOMEM;
452 return NULL;
453 }
454
455 while (--out != buf && (*out == '/' || !*out)) *out=0;
456 return buf;
457 }
458
459 /** \brief convert ftp-style path to system path
460 * \deprecated use \ref checkpath_new
461 */
checkpath(const char * wanted_path,char * path,wzd_context_t * context)462 int checkpath(const char *wanted_path, char *path, wzd_context_t *context)
463 {
464 char *allowed;
465 char *cmd;
466
467 allowed = malloc(WZD_MAX_PATH);
468 cmd = malloc(WZD_MAX_PATH);
469
470 {
471 snprintf(allowed,WZD_MAX_PATH,"%s/",GetUserByID(context->userid)->rootpath);
472 if (strcmp(allowed,"//")==0) allowed[1]='\0';
473 snprintf(cmd,WZD_MAX_PATH,"%s%s",GetUserByID(context->userid)->rootpath,context->currentpath);
474 }
475 if (cmd[strlen(cmd)-1] != '/')
476 strcat(cmd,"/");
477 if (wanted_path) {
478 if (wanted_path[0]!='/') {
479 strlcat(cmd,wanted_path,WZD_MAX_PATH);
480 } else {
481 strcpy(cmd,allowed);
482 strlcat(cmd,wanted_path+1,WZD_MAX_PATH);
483 }
484 }
485 DIRNORM(cmd,strlen(cmd),0);
486 /*#ifdef DEBUG
487 printf("Checking path '%s' (cmd)\nallowed = '%s'\n",cmd,allowed);
488 #endif*/
489 /* if (!realpath(cmd,path)) return 1;*/
490 if (!stripdir(cmd,path,WZD_MAX_PATH)) { free(allowed); free(cmd); return 1; }
491 /*#ifdef DEBUG
492 printf("Converted to: '%s'\n",path);
493 #endif*/
494 if (path[strlen(path)-1] != '/')
495 strcat(path,"/");
496 strcpy(cmd,path);
497 cmd[strlen(allowed)]='\0';
498 if (path[strlen(cmd)-1] != '/')
499 strcat(cmd,"/");
500 /* check if user is allowed to even see the path */
501 if (DIRNCMP(cmd,allowed,strlen(allowed))) { free(allowed); free(cmd); return 1; }
502 /* in the case of VFS, we need to convert here to a realpath */
503 vfs_replace(mainConfig->vfs,path,WZD_MAX_PATH,context);
504 if (strlen(path)>1 && path[strlen(path)-1] == '/') path[strlen(path)-1]='\0';
505 free(allowed);
506 free(cmd);
507 return 0;
508 }
509
510 /* FIXME: does not yet support vfs */
path_abs2rel(const char * abs,char * rel,int rel_len,wzd_context_t * context)511 int path_abs2rel(const char *abs, char *rel, int rel_len, wzd_context_t *context)
512 {
513 const char *ptr;
514 wzd_user_t * user;
515 wzd_vfs_t * vfs;
516 char buffer[2*WZD_MAX_PATH];
517
518 user = GetUserByID(context->userid);
519 if (!user) return E_USER_IDONTEXIST;
520
521 strncpy(buffer,abs,2*WZD_MAX_PATH);
522
523 vfs = mainConfig->vfs;
524 if (vfs) {
525 while (vfs->next_vfs) vfs = vfs->next_vfs;
526
527 /** \todo XXX FIXME this code is NOT finished ... */
528 if (strncmp(buffer,vfs->physical_dir,strlen(vfs->physical_dir)) == 0) {
529
530 }
531 }
532
533 if (strncmp(buffer,user->rootpath,strlen(user->rootpath))) /* VFS */
534 return 1;
535
536 ptr = buffer + strlen(user->rootpath);
537 strncpy(rel,ptr,rel_len);
538
539 return 0;
540 }
541
542 /** converts wanted_path (in ftp-style) to path (system path), checking
543 * for errors and permissions
544 *
545 * \param wanted_path The path in FTP-form
546 * \param path MUST have a minimum size of WZD_MAX_PATH
547 * \param context The current context
548 *
549 * If the return is 0, then we are SURE the result exists.
550 * If the real path points to a directory, then the result is / terminated
551 */
checkpath_new(const char * wanted_path,char * path,wzd_context_t * context)552 int checkpath_new(const char *wanted_path, char *path, wzd_context_t *context)
553 {
554 int ret;
555 char * ftppath, *syspath, *ptr, *lpart, *rpart;
556 char * ptr_ftppath;
557 wzd_user_t * user;
558 unsigned int sys_offset;
559 fs_filestat_t s;
560 struct wzd_file_t * perm_list, * entry;
561 fs_dir_t * dir;
562
563 WZD_ASSERT(context != NULL);
564 if (context == NULL) return E_USER_IDONTEXIST;
565
566 if (!wanted_path) return E_PARAM_NULL;
567
568 if (strlen(context->currentpath) == 0) return E_PARAM_INVALID;
569
570 user = GetUserByID(context->userid);
571
572 if (!user) return E_USER_IDONTEXIST;
573 if (strlen(user->rootpath) + strlen(wanted_path) >= WZD_MAX_PATH) return E_PARAM_BIG;
574
575 ftppath = malloc(WZD_MAX_PATH+1);
576 syspath = malloc(WZD_MAX_PATH+1);
577
578 #ifdef WIN32
579 if (strchr(user->flags,FLAG_FULLPATH) ) memset(syspath,0,sizeof(syspath));
580 else
581 #endif
582 {
583 wzd_strncpy(syspath, user->rootpath, WZD_MAX_PATH);
584 sys_offset = strlen(syspath);
585 }
586
587 /* if wanted_path is relative */
588 if (wanted_path[0] != '/') {
589
590 wzd_strncpy(ftppath, context->currentpath, WZD_MAX_PATH);
591 ptr_ftppath = ftppath + strlen(ftppath) - 1;
592 if (*ptr_ftppath != '/') {
593 *++ptr_ftppath = '/';
594 *++ptr_ftppath = '\0';
595 }
596 if (ptr_ftppath == ftppath) ptr_ftppath++; /* ftppath is / */
597 strcpy(ptr_ftppath, wanted_path);
598 if (strncmp(ftppath,"/../",4)==0) {
599 free(syspath); free(ftppath);
600 return E_WRONGPATH;
601 }
602
603 path_simplify(ftppath);
604
605 ret = checkpath_new(ftppath, syspath, context);
606 if (!ret || ret == E_FILE_NOEXIST)
607 wzd_strncpy(path, syspath, WZD_MAX_PATH);
608 free(syspath); free(ftppath);
609 return ret;
610
611 /** \bug the following will never be executed */
612 sys_offset = strlen(syspath);
613 /* remove trailing / */
614 if (syspath[sys_offset-1] == '/' && sys_offset > 2)
615 syspath[--sys_offset] = '\0';
616 } else { /* wanted_path is absolute */
617 wzd_strncpy(ftppath, wanted_path, WZD_MAX_PATH);
618
619 path_simplify(ftppath); /** \todo check that \ref path_simplify works as expected */
620 }
621
622 /* here we assume syspath contains the user's homedir
623 * syspath is not / terminated (for now)
624 */
625 ptr_ftppath = ftppath;
626 if (*ptr_ftppath == '/')
627 ptr_ftppath++;
628
629 #ifdef WIN32
630 if (strchr(user->flags,FLAG_FULLPATH) ) sys_offset=0;
631 else
632 #endif
633 {
634 if (syspath[sys_offset-1] != '/')
635 memcpy(&syspath[sys_offset++],"/\0",2); /*use either strcat or memcpy with terminating 0 or corruption can occur*/
636 }
637
638 while (ptr_ftppath[0] != '\0')
639 {
640 /* start from the top-level dir */
641 lpart = ptr_ftppath;
642 ptr = strchr(lpart,'/');
643 if (!ptr) {
644 ptr = lpart + strlen(lpart); /* position of \0 */
645 }
646
647 if (!ptr || ptr <= lpart)
648 {
649 /* we have finished ? */
650
651 wzd_strncpy(path, syspath, WZD_MAX_PATH);
652 free(ftppath);
653 free(syspath);
654 return 0;
655 }
656 if (*ptr == '\0')
657 rpart = ptr; /* if empty, point to the last 0 */
658 else
659 rpart = ptr+1;
660 *ptr = '\0';
661
662 /* out_err(LEVEL_INFO," %s | %s\n",lpart,rpart);*/
663
664 strcpy(syspath+sys_offset, lpart);
665
666 /** \todo check permissions here */
667 if (fs_file_lstat(syspath,&s)) {
668 /* file/dir does not exist
669 * 3 cases: error, vfs, symlink */
670
671 /* read permission file for parent */
672 strcpy(syspath+sys_offset, HARD_PERMFILE);
673 perm_list = NULL;
674 ret = readPermFile(syspath, &perm_list);
675 syspath[sys_offset] = '\0';
676
677 ret = 1;
678 /* check for symlink */
679 for (entry=perm_list; entry; entry = entry->next_file)
680 {
681 if (entry->kind == FILE_LNK && strcmp(lpart,entry->filename) == 0)
682 {
683 /* bingo, symlink */
684 /* we overwrite syspath ! */
685 if ( ((char*)entry->data)[0] == '/'
686 #ifdef WIN32
687 || ((char*)entry->data)[1] == ':'
688 #endif
689 )
690 { /* symlink target is absolute */
691 strncpy(syspath, (char*)entry->data, WZD_MAX_PATH);
692 sys_offset = strlen(syspath);
693 ret = 0;
694 break;
695 }
696 }
697 }
698
699 free_file_recursive(perm_list);
700
701 if (ret) { /* not a symlink, check for VFS */
702 /* XXX add vfs entries */
703 char * buffer_vfs = wzd_malloc(WZD_MAX_PATH+1);
704 char * ptr;
705 wzd_vfs_t * vfs = mainConfig->vfs;
706
707 while (vfs)
708 {
709 ret = 1;
710 ptr = vfs_replace_cookies(vfs->virtual_dir,context);
711 if (!ptr) {
712 out_log(LEVEL_CRITICAL,"vfs_replace_cookies returned NULL for %s\n",vfs->virtual_dir);
713 vfs = vfs->next_vfs;
714 continue;
715 }
716 strncpy(buffer_vfs,ptr,WZD_MAX_PATH);
717 wzd_free(ptr);
718 /** \bug this comparison is false */
719 if (DIRNCMP(buffer_vfs,syspath,strlen(syspath))==0)
720 { /* ok, we have a candidate. Now check if user is allowed to see it */
721 if (!vfs_match_perm(vfs->target,user)) { vfs = vfs->next_vfs; continue; }
722 ptr = buffer_vfs + strlen(syspath);
723 /* bingo, vfs */
724 /* we overwrite syspath ! */
725 if ( strchr(ptr,'/')==NULL && !DIRCMP(lpart,ptr) ) { /* not a subdir and same name */
726 strncpy(syspath, vfs->physical_dir, WZD_MAX_PATH);
727 sys_offset = strlen(syspath);
728 ret = 0;
729 break;
730 }
731 }
732
733 vfs = vfs->next_vfs;
734 } /* while (vfs) */
735
736 wzd_free(buffer_vfs);
737 } /* check for vfs entries */
738
739 /* even if found, check the new destination exists */
740 if (ret || fs_file_lstat(syspath,&s)) { /* this time, it is really not found */
741 if (!rpart || *rpart=='\0') {
742 /* we return the 'what it would have been' path anyway, so it can be used */
743 strcpy(syspath+sys_offset, lpart);
744 wzd_strncpy(path, syspath, WZD_MAX_PATH);
745 ret = E_FILE_NOEXIST;
746 } else {
747 ret = E_WRONGPATH;
748 }
749 free(ftppath);
750 free(syspath);
751 return ret;
752 }
753
754 } else {
755 /* existing file/dir */
756 sys_offset += strlen(lpart);
757 } /* stat */
758
759 /* 3 possibilities:
760 * - regular directory
761 * - symlink (on filesystem)
762 * - file
763 */
764 if (S_ISDIR(s.mode) || S_ISLNK(s.mode)) {
765 if (syspath[sys_offset-1] != '/')
766 memcpy(&syspath[sys_offset++],"/\0",2); /*use either strcat or memcpy with terminating 0 or corruption can occur*/
767 if (_checkFileForPerm(syspath,".",RIGHT_CWD,user)) {
768 /* no permissions ! */
769 free(ftppath);
770 free(syspath);
771 return E_NOPERM;
772 }
773 } else {
774 /* we've ended up with a file, and we want a directory or link... ignore & continue loop */
775 }
776
777
778 /* loop */
779 ptr_ftppath = rpart;
780 }
781
782 /* check to see if the file system allows us access to the returned path */
783 if (fs_dir_open(syspath,&dir)) {
784 free(syspath);
785 free(ftppath);
786 return E_NOPERM;
787 } else fs_dir_close(dir);
788
789 wzd_strncpy(path, syspath, WZD_MAX_PATH);
790 free(ftppath);
791 free(syspath);
792 return 0;
793 }
794
795 /** Tests a path system path, checking
796 * for errors and permissions
797 *
798 * \param trial_path The path in system-form
799 * \param context The user context
800 *
801 * If the return is 0, then we are SURE the result exists.
802 * If the real path points to a directory, then the it must be / terminated
803 *
804 * Can be used after checkpath_new generates a system path if you wish to
805 * recheck whether the file/dir still or now exists
806 */
807
test_path(const char * trial_path,wzd_context_t * context)808 int test_path(const char *trial_path, wzd_context_t *context)
809 {
810 wzd_user_t * user;
811 unsigned int trial_offset;
812 fs_filestat_t s;
813
814 /* check that we have a valid user, otherwise we can't check permissions */
815 user = GetUserByID(context->userid);
816
817 if (!user) return E_USER_IDONTEXIST;
818
819 if (fs_file_lstat(trial_path,&s)) {
820 /* test failed, file does not exist */
821 return E_FILE_NOEXIST;
822 }
823 else {
824 /* 3 possibilities:
825 * - regular directory
826 * - symlink (on filesystem)
827 * - file
828 */
829 if (S_ISDIR(s.mode) || S_ISLNK(s.mode)) {
830 trial_offset = strlen(trial_path);
831 /* check th */
832 if (trial_path[trial_offset-1] != '/') {
833 return E_WRONGPATH;
834 }
835
836 if (_checkFileForPerm(trial_path,".",RIGHT_CWD,user)) {
837 /* no permissions ! */
838
839 return E_NOPERM;
840 }
841 }
842
843 }
844
845 /* its all good */
846 return 0;
847 }
848
849
killpath(const char * path,wzd_context_t * context)850 int killpath(const char *path, wzd_context_t * context)
851 {
852 char * test_realpath;
853 int found = 0;
854 wzd_user_t * me, * user;
855 size_t length;
856
857 if (!path) return E_FILE_NOEXIST;
858
859 length = strlen(path);
860 test_realpath = malloc(WZD_MAX_PATH+1);
861
862 me = GetUserByID(context->userid);
863 WZD_ASSERT( me != NULL );
864 if (checkpath_new(context->currentpath,test_realpath,context)) {
865 free(test_realpath);
866 return E_USER_IDONTEXIST;
867 }
868 #if 0
869 /* preliminary check: i can't kill myself */
870 if (strncmp(path,test_realpath,length)==0) {
871 free(test_realpath);
872 return E_USER_ICANTSUICIDE;
873 }
874 #endif
875
876 /* kill'em all ! */
877 {
878 ListElmt * elmnt;
879 wzd_context_t * ctxt;
880 for (elmnt=list_head(context_list); elmnt!=NULL; elmnt=list_next(elmnt)) {
881 ctxt = list_data(elmnt);
882 if (ctxt->magic == CONTEXT_MAGIC) {
883 user = GetUserByID(ctxt->userid);
884 WZD_ASSERT( user != NULL );
885 if (ctxt->userid == context->userid) { continue; } /* no suicide */
886 if (checkpath_new(ctxt->currentpath,test_realpath,ctxt) == 0) {
887 if (strncmp(path,test_realpath,length)==0) {
888 found++;
889 kill_child_new(ctxt->pid_child,context);
890 }
891 }
892 }
893 } /* for all contexts */
894 }
895
896 free(test_realpath);
897
898 if (!found) return E_USER_NOBODY;
899
900 return E_OK;
901 }
902
903
904
905
906
907