Back to index

wims  3.65+svn20090927
chroot.c
Go to the documentation of this file.
00001 /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
00002  *
00003  *  This program is free software; you can redistribute it and/or modify
00004  *  it under the terms of the GNU General Public License as published by
00005  *  the Free Software Foundation; either version 2 of the License, or
00006  *  (at your option) any later version.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program; if not, write to the Free Software
00015  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017 
00018        /* Change root and exec */
00019 
00020 #define PROC_QUOTA 15
00021 #define UID_MIN 10000
00022 #define UID_MASK 127
00023 #define CPU_MAX 4096
00024 #define TIME_MASK 16383
00025 #define CLEAN_DELAY 100
00026 #define CLEAN_DELAY2 500000
00027 #define MAX_PARMLEN 16384
00028 #define MAX_OUTLEN  65536
00029 #define chroot_tmp "../chroot/tmp/sessions"
00030 #define chroot_path "/bin:/usr/bin:/usr/local/bin"
00031 #define timestamp "../tmp/log/chroot.stamp"
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <errno.h>
00036 #include <fcntl.h>
00037 #include <unistd.h>
00038 #include <dirent.h>
00039 #include <string.h>
00040 #include <time.h>
00041 #include <signal.h>
00042 #include <utime.h>
00043 #include <sys/time.h>
00044 #include <sys/stat.h>
00045 #include <sys/types.h>
00046 #include <sys/resource.h>
00047 
00048 int execuid=15999;
00049 int execgid=15999;
00050 int must=0;
00051 time_t now;
00052 
00053 char *env_rm[]={
00054     "s2_dir", "w_wims_home", "session_base_dir", "trusted_module",
00055       "session_dir", "wims_server_base", "w_httpd_PWD",
00056       "w_wims_sesdir", "HTTP_CONNECTION",
00057       "w_gnuplot_format", "w_insplot_font", "w_ins_anim_limit",
00058       "w_ins_density", "w_ins_format",
00059       "texgif_fontdir", "texgif_texheader",
00060       "w_wims_tmp_debug", "w_insmath_logic", "w_insmath_rawmath",
00061       "SERVER_ADMIN", "SERVER_ADDR", "SERVER_NAME"
00062 };
00063 
00064 #define env_rm_cnt (sizeof(env_rm)/sizeof(env_rm[0]))   
00065 
00066 char name_sh[32]="/bin/ash.static";
00067 char opt_sh[32]="-c";
00068 char *pre_sh="\
00069 cd $TMPDIR || exit\n\
00070 echo >/dev/null || exit\n\
00071 cd / 2>/dev/null && exit\n\
00072 ";
00073 
00074 char *name_perl="/usr/bin/perl";
00075 char *opt_perl="-e";
00076 char *pre_perl="\
00077 chdir($ENV{TMPDIR}) || exit;\n\
00078 chdir(\"/\") && exit;\n\
00079 ";
00080 
00081        /* remove a tree */
00082 int remove_tree(char *dirname)
00083 {
00084     DIR *sdir;
00085     struct dirent *f;
00086     struct stat dst;
00087 
00088     sdir=opendir(dirname);
00089     if(sdir==NULL) {   /* Cannot open session directory. */
00090        return -1;
00091     }
00092     while((f=readdir(sdir))!=NULL) {
00093        char fname[255];
00094        if(strcmp(".",f->d_name)==0 || strcmp("..",f->d_name)==0) continue;
00095        snprintf(fname,sizeof(fname),"%s/%s",dirname,f->d_name);
00096        if(lstat(fname,&dst)) continue;
00097        if(S_ISDIR(dst.st_mode)) remove_tree(fname);
00098        else {
00099            if(remove(fname)<0)
00100              fprintf(stderr,"ch..root: unable to remove %s. %s\n",fname,strerror(errno));
00101        }
00102     }
00103     closedir(sdir);
00104     if(rmdir(dirname)<0) {  /* Cannot remove directory. */
00105        return -1;
00106     }
00107     return 0;
00108 }
00109 
00110 void cleantmp(void)
00111 {
00112     DIR *sdir_base;
00113     struct dirent *ses;
00114     struct stat dst;
00115     
00116     if(chdir("../chroot/tmp/sessions")<0) return;
00117     sdir_base=opendir(".");
00118     if(sdir_base==NULL) return;
00119     while((ses=readdir(sdir_base))!=NULL) {
00120        if(ses->d_name[0]=='.') continue;
00121        if(lstat(ses->d_name,&dst)) continue;
00122        if(!S_ISDIR(dst.st_mode)) continue;
00123        if(dst.st_mtime <= now) {
00124            if(dst.st_mtime>=now-CLEAN_DELAY) continue;
00125            if(dst.st_mtime>=now-CLEAN_DELAY2 && (dst.st_mode&S_IRWXO)==0) continue;
00126        }
00127        remove_tree(ses->d_name);
00128     }
00129 }
00130 
00131 void cleaning(void)
00132 {
00133     DIR *sdir_base;
00134     struct dirent *ses;
00135     struct stat dst;
00136     struct utimbuf ub;
00137     char dbuf[256];
00138 
00139     if(stat(timestamp,&dst)==0 && dst.st_mtime==now) return;
00140     ub.actime=ub.modtime=now; utime(timestamp,&ub);
00141     sdir_base=opendir("/proc");
00142     if(sdir_base==NULL) goto tmpdir;
00143     while((ses=readdir(sdir_base))!=NULL) {
00144        if(ses->d_name[0]<'0' || ses->d_name[9]>'9') continue;
00145        snprintf(dbuf,sizeof(dbuf),"/proc/%s",ses->d_name);
00146        if(lstat(dbuf,&dst)) continue;
00147        if(!S_ISDIR(dst.st_mode)) continue;
00148        if(dst.st_uid<UID_MIN || dst.st_uid>UID_MIN+UID_MASK) continue;
00149        if(((dst.st_gid-UID_MIN-now)&TIME_MASK)<=CPU_MAX) continue;
00150        kill(atoi(ses->d_name),SIGKILL);
00151     }
00152     closedir(sdir_base);
00153     tmpdir: return;
00154 }
00155 
00156 int test_must(void)
00157 {
00158     char *pc;
00159     if(must) return 1;
00160     pc=getenv("chroot"); if(pc && strcmp(pc,"must")==0) return 1;
00161     else return 0;
00162 }
00163 
00164 int main(int argc,char *argv[])
00165 {
00166     char *args[1024];
00167     char parm[MAX_PARMLEN];
00168     char tmpbuf[256];
00169     int i,k,uid,t;
00170     struct stat st;
00171     struct rlimit lim;
00172     char *p, *pp;
00173     
00174     if(argc<2) return 0;
00175     now=time(NULL);
00176     uid=geteuid();
00177     t=stat("../chroot/tmp/sessions/.chroot",&st);
00178     if(uid!=0 || t!=0) {
00179        if(test_must()) goto abandon;
00180        args[0]="bin/wrap..exec"; k=1;
00181     }
00182     else {
00183        k=0;
00184        p=getenv("REMOTE_ADDR"); if(p && *p) {
00185            pp=strrchr(p,'.'); if(pp) execuid=(atoi(++pp)&UID_MASK)+UID_MIN;
00186        }
00187        getrlimit(RLIMIT_CPU,&lim);
00188        i=lim.rlim_max; if(i<0) i=0; if(i>=CPU_MAX) i=CPU_MAX-1;
00189        execgid=((i+now+1)&TIME_MASK)+UID_MIN;
00190        cleaning();
00191     }
00192     if(argc>1 && strcmp(argv[1],"cleantmpdir")==0) {
00193        if(uid!=0) fprintf(stderr,"ch..root cleantmpdir: uid not changed.\n");
00194        else cleantmp();
00195        return 0;
00196     }
00197     if(argc>3 && argv[1][0]=='&') {
00198        if(k) goto abandon;
00199        if(strcmp(argv[2],"sh")==0) {
00200            lim.rlim_cur=lim.rlim_max=MAX_OUTLEN;
00201            setrlimit(RLIMIT_FSIZE,&lim);
00202            args[0]=name_sh; args[1]=opt_sh;
00203            snprintf(parm,sizeof(parm),"%s\n%s\n",pre_sh,argv[3]);
00204            args[2]=parm; args[3]=NULL; must=1;
00205            goto cont;
00206        }
00207        if(strcmp(argv[2],"perl")==0) {
00208            lim.rlim_cur=lim.rlim_max=MAX_OUTLEN;
00209            setrlimit(RLIMIT_FSIZE,&lim);
00210            args[0]=name_perl; args[1]=opt_perl;
00211            snprintf(parm,sizeof(parm),"%s\n%s\n",pre_perl,argv[3]);
00212            args[2]=parm; args[3]=NULL; must=1;
00213            goto cont;
00214        }
00215        goto abandon;
00216     }
00217     for(i=0;i<1000 && i<argc; i++) args[i+k]=argv[i+1];
00218     args[i]=NULL;
00219     cont:
00220     if(uid!=0) {
00221        if(test_must()) goto abandon;
00222        goto ex2;
00223     }
00224     if(t!=0) {
00225        stat("bin",&st); execuid=execgid=st.st_uid;
00226        if(test_must()) goto abandon;
00227        goto ex;
00228     }
00229     if(chroot("../chroot")==0) {
00230        chdir("/tmp");
00231        lim.rlim_cur=lim.rlim_max=PROC_QUOTA;
00232        setrlimit(RLIMIT_NPROC,&lim);
00233        setenv("PATH",chroot_path,1);
00234        p=getenv("w_wims_session"); if(p && *p) {
00235            snprintf(tmpbuf,sizeof(tmpbuf),"/tmp/sessions/%s",p);
00236            p=strchr(tmpbuf,'_'); if(p) *p=0;
00237            setenv("TMPDIR",tmpbuf,1);
00238            setenv("tmp_dir",tmpbuf,1);
00239            p=getenv("w_wims_priv_chroot");
00240            if(p && strstr(p,"tmpdir")!=NULL)
00241              chdir(tmpbuf);
00242        }
00243     }
00244     else if(test_must()) goto abandon;
00245     ex:
00246     if(setregid(execgid,execgid)<0) goto abandon;
00247     if(setreuid(execuid,execuid)<0) goto abandon; 
00248     ex2:
00249     for(i=0;i<env_rm_cnt;i++) unsetenv(env_rm[i]);
00250     if(strchr(args[0],'/')) execv(args[0],args); else execvp(args[0],args);
00251     abandon: return 127;
00252 }
00253