Beispiel #1
0
void SwitchProgram(const char *CommandLine, const  char *User,const  char *Group, const  char *Dir)
{
char **argv, *ptr;
char *Token=NULL, *SafeStr=NULL;
int i;

SafeStr=MakeShellSafeString(SafeStr,CommandLine,0);
argv=(char **) calloc(101,sizeof(char *));
ptr=SafeStr;
for (i=0; i < 100; i++)
{
	ptr=GetToken(ptr,"\\S",&Token,GETTOKEN_QUOTES);
	if (! ptr) break;
	argv[i]=CopyStr(argv[i],Token);
}

if (StrLen(Dir)) chdir(Dir);
if (StrLen(Group)) SwitchGroup(Group);
if (StrLen(User)) SwitchUser(User);

DestroyString(Token);
DestroyString(SafeStr);

/* we are the child so we continue */
execv(argv[0],argv);
//no point trying to free stuff here, we will no longer
//be the main program
}
Beispiel #2
0
int HTTPServerExecCGI(STREAM *ClientCon, HTTPSession *Session, const char *ScriptPath)
{
char *Tempstr=NULL;
int i;

      if (StrLen(Session->Group) && (! SwitchGroup(Session->Group)))
      {
        LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' to execute script: %s",Session->Group, ScriptPath);
      }


    //Switch user. ALAYA WILL NOT RUN SCRIPTS AS ROOT!
      if ((geteuid()==0) && (! SwitchUser(Session->RealUser)))
      {
        LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' to execute script: %s",Session->RealUser, ScriptPath);
				return(FALSE);
      }

      if (geteuid()==0)
      {
        HTTPServerSendHTML(ClientCon, NULL, "403 Forbidden","Alaya will not run .cgi programs as 'root'.<br>\r\nTry setting 'Default User' in config file or command line.");
        LogToFile(Settings.LogPath, "Failed to switch user to '%s' for running a .cgi program. Will not run programs as 'root'. Set 'DefaultUser' in config file or command line.",Session->RealUser);
      }
      else
      {
        Session->ResponseCode=CopyStr(Session->ResponseCode,"200 OK");
        HTTPServerSendHeaders(ClientCon, Session, HEADERS_CGI);
        STREAMFlush(ClientCon);

        SetupEnvironment(Session, ScriptPath);
        Tempstr=FindScriptHandlerForScript(Tempstr,ScriptPath);
        if (Tempstr) LogToFile(Settings.LogPath,"Execute script: %s using handler '%s'",ScriptPath,Tempstr);
        else LogToFile(Settings.LogPath,"Execute script: %s QUERY_STRING= '%s'",ScriptPath,getenv("QUERY_STRING"));

        //Only do this late! Otherwise logging won't work.
        for (i=3; i < 1000; i++) close(i);

        if (StrLen(Tempstr)) execl(Tempstr, Tempstr, ScriptPath,NULL);
        else execl(ScriptPath,ScriptPath,NULL);

        /*If this code gets executed, then 'execl' failed*/
        HTTPServerSendHTML(ClientCon, Session, "403 Forbidden","You don't have permission for that.");
  
        //Logging won't work after we've closed all the file descriptors!
        LogToFile(Settings.LogPath,"Cannot execute script: %s",ScriptPath);
    }

	//if we get there then, for whatever reason, our script didn't run
	DestroyString(Tempstr);
	return(FALSE);
}
Beispiel #3
0
pid_t ForkWithContext(const char *User, const char *Dir, const char *Group)
{
pid_t pid;

LogFileFlushAll(TRUE);
pid=fork();
if (pid==0)
{
	if (StrLen(Dir)) chdir(Dir);
	if (StrLen(Group)) SwitchGroup(Group);
	if (StrLen(User)) SwitchUser(User);
}
return(pid);
}
Beispiel #4
0
pid_t HandlePostFileRequest(STREAM *ClientCon, char *Data)
{
HTTPSession *Response;
STREAM *S;
char *Tempstr=NULL;
pid_t pid;

pid=fork();
if (pid==0)
{	
	Response=ParseSessionInfo(Data);
	Tempstr=FindFileInPath(Tempstr,Response->Path,Response->SearchPath);
	Response->Path=CopyStr(Response->Path, Tempstr);

	if (! SwitchGroup(Response->Group))
	{
		LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' when posting  '%s'", Response->RealUser, Tempstr);
	}


	if (! SwitchUser(Response->RealUser))
	{
		LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' when posting to document '%s'", Tempstr);
		LogFileFlushAll(TRUE);
		_exit(0);
	}

 LogToFile(Settings.LogPath,"SWITCH USER: '******' posting document '%s'", Response->RealUser, Response->Group, Tempstr);

	STREAMWriteLine("READY\n",ClientCon); STREAMFlush(ClientCon);
	HTTPServerHandlePost(ClientCon, Response);
	
	//exit 1 means that we can keep connection alive for re-use
	LogFileFlushAll(TRUE);
	if (Response->Flags & SESSION_KEEPALIVE) _exit(1);
	_exit(0);
}
else ClientCon->State |= SS_EMBARGOED;

DestroyString(Tempstr);

return(pid);
}
Beispiel #5
0
pid_t HandleGetFileRequest(STREAM *ClientCon, char *Data)
{
HTTPSession *Response;
char *Tempstr=NULL;
pid_t pid;


pid=fork();
if (pid==0)
{
	Response=ParseSessionInfo(Data);
	Tempstr=FindFileInPath(Tempstr,Response->Path,Response->SearchPath);
	if (! SwitchGroup(Response->Group))
	{
		LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' when getting document '%s'", Response->RealUser, Tempstr);
	}

	if (! SwitchUser(Response->RealUser))
	{
		LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' when getting document '%s'", Tempstr);
		LogFileFlushAll(TRUE);
		_exit(0);
	}


	STREAMWriteLine("READY\n",ClientCon); STREAMFlush(ClientCon);
	HTTPServerSendDocument(ClientCon, Response, Tempstr, HEADERS_SENDFILE|HEADERS_USECACHE|HEADERS_KEEPALIVE);
		
	//exit 1 means that we can keep connection alive for re-use
	LogFileFlushAll(TRUE);
	if (Response->Flags & SESSION_KEEPALIVE) _exit(1);
	_exit(0);
}

DestroyString(Tempstr);

return(pid);
}
Beispiel #6
0
pid_t PseudoTTYSpawnFunction(int *ret_pty, BASIC_FUNC Func, void *Data, const char *User, const char *Group, int TTYFlags)
{
pid_t pid=-1;
int tty, pty, i;

if (GrabPseudoTTY(&pty, &tty, TTYFlags))
{
pid=ForkWithContext(NULL,NULL,NULL);
if (pid==0)
{
for (i=0; i < 4; i++) close(i);
close(pty);

setsid();
ioctl(tty,TIOCSCTTY,0);

dup(tty);
dup(tty);
dup(tty);

///now that we've dupped it, we don't need to keep it open
//as it will be open on stdin/stdout
close(tty);

if (StrLen(Group)) SwitchGroup(Group);
if (StrLen(User)) SwitchUser(User);

Func((char *) Data);
_exit(0);
}

close(tty);
}

*ret_pty=pty;
return(pid);
}
Beispiel #7
0
pid_t HandleIconRequest(STREAM *ClientCon, char *Data)
{
HTTPSession *Response;
char *Name=NULL, *Value=NULL, *ptr, *tptr;
char *Tempstr=NULL;
ListNode *Vars;
pid_t pid;


pid=fork();
if (pid==0)
{
Response=ParseSessionInfo(Data);
Vars=ListCreate();
ptr=GetNameValuePair(Response->Arguments,"&","=",&Name,&Tempstr);
while (ptr)
{
	Value=HTTPUnQuote(Value,Tempstr);
	SetVar(Vars,Name,Value);
	if (strcasecmp(Name,"MimeType")==0)
	{
		tptr=GetToken(Value,"/",&Tempstr,0);
		SetVar(Vars,"MimeClass",Tempstr);
		SetVar(Vars,"MimeSub",tptr);
	}
	ptr=GetNameValuePair(ptr,"&","=",&Name,&Tempstr);
}

	if (! SwitchGroup(Response->Group))
	{
		LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' for mimeicons '%s'", Response->RealUser, Tempstr);
	}

	if (! SwitchUser(Response->RealUser))
	{
		LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' when getting icons from '%s'", Response->SearchPath);
		LogFileFlushAll(TRUE);
		_exit(0);
	}


	STREAMWriteLine("READY\n",ClientCon); STREAMFlush(ClientCon);
	ptr=GetToken(Response->SearchPath,":",&Value,0);
	while (ptr)
	{
		Tempstr=SubstituteVarsInString(Tempstr,Value,Vars,0);
		if (access(Tempstr,R_OK)==0) break;
		ptr=GetToken(ptr,":",&Value,0);
	}
		
	//HTTPServerSendDocument(ClientCon, Response, Tempstr, HEADERS_SENDFILE|HEADERS_USECACHE|HEADERS_KEEPALIVE);
	HTTPServerSendDocument(ClientCon, Response, Tempstr, HEADERS_SENDFILE|HEADERS_USECACHE);
	//exit 1 means we can keep connection alive for reuse
	LogFileFlushAll(TRUE);
	_exit(1);
}

DestroyString(Name);
DestroyString(Value);
DestroyString(Tempstr);

return(pid);
}
Beispiel #8
0
pid_t HandleWebsocketExecRequest(STREAM *ClientCon, char *Data)
{
char *Tempstr=NULL, *Name=NULL, *Value=NULL;
char *ScriptPath=NULL;
int result, i;
HTTPSession *Response;

	//We will never read from this stream again. Any further data will be read
	//by the process we spawn off
	ClientCon->State |= SS_EMBARGOED;
	Response=ParseSessionInfo(Data);
	CleanStr(Response->Path);
	CleanStr(Response->SearchPath);
	CleanStr(Response->StartDir);
	ScriptPath=FindFileInPath(ScriptPath, Response->Path, Response->SearchPath);
	LogToFile(Settings.LogPath,"Script: Found=[%s] SearchPath=[%s] ScriptName=[%s] Arguments=[%s]", ScriptPath, Response->SearchPath, Response->Path, Response->Arguments);

	if (access(ScriptPath,F_OK) !=0)
	{
			LogToFile(Settings.LogPath,"No such script: %s in path %s = %s",Response->Path, Response->SearchPath, ScriptPath);
	}
	else if (
					(access(ScriptPath,X_OK) !=0) || 
					(! CheckScriptIntegrity(ScriptPath))
			)
	{
			LogToFile(Settings.LogPath,"Cannot execute script: %s", ScriptPath);
	}
	else
	{
		STREAMFlush(ClientCon);
		result=fork();
		if (result==0)
		{
			//do this so that when we exec the script, anything output goes to the client
			close(0);
			dup(ClientCon->in_fd);
			close(1);
			dup(ClientCon->out_fd);

      if (! SwitchGroup(Response->Group))
			{
        LogToFile(Settings.LogPath,"WARN: Failed to switch to group '%s' to execute script: %s using handler '%s'", Response->RealUser, ScriptPath, Tempstr);
			}

      //Switch user. ALAYA WILL NOT RUN SCRIPTS AS ROOT!
      if (! SwitchUser(Response->RealUser))
      {
        LogToFile(Settings.LogPath,"ERROR: Failed to switch to user '%s' to execute script: %s using handler '%s'", Response->RealUser, ScriptPath, Tempstr);
				LogFileFlushAll(TRUE);
        _exit(0);
      }

			if (geteuid()==0)
			{
				LogToFile(Settings.LogPath, "Failed to switch user to '%s' for running a .cgi program. Will not run programs as 'root'. Set 'DefaultUser' in config file or command line.", Response->RealUser);
			}
			else
			{
				SetupEnvironment(Response, ScriptPath);
				Tempstr=FindScriptHandlerForScript(Tempstr,ScriptPath);
				if (Tempstr) LogToFile(Settings.LogPath,"Execute script: %s using handler '%s'",ScriptPath,Tempstr);
				else LogToFile(Settings.LogPath,"Execute script: %s QUERY_STRING= '%s'",ScriptPath,getenv("QUERY_STRING"));

				//Only do this late! Otherwise logging won't work.
				for (i=3; i < 1000; i++) close(i);

				if (StrLen(Tempstr)) execl(Tempstr, Tempstr, ScriptPath,NULL);
				else execl(ScriptPath,ScriptPath,NULL);

				//Logging won't work after we've closed all the file descriptors!
				LogToFile(Settings.LogPath,"Cannot execute script: %s",ScriptPath);
		}
		LogFileFlushAll(TRUE);
		_exit(0);
	}
	else
	{

	}
}


HTTPSessionDestroy(Response);
DestroyString(ScriptPath);
DestroyString(Tempstr);
DestroyString(Name);
DestroyString(Value);


//Always return STREAM_CLOSED, so that pipe gets closed regardless of exit status of 
//forked helper process
return(STREAM_CLOSED);
}
Beispiel #9
0
/* This creates a child process that we can talk to using a couple of pipes*/
pid_t PipeSpawnFunction(int *infd,int  *outfd,int  *errfd, BASIC_FUNC Func, void *Data, const char *User, const char *Group)
{
pid_t pid;
int channel1[2], channel2[2], channel3[2], DevNull=-1;

if (infd) pipe(channel1);
if (outfd) pipe(channel2);
if (errfd) pipe(channel3);

pid=ForkWithContext(NULL,NULL,NULL);
if (pid==0)
{
/* we are the child */
if (infd) close(channel1[1]);
else if (DevNull==-1) DevNull=open("/dev/null",O_RDWR);
if (outfd) close(channel2[0]);
else if (DevNull==-1) DevNull=open("/dev/null",O_RDWR);
if (errfd) close(channel3[0]);
else if (DevNull==-1) DevNull=open("/dev/null",O_RDWR);

/*close stdin, stdout and stderr*/
close(0);
close(1);
close(2);
/*channel 1 is going to be our stdin, so we close the writing side of it*/
if (infd) dup(channel1[0]);
else dup(DevNull);
/* channel 2 is stdout */
if (outfd) dup(channel2[1]);
else dup(DevNull);
/* channel 3 is stderr */
if (errfd)
{
	//Yes, we can pass an integer value as errfd, even though it's an int *. 
	//This is probably a bad idea, and will likely be changed in future releases
	if (errfd==(int) COMMS_COMBINE_STDERR) dup(channel2[1]);
	else dup(channel3[1]);
}
else dup(DevNull);

if (StrLen(Group)) SwitchGroup(Group);
if (StrLen(User)) SwitchUser(User);


Func(Data);
exit(0);
}
else 
{
/* we close the appropriate halves of the link */
if (infd) 
{
	close(channel1[0]);
	*infd=channel1[1];
}
if (outfd)
{
	close(channel2[1]);
	*outfd=channel2[0];
}
if (errfd)
{
	close(channel3[1]);	
	//Yes, we can pass an integer value as errfd, even though it's an int *. 
	//This is probably a bad idea, and will likely be changed in future releases
	if (errfd != (int) COMMS_COMBINE_STDERR) *errfd=channel3[0];
}

}

return(pid);
}
Beispiel #10
0
void SpawnApplyConfig(const char *Config, int Flags)
{
char *User=NULL, *Group=NULL, *Dir=NULL;
char *Name=NULL, *Value=NULL;
const char *ptr;
struct rlimit limit;
rlim_t val;

int i;

//set all signal handlers to default
if (Flags & SPAWN_SIGDEF)
{
for (i =0; i < _NSIG; i++) signal(i,SIG_DFL);
}

//Set controlling tty to be stdin. This means that CTRL-C, SIGWINCH etc is handled for the 
//stdin file descriptor, not for any oher 
if (Flags & SPAWN_DAEMON) demonize();
else
{
	if (Flags & SPAWN_SETSID) setsid();
	if (Flags & SPAWN_CTRL_TTY) tcsetpgrp(0, getpgrp());
}

User=CopyStr(User,"");
Group=CopyStr(Group,"");
ptr=GetNameValuePair(Config,"\\S","=",&Name,&Value);
while (ptr)
{
	if (strcasecmp(Name,"User")==0) User=CopyStr(User, Value);
	else if (strcasecmp(Name,"Group")==0) Group=CopyStr(Group, Value);
	else if (strcasecmp(Name,"Dir")==0) Dir=CopyStr(Dir, Value);
	else if (strcasecmp(Name,"PidFile")==0) WritePidFile(Value);
	else if (strcasecmp(Name,"prio")==0) setpriority(PRIO_PROCESS, 0, atoi(Value));
	else if (strcasecmp(Name,"nice")==0) setpriority(PRIO_PROCESS, 0, atoi(Value));
	else if (strcasecmp(Name,"priority")==0) setpriority(PRIO_PROCESS, 0, atoi(Value));
	else if (strcasecmp(Name,"mem")==0) 
	{
		val=(rlim_t) ParseHumanReadableDataQty(Value, 0);
		limit.rlim_cur=val;
		limit.rlim_max=val;
		setrlimit(RLIMIT_DATA, &limit);
	}
	else if (strcasecmp(Name,"fsize")==0) 
	{
		val=(rlim_t) ParseHumanReadableDataQty(Value, 0);
		limit.rlim_cur=val;
		limit.rlim_max=val;
		setrlimit(RLIMIT_FSIZE, &limit);
	}
	else if (strcasecmp(Name,"files")==0) 
	{
		val=(rlim_t) ParseHumanReadableDataQty(Value, 0);
		limit.rlim_cur=val;
		limit.rlim_max=val;
		setrlimit(RLIMIT_NOFILE, &limit);
	}
	else if (strcasecmp(Name,"coredumps")==0) 
	{
		val=(rlim_t) ParseHumanReadableDataQty(Value, 0);
		limit.rlim_cur=val;
		limit.rlim_max=val;
		setrlimit(RLIMIT_CORE, &limit);
	}
	else if ( (strcasecmp(Name,"procs")==0) || (strcasecmp(Name,"nproc")==0) )
	{
		val=(rlim_t) ParseHumanReadableDataQty(Value, 0);
		limit.rlim_cur=val;
		limit.rlim_max=val;
		setrlimit(RLIMIT_NPROC, &limit);
	}
	ptr=GetNameValuePair(ptr,"\\S","=",&Name,&Value);
}

// This allows us to chroot into a whole different unix directory tree, with its own
// password file etc
if (Flags & SPAWN_CHROOT) chroot(".");

if (StrLen(Dir)) chdir(Dir);

//Always do group first, otherwise we'll lose ability to switch user/group
if (StrLen(Group)) SwitchGroup(Group);
if (StrLen(User)) SwitchUser(User);

//Must do this last! After parsing Config, and also after functions like
//SwitchUser that will need access to /etc/passwd
if (Flags & SPAWN_JAIL) chroot(".");

DestroyString(Name);
DestroyString(Value);
DestroyString(User);
DestroyString(Group);
DestroyString(Dir);
}