/* See the file wizshell.c, wizshell.h or the files in docs/ for information */ #include "wizshell.h" /* tokenlen * returns length of string to '/' or '\0' */ int tokenlen(token) char *token; { int i=0; /* v-- is this needed? */ while(token[i]!='/' && token[i]!='\0') i++; return i; } /* match_token * see if tok1 matches the tok2 from access tree */ int match_token(tok1,tok2) char *tok1,*tok2; { if(!strcmp(tok1,tok2)) return 1; if(!strcmp(tok2,"*")) return 1; /* add pattern matching later?? */ return 0; } /* find_token * finds token in down level from acc or returns NULL */ acc_t *find_token(acc,token) acc_t *acc; char *token; { acc=acc->down; while((acc!=NULL) && (!match_token(token,acc->token))) acc=acc->next; return acc; } /* add_token * finds or adds a token in down level from acc and returns it * replacements: $<var>->environment variable * $JUBAL.o will replace the variable $JUBAL, not $JUBAL.o <- not yet.. :) * (i.e. if $JUBAL=moo, then $JUBAL.o becomes moo.o) */ acc_t *add_token(acc,token) acc_t *acc; char *token; { int len; acc_t *tmp; char *str; /* fprintf(stderr,"Adding token: %s\n",token); /* */ tmp=find_token(acc,token); /* if(tmp) fprintf(stderr," -- found it.\n"); */ if(tmp) return tmp; /* Deal with environment variables */ if(token[0]=='$' && (str=getenv(token+1))) token=str; MK_ACC(tmp); tmp->next=acc->down; acc->down=tmp; tmp->down=NULL; len=strlen(token); MK_STR(tmp->token,len) strncpy(tmp->token,token,len+1); /* fprintf(stderr," -- added: %s\n",tmp->token); /* */ return tmp; } /* Prints out access tree (for debugging) (maybe as a catch later?) */ void print_acc(acc,spc) acc_t *acc; int spc; { acc_t *tmp,*dis; int i; if(!spc) { if(acc==write_acc || acc==read_acc) { if(acc==write_acc) fprintf(stderr,"Write Access Tree:\n"); else fprintf(stderr,"Read Access Tree:\n"); print_acc(acc->down,2); if(acc->next->down) { fprintf(stderr,"excluding:\n"); print_acc(acc->next->down,2); } return; } fprintf(stderr,"Access Tree\n"); } tmp=acc; while(tmp) { for(i=0;i<spc;i++) fprintf(stderr," "); fprintf(stderr,"/%s\n",tmp->token); if(tmp->down) print_acc(tmp->down,spc+strlen(tmp->token)+1); tmp=tmp->next; } } /* Frees all tokens down and next of acc */ /* Recursive because we have a tree to traverse */ void free_acc(acc) acc_t *acc; { if(!acc) return; if(acc->down) free_acc(acc->down); if(acc->next) free_acc(acc->next); free(acc); } /* adds path in down level of acc */ /* returns the last token added (useful for $var expansion) */ acc_t *add_path(acc,path) acc_t *acc; char *path; { char buf[MAX_BUF]; char *tmp; int i=0; if(path[0]=='/') path++; while(*path!='\0') { i=0; while(*path!='\0' && *path!='/') buf[i++]=*(path++); buf[i]='\0'; if(*path=='/') path++; acc=add_token(acc,buf); } /* Sometimes (because of groups) we might add /path1/path2/path3 and then * /path1 - if we do that we want to get rid of the rest of the path * that's there, otherwise we only get access to /path1/path2/path3 */ if(acc->down) { free_acc(acc->down); acc->down=NULL; } return acc; } /* get next full path in access file, return 0 if another * access type entry is found. If path is of the form $<str> then * the value of the environment variable <str> will be added * If the path is of the form +<str> then the access type <str> is added. */ int get_next_path(acc,fp,fname) acc_t *acc; FILE *fp; char *fname; { char buf[MAX_BUF+1],*str; acc_t *tmp=acc; int i=0,nottoken=1,c; while(nottoken) { while((c=getc(fp))==' ' || c=='\t' || c=='\n'); /* eat white space */ if(c=='#') while((c=getc(fp))!='\n' && c!=EOF); /* eat comments */ else nottoken=0; } while(c!=' ' && c!='\t' && c!='\n' && c!=EOF) { buf[i++]=c; c=getc(fp); } buf[i]='\0'; if(i==0) return 0; if(buf[i-1]==':') return 0; /* we shouldn't need this, but I couldn't get it to work properly in add_path */ if(buf[0]=='$' && (str=getenv(buf+1))) add_path(acc,str); else /* figure this out later.. :( */ if(buf[0]=='+') get_acc(&acc,fname,buf+1); else if(buf[0]=='-') add_path(acc->next,buf+1); else add_path(acc,buf); if(c==EOF) return 0; return 1; } /* get_acc * reads access file <fname> and loads access of type <type> into <acc> * Creates two handles of type acc_t, the main handle, and the one ->next * from it, which are the disallowed directories. */ void get_acc(acch,fname,type) acc_t **acch; char *fname; char *type; { FILE *fp; acc_t *acc; if(!(fp=fopen(fname,"r"))) { fprintf(stdout,"Could not open access file: %s",fname); exit(1); } rewind(fp); if (!find_entry(type,fp)) { /* use find_entry from login.c */ fprintf(stderr,"Could not find access type: %s\n",type); fprintf(stderr,"In access file: %s",fname); exit(1); } if(!*acch) { MK_ACC(acc) MK_ACC((acc->next)) /* for disallowed directories */ (*acch)=acc; } while(get_next_path(*acch,fp,fname)); /* print_acc((*acch),0); */ fclose(fp); return; } /* mk_path * things that are not implemented (yet): * str="." * wildcards in string? shouldn't be a problem... * frees string and returns new one */ char *mk_path(str) char *str; { char *arg; char *hmdir,*user,*tmp,c; /* to turn off mk_path for debugging (don't use!): MK_STR(arg,strlen(str)) str_add(arg,str); return gen_abs_path(arg); */ switch (str[0]) { case '-': return str; break; case '/': MK_STR(arg,ROOT_DIR_LEN+strlen(str)) str_add(arg,ROOT_DIR); str_add(arg,str); break; case '~': if (str[1]=='/' || str[1]=='\0') { MK_STR(arg,ROOT_DIR_LEN+strlen(envir.home)+strlen(str+1)) str_add(arg,ROOT_DIR); str_add(arg,envir.home); str_add(arg,str+1); } else { user=str+1; tmp=user; while(*tmp!='/' && *tmp!='\0') tmp++; c=*tmp; *tmp='\0'; /* temporarily null terminate the user name */ hmdir=get_home_dir(user); if(!hmdir) { fprintf(stderr,"Unknown user: %s.\n",user); *tmp=c; return 0; } *tmp=c; MK_STR(arg,ROOT_DIR_LEN+strlen(hmdir)+strlen(tmp)+5) str_add(arg,ROOT_DIR); str_add(arg,"/"); str_add(arg,hmdir); str_add(arg,tmp); /* there was a seg fault here.. should be okay now */ FREE(hmdir); } break; default: MK_STR(arg,ROOT_DIR_LEN+strlen(WD)+1+strlen(str)) str_add(arg,ROOT_DIR); if(*(WD+1)!='\0') str_add(arg,WD); /* not if WD->"/\0" */ str_add(arg,"/"); str_add(arg,str); break; } FREE(str); arg=gen_abs_path(arg); if(DEBUGON) if(arg) fprintf(stderr,"ABS_FILE: %s\n",arg); return arg; } /* gen_abs_path * take care of /../ and /./ and // and last / on string * States: * state=0 not in possible "/../" or "/./" * state=1 encountered "/" (rewind back 1 if next char='/' or '\0') * state=2 encountered "/." (rewind back 2 if next char='/' or '\0') * state=3 encountered "/.." (rewind back a pathstep if next='/' or '\0') * if RESOLVE_LINKS is defined, then the REAL absolute path will be found * allows that the last token of the pathname not exist (for creating files) */ #ifdef RESOLVE_LINKS /* Resolve links - follow links to create REAL absolute path * if the ROOT_DIR is linked to somewhere else, and a path component * makes a root path which!= ROOT_DIR, then access will be denied */ char *gen_abs_path(arg) char *arg; { int DEBUG=0; char *tmp,*before,*after,*newarg; char link[MAX_PATH]; int lasttoken=0; struct stat status; int result,len,loop=0; tmp=arg+ROOT_DIR_LEN-1; while(*tmp!='\0') { if(loop++>100) { fprintf(stderr,"too many loops in gen_abs()\n");return 0;} if (DEBUG) fprintf(stderr,"while (arg:tmp): %s:%s\n",arg,tmp); if(*(tmp+1)=='.' && *(tmp+2)=='.' && (*(tmp+3)=='/' || *(tmp+3)=='\0')) { before=tmp; while(before>=arg && *(--before)!='/'); if(before<arg+ROOT_DIR_LEN-1) { fprintf(stderr,"Can't .. before root directory\n"); return 0; } before++; if(*(tmp+3)=='\0' || (*(tmp+3)=='/' && *(tmp+4)=='\0')) { *before='\0'; lasttoken=1; } else strcpy(before,tmp+4); tmp=before-1; if (DEBUG) fprintf(stderr,"REWOUND: %s\n",arg); } else if(*(tmp+1)=='.' && *(tmp+2)=='/') { strcpy(tmp+1,tmp+3); } else if(*(tmp+1)=='.' && *(tmp+2)=='\0') { *tmp='\0'; lasttoken=1; } else if(*(tmp+1)=='/') { strcpy(tmp+1,tmp+2); } else if(*(tmp+1)=='\0') { *tmp='\0'; lasttoken=1; } else { before=tmp+1; while(*(++tmp)!='/' && *tmp!='\0'); /* go to next '/' */ if(*tmp=='\0') lasttoken=1; else lasttoken=0; *tmp='\0'; if (DEBUG) fprintf(stderr,"Checking for link: %s\n",arg); if((result=lstat(arg,&status))==-1) { if(errno==ENOENT) { if(lasttoken) return arg; /* allow last token to not exist */ else fprintf(stderr,"%s: Path not found.\n",arg+ROOT_DIR_LEN);} else if(errno==ENOTDIR) fprintf(stderr,"Directory doesn't exist\n"); else if(errno==EACCES) fprintf(stderr,"%s unreadable\n",arg+ROOT_DIR_LEN);else if(errno==ELOOP) fprintf(stderr,"Too many symbolic links\n"); else if(errno==ENAMETOOLONG) fprintf(stderr,"Filename too long\n"); else fprintf(stderr,"Error in reading path: %s\n",arg+ROOT_DIR_LEN); FREE(arg); return 0; } else if((status.st_mode & S_IFMT)==S_IFLNK) { if (DEBUG) fprintf(stderr,"LINK!\n"); result=readlink(arg,link,MAX_PATH); if(result==-1) { if(errno==ENOENT) fprintf(stderr,"file does not exist\n"); else fprintf(stderr,"error in reading link: %s\n",arg+ROOT_DIR_LEN); FREE(arg); return 0; } if(link[result-1]!='/') link[result++]='/'; link[result]='\0'; if (DEBUG) fprintf(stderr,"LINK TO: %s\n",link); if(link[0]=='/') { /* absolute link */ if(strncmp(link,ROOT_DIR,ROOT_DIR_LEN-1)) { fprintf(stderr,"link to outside root directory\n"); FREE(arg); return 0; } MK_STR(newarg,result+(1-lasttoken)*strlen(tmp+1)+2) str_add(newarg,link); if(!lasttoken) str_add(newarg,tmp+1); FREE(arg); arg=newarg; tmp=arg+ROOT_DIR_LEN-1; } else { /* not absolute link */ *before='\0'; len=before-arg; MK_STR(newarg,len+result+(1-lasttoken)*strlen(tmp+1)+3) str_add(newarg,arg); str_add(newarg,link); if(!lasttoken) str_add(newarg,tmp+1); FREE(arg); arg=newarg; tmp=arg+len-1; } if (DEBUG) fprintf(stderr,"Resolved link: %s\n",arg); } if(!lasttoken) *(tmp)='/'; } } /* end while */ if(*(tmp-1)=='/') *(tmp-1)='\0'; return arg; } #else /* #ifdef RESOLVE_LINKS */ /* Don't resolve links, just clean up pathname string - NOT SECURE, FASTER */ char *gen_abs_path(arg) char *arg; { int i,state=0; char *start,*a,*b; a=b=start=arg+ROOT_DIR_LEN-1; do { *b=*a; switch (state) { case 0: if(*a=='/') state=1; break; case 1: if(*a=='/' || *a=='\0') b-=1; /* "//" encountered, stay in state 1 */ else if(*a=='.') state=2; else state=0; break; case 2: if(*a=='.') state=3; else if(*a=='/' || *a=='\0') {state=1; b-=2;} /* encountered "/./" */ else state=0; break; case 3: if(*a=='/' || *a=='\0') { b-=4; while(b>=start&&*b!='/') b--; if(b<start) b=start; /* rewound past start */ state=1; } else state=0; break; default: FATAL("FATAL ERROR: impossible state in gen_abs_path") break; } /* end switch */ } while(*(a++)!='\0' && *(b++)!=0); if(b==start) *(b++)='/'; *b='\0'; return arg; } #endif /* #ifdef RESOLVE_LINKS */ /* ref_from * Takes the absolute path from gen-abs_path and then * references it from either the WD or the ROOT or both * depending upon which of (FROM_WD, FROM_ROOT, HYBRID) * is defined * returns new string, does not free abs */ char *ref_from(abs) char *abs; { char *tmp; if(*abs=='-') return abs; #ifdef FROM_WD fprintf(stderr,"FROM_WD not available yet.\n"); #endif /* FROM_WD */ #ifdef HYBRID fprintf(stderr,"HYBRID not available yet.\n"); #endif /* HYBRID */ #ifdef FROM_ROOT MK_STR(tmp,strlen(abs+ROOT_DIR_LEN)+1) strcpy(tmp,abs+ROOT_DIR_LEN); return tmp; #endif /* FROM_ROOT */ fprintf(stderr, "You must choose to define FROM_WD or FROM_ROOT in config.c\n"); return 0; } /* see if path is valid in acc tree */ int valid_path(acc,path) acc_t *acc; char *path; { char buf[MAX_BUF]; acc_t *tmp; int i; if(*path=='/' && *(path+1)=='\0') /* accessing "/" */ if((acc=find_token(acc,"*")) && !acc->down) return 1; else return 0; do { i=0; while(((buf[i]=*path)!='\0') && (*path!='/')) { /*copy 1st token*/ path++; i++; } buf[i]='\0'; if(*path!='\0') path++; /* point after '/' */ } while (buf[0]!='\0' && acc->down && (acc=find_token(acc,buf))); if(acc && !acc->down) return 1; return 0; } /* ck_access ("access" is already defined somewhere...) */ int ck_access(comm,file_args) char **comm; int *file_args; { char *path,num[10]; int i=1,num_file_args=0; acc_t *acc; while(comm[i]) { if(!file_args[i]) { i++; continue; } path=comm[i]+ROOT_DIR_LEN; if(*(path-1)=='\0') path="/"; /* need to null terminate? */ sprintf(num,"%d",i); if(DEBUGON)fprintf(stderr,"ck_access %d [%s]\n",i,comm[i]); if(!valid_path(write_acc,path) || (write_acc->next->down && valid_path(write_acc->next,path))) { if(!(acc=find_token(can_read,comm[0])) || (acc->down && !find_token(acc,num))) { ERROR(path,"permission denied [write_access]"); return 0; } if(!valid_path(read_acc,path) || (read_acc->next->down && valid_path(read_acc->next,path))) { ERROR(path,"permission denied [read_access]"); return 0; } } i++; } return 1; }