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