974 lines
20 KiB
C++
Executable File
974 lines
20 KiB
C++
Executable File
/*
|
|
* This file is a part of Winix
|
|
* and is not publicly distributed
|
|
*
|
|
* Copyright (c) 2010, Tomasz Sowa
|
|
* All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <unistd.h>
|
|
|
|
#include "app.h"
|
|
#include "plugin.h"
|
|
#include "misc.h"
|
|
#include "functions/functions.h"
|
|
#include "ezc.h"
|
|
|
|
|
|
|
|
|
|
App::App()
|
|
{
|
|
stdout_is_closed = false;
|
|
last_sessions_save = std::time(0);
|
|
|
|
db.SetConn(db_conn);
|
|
|
|
plugin.SetDb(&db);
|
|
plugin.SetConfig(&config);
|
|
plugin.SetRequest(&request);
|
|
plugin.SetSystem(&system);
|
|
plugin.SetFunctions(&functions);
|
|
plugin.SetTemplates(&templates);
|
|
plugin.SetSessionManager(&session_manager);
|
|
|
|
request.SetConfig(&config);
|
|
|
|
functions.SetConfig(&config);
|
|
functions.SetRequest(&request);
|
|
functions.SetDb(&db);
|
|
functions.SetSystem(&system);
|
|
functions.SetTemplates(&templates);
|
|
functions.SetNotify(¬ify);
|
|
|
|
system.SetConfig(&config);
|
|
system.SetRequest(&request);
|
|
system.SetDb(&db);
|
|
|
|
templates_notify.SetConfig(&config);
|
|
|
|
notify.SetRequest(&request);
|
|
notify.SetConfig(&config);
|
|
notify.SetSystem(&system);
|
|
notify.SetTemplatesNotify(&templates_notify);
|
|
|
|
templates.SetConfig(&config);
|
|
templates.SetRequest(&request);
|
|
templates.SetDb(&db);
|
|
templates.SetSystem(&system);
|
|
templates.SetFunctions(&functions);
|
|
templates.SetSessionManager(&session_manager);
|
|
|
|
session_manager.SetLastContainer(&system.users.last);
|
|
session_manager.SetConfig(&config);
|
|
session_manager.SetRequest(&request);
|
|
session_manager.SetSystem(&system);
|
|
|
|
post_multi_parser.SetConfig(&config);
|
|
}
|
|
|
|
|
|
|
|
bool App::CreateFCGISocket()
|
|
{
|
|
const char * sock = config.fcgi_socket.c_str();
|
|
|
|
unlink(sock);
|
|
|
|
|
|
|
|
int s = FCGX_OpenSocket(sock, 100);
|
|
|
|
if( s < 0 )
|
|
{
|
|
log << log1 << "An error during creating a socket" << logend;
|
|
return false;
|
|
}
|
|
|
|
|
|
chmod(sock, config.fcgi_socket_chmod);
|
|
|
|
passwd * pw = getpwnam(config.fcgi_socket_user.c_str());
|
|
|
|
if( !pw )
|
|
{
|
|
log << log1 << "There is no user: " << config.fcgi_socket_user << logend;
|
|
return false;
|
|
}
|
|
|
|
group * gr = getgrnam(config.fcgi_socket_group.c_str());
|
|
|
|
if( !gr )
|
|
{
|
|
log << log1 << "There is no group: " << config.fcgi_socket_group << logend;
|
|
return false;
|
|
}
|
|
|
|
chown(sock, pw->pw_uid, gr->gr_gid);
|
|
|
|
|
|
if( setuid(pw->pw_uid) < 0 )
|
|
{
|
|
log << log1 << "I can't change the user into: " << config.fcgi_socket_user << logend;
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
if( setgid(gr->gr_gid) < 0 )
|
|
{
|
|
int e = errno;
|
|
|
|
log << log1 << "I can't change the group into: " << config.fcgi_socket_group << " " << gr->gr_gid << logend;
|
|
log << log1 << "errno: " << e << logend;
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
dup2(s, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool App::Init()
|
|
{
|
|
db_conn.SetConnParam(config.db_database, config.db_user, config.db_pass);
|
|
db_conn.WaitForConnection();
|
|
db.LogQueries(config.log_db_query);
|
|
|
|
if( !CreateFCGISocket() )
|
|
return false; // !! dodac logsave do logow
|
|
|
|
request.Clear();
|
|
compress.Init();
|
|
system.Init();
|
|
functions.Init();
|
|
|
|
// !! teraz mamy dwa katalogi z templetami
|
|
// !! o co chodzilo?
|
|
if( !notify.Init() )
|
|
return false; // !! dodac logsave do logow
|
|
|
|
// call this after system.Init() (mount points identificators should be created)
|
|
templates_notify.SetMountTypes( system.mounts.MountTypeCms(),
|
|
system.mounts.MountTypeThread(),
|
|
2 ); // !! chwilowo
|
|
//system.mounts.MountTypeTicket() );
|
|
|
|
|
|
// init templates after functions are created
|
|
templates.ReadIndexFileNames();
|
|
templates.ReadTemplates();
|
|
templates.CreateFunctions();
|
|
session_manager.LoadSessions();
|
|
|
|
plugin.Call(WINIX_PLUGIN_INIT);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void App::Close()
|
|
{
|
|
session_manager.SaveSessions();
|
|
session_manager.DeleteAllPluginsData();
|
|
}
|
|
|
|
|
|
|
|
bool App::BaseUrlRedirect()
|
|
{
|
|
if( request.role == Request::responder )
|
|
{
|
|
if( config.base_url_http_host.empty() )
|
|
return false;
|
|
|
|
if( Equal(config.base_url_http_host.c_str(), request.env_http_host) )
|
|
return false;
|
|
|
|
request.redirect_to = config.base_url;
|
|
AssignString(request.env_request_uri, request.redirect_to, false);
|
|
}
|
|
else
|
|
{
|
|
// authorizer
|
|
|
|
if( config.base_url_auth_http_host.empty() )
|
|
return false;
|
|
|
|
if( Equal(config.base_url_auth_http_host.c_str(), request.env_http_host) )
|
|
return false;
|
|
|
|
request.redirect_to = config.base_url_auth;
|
|
AssignString(request.env_request_uri, request.redirect_to, false);
|
|
}
|
|
|
|
log << log3 << "RC: BaseUrlRedirect from: " << request.env_http_host << logend;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void App::ProcessRequestThrow()
|
|
{
|
|
ReadRequest();
|
|
|
|
// when BaseUrlRedirect() return true we didn't have to set everything in request.Read()
|
|
// in the future request.Read() can be split and at the beginning only environment variables will be read
|
|
// and then BaseUrlRedirect() will be called (for performance)
|
|
if( !BaseUrlRedirect() )
|
|
{
|
|
session_manager.DeleteOldSessions();
|
|
session_manager.SetSession(); // set request.session as well
|
|
|
|
// !! tutaj dodac to ustawianie request.session
|
|
|
|
functions.Parse();
|
|
|
|
system.mounts.CalcCurMount();
|
|
|
|
Make();
|
|
}
|
|
|
|
SendAnswer();
|
|
notify.ItemChanged(request.notify_code);
|
|
}
|
|
|
|
|
|
void App::ProcessRequest()
|
|
{
|
|
try
|
|
{
|
|
ProcessRequestThrow();
|
|
}
|
|
catch(const std::exception & e)
|
|
{
|
|
log << log1 << "App: there was an exception: std::exception: " << e.what() << logend;
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
log << log1 << "App: there was an exception: Error: " << e << logend;
|
|
}
|
|
catch(...)
|
|
{
|
|
log << log1 << "App: there was an unknown exception" << logend;
|
|
}
|
|
}
|
|
|
|
|
|
void App::Start()
|
|
{
|
|
while( FCGX_Accept(&request.in, &request.out, &request.err, &request.env) == 0 )
|
|
{
|
|
system.load_avg.StartRequest();
|
|
log << log2 << config.log_delimiter << logend;
|
|
|
|
ProcessRequest();
|
|
SaveSessionsIfNeeded();
|
|
|
|
if( request.function )
|
|
request.function->Clear();
|
|
|
|
request.Clear();
|
|
system.load_avg.StopRequest();
|
|
log << logsave;
|
|
}
|
|
}
|
|
|
|
|
|
void App::SaveSessionsIfNeeded()
|
|
{
|
|
time_t t = std::time(0);
|
|
|
|
if( last_sessions_save + 86400 > t )
|
|
return;
|
|
|
|
// saving once a day for safety
|
|
last_sessions_save = t;
|
|
session_manager.SaveSessions();
|
|
}
|
|
|
|
|
|
|
|
|
|
// !! zmienic na lepsza nazwe
|
|
void App::MakePage()
|
|
{
|
|
bool sent = false;
|
|
|
|
|
|
if( !request.redirect_to.empty() || !request.x_sendfile.empty() )
|
|
return;
|
|
|
|
|
|
if( request.is_item && request.item.auth == Item::auth_none &&
|
|
request.item.content_type == Item::ct_raw && request.status == WINIX_ERR_OK && request.function )
|
|
{
|
|
if( request.function == &functions.fun_cat )
|
|
{
|
|
request.page << request.item.content;
|
|
sent = true;
|
|
}
|
|
else
|
|
if( request.function == &functions.fun_run )
|
|
{
|
|
templates.GenerateRunRaw();
|
|
sent = true;
|
|
}
|
|
}
|
|
|
|
if( !sent )
|
|
{
|
|
templates.Generate();
|
|
}
|
|
}
|
|
|
|
|
|
// zmienic nazwe np na ProcessRequest
|
|
void App::Make()
|
|
{
|
|
if( request.dir_tab.empty() )
|
|
{
|
|
log << log1 << "Content: there is no a root dir (dir_tab is empty)" << logend;
|
|
return;
|
|
}
|
|
|
|
// request.status can be changed by function_parser
|
|
if( request.status == WINIX_ERR_OK )
|
|
{
|
|
plugin.Call(WINIX_PREPARE_REQUEST);
|
|
|
|
if( system.DirsHaveReadExecPerm() )
|
|
{
|
|
if( request.method == Request::post )
|
|
functions.MakePost();
|
|
|
|
if( request.redirect_to.empty() && request.status == WINIX_ERR_OK )
|
|
functions.MakeGet();
|
|
}
|
|
else
|
|
request.status = WINIX_ERR_PERMISSION_DENIED;
|
|
}
|
|
|
|
if( request.session->spam_score > 0 )
|
|
log << log1 << "App: spam score: " << request.session->spam_score << logend;
|
|
|
|
if( request.IsParam(L"noredirect") )
|
|
request.redirect_to.clear();
|
|
|
|
if( request.status == WINIX_ERR_OK )
|
|
plugin.Call(WINIX_PROCESS_REQUEST);
|
|
|
|
if( !request.redirect_to.empty() )
|
|
return;
|
|
|
|
if( request.dir_tab.empty() )
|
|
{
|
|
log << log1 << "App: there is no a root dir (dir_tab is empty -- after calling a function)" << logend;
|
|
return;
|
|
}
|
|
|
|
plugin.Call(WINIX_CONTENT_MAKE);
|
|
MakePage();
|
|
|
|
if( config.debug_info )
|
|
{
|
|
// !! dodac inne informacje (get, post, itp)
|
|
// jesli jest debug_info wlaczone to nie robic przekierowan
|
|
request.PrintGetTab();
|
|
request.PrintEnv();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void App::ReadRequest()
|
|
{
|
|
ReadEnvVariables();
|
|
CheckRequestMethod();
|
|
CheckFCGIRole();
|
|
|
|
LogAccess();
|
|
|
|
ReadGetPostVars();
|
|
cookie_parser.Parse(request.env_http_cookie, request.cookie_tab);
|
|
accept_encoding_parser.Parse(request.env_http_accept_encoding);
|
|
|
|
CheckIE();
|
|
CheckKonqueror();
|
|
|
|
if( request.role == Request::authorizer )
|
|
log << log3 << "Request: fast cgi role: authorizer" << logend;
|
|
}
|
|
|
|
|
|
|
|
void App::SetEnv(const char * & env, const char * name)
|
|
{
|
|
const char * v = FCGX_GetParam(name, request.env);
|
|
|
|
if( v )
|
|
env = v;
|
|
|
|
// by default env is set to an empty string (in request.Clear() method)
|
|
}
|
|
|
|
|
|
|
|
void App::ReadEnvVariables()
|
|
{
|
|
// we store that values because FCGX_GetParam has O(n) complexity
|
|
// with this variables (env_*) we have O(1)
|
|
|
|
SetEnv(request.env_request_method, "REQUEST_METHOD");
|
|
SetEnv(request.env_request_uri, "REQUEST_URI");
|
|
SetEnv(request.env_http_cookie, "HTTP_COOKIE");
|
|
SetEnv(request.env_remote_addr, "REMOTE_ADDR");
|
|
SetEnv(request.env_http_host, "HTTP_HOST");
|
|
SetEnv(request.env_http_user_agent, "HTTP_USER_AGENT");
|
|
SetEnv(request.env_fcgi_role, "FCGI_ROLE");
|
|
SetEnv(request.env_content_type, "CONTENT_TYPE");
|
|
SetEnv(request.env_http_accept_encoding,"HTTP_ACCEPT_ENCODING");
|
|
}
|
|
|
|
|
|
|
|
void App::CheckRequestMethod()
|
|
{
|
|
request.method = Request::none;
|
|
|
|
if( ToSmall(request.env_request_method[0]) == 'g' )
|
|
request.method = Request::get;
|
|
else
|
|
if( ToSmall(request.env_request_method[0]) == 'p' )
|
|
request.method = Request::post;
|
|
else
|
|
if( ToSmall(request.env_request_method[0]) == 'h' )
|
|
request.method = Request::head;
|
|
}
|
|
|
|
|
|
void App::CheckFCGIRole()
|
|
{
|
|
// default we assume 'responder'
|
|
request.role = Request::responder;
|
|
|
|
if( ToSmall(request.env_fcgi_role[0]) == 'a' )
|
|
request.role = Request::authorizer;
|
|
}
|
|
|
|
|
|
|
|
void App::LogAccess()
|
|
{
|
|
log.PutDate(log1);
|
|
|
|
log << request.env_remote_addr << ' '
|
|
<< request.env_request_method << ' '
|
|
<< request.env_http_host
|
|
<< request.env_request_uri << ' '
|
|
<< request.env_http_user_agent << logend;
|
|
}
|
|
|
|
|
|
|
|
|
|
void App::ReadGetPostVars()
|
|
{
|
|
// get parameters we have always
|
|
get_parser.UTF8(config.utf8);
|
|
get_parser.Parse(request.env_request_uri, request.get_tab);
|
|
|
|
if( request.method == Request::post )
|
|
{
|
|
if( IsSubStringNoCase("multipart/form-data", request.env_content_type) )
|
|
{
|
|
log << log3 << "Request: post content type: multipart/form-data" << logend;
|
|
// !! dodac metode UTF8 do post_multi_parsera
|
|
// (narazie bierze bezposrednio z konfigu)
|
|
// w ogole wywalic zaleznosc od konfiga
|
|
post_multi_parser.Parse(request.in, request.post_tab, request.post_file_tab);
|
|
}
|
|
else
|
|
{
|
|
post_parser.UTF8(config.utf8);
|
|
post_parser.Parse(request.in, request.post_tab);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void App::CheckIE()
|
|
{
|
|
char * msie = strstr(request.env_http_user_agent, "MSIE");
|
|
|
|
if( msie )
|
|
request.browser_msie = true;
|
|
else
|
|
request.browser_msie = false;
|
|
}
|
|
|
|
|
|
|
|
void App::CheckKonqueror()
|
|
{
|
|
char * kon = strstr(request.env_http_user_agent, "Konqueror");
|
|
|
|
if( kon )
|
|
request.browser_konqueror = true;
|
|
else
|
|
request.browser_konqueror = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void App::PrepareSessionCookie()
|
|
{
|
|
if( !request.session || request.session->id==0 )
|
|
return;
|
|
|
|
if( !request.session->puser || !request.session->remember_me )
|
|
{
|
|
request.SetCookie(config.http_session_id_name.c_str(), request.session->id);
|
|
}
|
|
else
|
|
{
|
|
time_t t = std::time(0) + config.session_remember_max_idle;
|
|
tm expires = Time(t);
|
|
request.SetCookie(config.http_session_id_name.c_str(), request.session->id, &expires);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void App::SendHeaders(bool compressing, Header header)
|
|
{
|
|
PrepareSessionCookie();
|
|
|
|
if( request.send_as_attachment )
|
|
FCGX_PutS("Content-Disposition: attachment\r\n", request.out);
|
|
|
|
if( !request.redirect_to.empty() )
|
|
{
|
|
FCGX_PutS("Status: 301 Moved Permanently\r\n", request.out);
|
|
AssignString(request.redirect_to, request.aredirect_to);
|
|
FCGX_FPrintF(request.out, "Location: %s\r\n", request.aredirect_to.c_str());
|
|
log << log2 << "Redirect to: " << request.redirect_to << logend;
|
|
}
|
|
else
|
|
if( !request.x_sendfile.empty() )
|
|
{
|
|
static std::string temp, temp2; // !! wrzucic gdzies to
|
|
|
|
AssignString(config.http_header_send_file, temp);
|
|
AssignString(request.x_sendfile, temp2);
|
|
FCGX_FPrintF(request.out, "%s: %s\r\n", temp.c_str(), temp2.c_str());
|
|
|
|
FCGX_PutS("Status: 200 OK\r\n", request.out);
|
|
log << log2 << "Sending file: " << request.x_sendfile << logend;
|
|
}
|
|
else
|
|
{
|
|
switch( header )
|
|
{
|
|
case h_404:
|
|
FCGX_PutS("Status: 404 Not Found\r\n", request.out);
|
|
FCGX_PutS("Content-Type: text/html\r\n", request.out);
|
|
log << log2 << "Request: response: 404 Not Found" << logend;
|
|
break;
|
|
|
|
case h_403:
|
|
FCGX_PutS("Status: 403 Forbidden\r\n", request.out);
|
|
FCGX_PutS("Content-Type: text/html\r\n", request.out);
|
|
log << log2 << "Request: response: 403 Forbidden" << logend;
|
|
break;
|
|
|
|
default:
|
|
FCGX_PutS("Status: 200 OK\r\n", request.out);
|
|
|
|
if( request.role != Request::authorizer )
|
|
FCGX_PutS("Content-Type: text/html\r\n", request.out);
|
|
}
|
|
}
|
|
|
|
if( compressing )
|
|
FCGX_PutS("Content-Encoding: deflate\r\n", request.out);
|
|
|
|
FCGX_PutS(request.headers.CStr(), request.out);
|
|
FCGX_PutS("\r\n", request.out);
|
|
}
|
|
|
|
|
|
|
|
void App::SetHtmlFilterConf()
|
|
{
|
|
html_filter.TrimWhite(config.html_filter_trim_white);
|
|
html_filter.BreakLines(config.html_filter_break_lines);
|
|
html_filter.InsertTabs(config.html_filter_tabs);
|
|
|
|
if( config.html_filter_orphans )
|
|
html_filter.CheckOrphans(config.html_filter_orphans_lang, config.html_filter_orphans_mode);
|
|
}
|
|
|
|
|
|
// !! kopiowanie tych stringow bedzie zmienione
|
|
// gdy bedziemy korzystac w przyszlosci z wlasnego stringstream
|
|
void App::FilterCompressSend(bool compressing, const std::wstring & source_ref)
|
|
{
|
|
const std::wstring * source = &source_ref;
|
|
|
|
bool raw = request.is_item && request.item.content_type == Item::ct_raw && request.status == WINIX_ERR_OK &&
|
|
request.function && (request.function == &functions.fun_cat || request.function == &functions.fun_run);
|
|
|
|
if( config.html_filter && !raw )
|
|
{
|
|
SetHtmlFilterConf();
|
|
html_filter.Filter(*source, clean_html);
|
|
AddDebugInfo(clean_html);
|
|
source = &clean_html;
|
|
}
|
|
else
|
|
{
|
|
html_with_debug = *source;
|
|
AddDebugInfo(html_with_debug);
|
|
source = &html_with_debug;
|
|
}
|
|
|
|
|
|
// !! zrobic z tym porzadek
|
|
std::string temp;
|
|
Ezc::WideToUTF8(*source, temp);
|
|
|
|
if( compressing )
|
|
compress.CompressAndPut(temp.c_str(), temp.length(), request.out);
|
|
else
|
|
FCGX_PutS(temp.c_str(), request.out);
|
|
}
|
|
|
|
|
|
|
|
bool App::IsCompressionAllowed(const std::wstring & source)
|
|
{
|
|
return( config.compression &&
|
|
request.role == Request::responder &&
|
|
request.redirect_to.empty() &&
|
|
request.x_sendfile.empty() &&
|
|
!request.browser_msie &&
|
|
!request.browser_konqueror &&
|
|
accept_encoding_parser.AcceptDeflate() &&
|
|
source.size() >= (size_t)config.compression_page_min_size );
|
|
}
|
|
|
|
|
|
bool App::CanSendContent(Header header)
|
|
{
|
|
if( !request.redirect_to.empty() || !request.x_sendfile.empty() )
|
|
// if there is a redirect or a file to send then we do not send a content
|
|
return false;
|
|
|
|
if( header == h_200 && request.role == Request::authorizer && request.is_item && request.item.auth != Item::auth_none )
|
|
// if there is an item and the item has 'file' storage we do not send a content
|
|
return false;
|
|
|
|
/*
|
|
we don't have to check the HEAD method
|
|
the server (lighttpd) doesn't send the body of its own
|
|
*/
|
|
if( request.method == Request::head )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void App::AddDebugInfo(std::wstring & out)
|
|
{
|
|
if( config.debug_info )
|
|
{
|
|
if( !request.debug.Empty() )
|
|
{
|
|
out += L"\n<!--\n";
|
|
out += request.debug.Str();
|
|
out += L"\n-->\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void App::SendAnswer()
|
|
{
|
|
const std::wstring & source = request.page.Str();
|
|
Header header = h_200;
|
|
bool compressing = IsCompressionAllowed(source);
|
|
Error status = request.status;
|
|
|
|
if( status == WINIX_ERR_NO_ITEM || status == WINIX_ERR_NO_FUNCTION || status == WINIX_ERR_UNKNOWN_PARAM )
|
|
header = h_404;
|
|
|
|
if( status == WINIX_ERR_PERMISSION_DENIED || status == WINIX_ERR_CANT_CHANGE_USER || status == WINIX_ERR_CANT_CHANGE_GROUP )
|
|
header = h_403;
|
|
|
|
SendHeaders(compressing, header);
|
|
|
|
if( CanSendContent(header) )
|
|
{
|
|
// filtering (html), compressing (deflate) and sending back to the web browser
|
|
FilterCompressSend(compressing, source);
|
|
}
|
|
}
|
|
|
|
|
|
void App::LogUser(const char * msg, uid_t id)
|
|
{
|
|
log << log3 << msg << " ";
|
|
|
|
passwd * p = getpwuid(id);
|
|
|
|
if( p )
|
|
log << p->pw_name;
|
|
else
|
|
log << (int)id;
|
|
|
|
log << logend;
|
|
}
|
|
|
|
|
|
void App::LogGroup(const char * msg, gid_t id, bool put_logend)
|
|
{
|
|
log << log3 << msg << " ";
|
|
|
|
group * g = getgrgid(id);
|
|
|
|
if( g )
|
|
log << g->gr_name;
|
|
else
|
|
log << (int)id;
|
|
|
|
if( put_logend )
|
|
log << logend;
|
|
}
|
|
|
|
|
|
void App::LogUsers()
|
|
{
|
|
uid_t eid, rid;
|
|
|
|
eid = geteuid();
|
|
rid = getuid();
|
|
|
|
if( eid == rid )
|
|
{
|
|
LogUser("App: effective/real user:", eid);
|
|
}
|
|
else
|
|
{
|
|
LogUser("App: effective user:", eid);
|
|
LogUser("App: real user:", rid);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void App::LogEffectiveGroups(std::vector<gid_t> & tab)
|
|
{
|
|
log << log3 << "App: effective groups:";
|
|
|
|
for(size_t i=0 ; i<tab.size() ; ++i)
|
|
{
|
|
bool was_printed = false;
|
|
|
|
for(size_t x=0 ; x<i ; ++x)
|
|
{
|
|
if( tab[i] == tab[x] )
|
|
{
|
|
was_printed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !was_printed )
|
|
LogGroup("", tab[i], false);
|
|
}
|
|
|
|
log << logend;
|
|
}
|
|
|
|
|
|
void App::LogGroups()
|
|
{
|
|
std::vector<gid_t> tab;
|
|
gid_t rgid;
|
|
int len;
|
|
|
|
rgid = getgid();
|
|
len = getgroups(0, 0);
|
|
|
|
if( len <= 0 )
|
|
{
|
|
log << log3 << "App: I can't read how many groups there are" << logend;
|
|
return;
|
|
}
|
|
|
|
tab.resize(len);
|
|
len = getgroups(len, &(tab[0]));
|
|
|
|
if( len == -1 )
|
|
{
|
|
log << log3 << "App: I can't read groups" << logend;
|
|
return;
|
|
}
|
|
|
|
if( len == 1 && rgid == tab[0] )
|
|
{
|
|
LogGroup("App: effective/real group:", rgid);
|
|
}
|
|
else
|
|
{
|
|
tab.resize(len);
|
|
LogEffectiveGroups(tab);
|
|
LogGroup("App: real group:", rgid);
|
|
}
|
|
}
|
|
|
|
|
|
void App::LogUserGroups()
|
|
{
|
|
LogUsers();
|
|
LogGroups();
|
|
}
|
|
|
|
|
|
|
|
bool App::DropPrivileges(const std::string & user, uid_t uid, gid_t gid, bool additional_groups)
|
|
{
|
|
if( additional_groups )
|
|
{
|
|
if( initgroups(user.c_str(), gid) < 0 )
|
|
{
|
|
log << log1 << "App: I can't init groups for user: " << user << logend << logsave;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( setgroups(1, &gid) < 0 )
|
|
{
|
|
log << log1 << "App: I can't init groups for user: " << user << logend << logsave;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// for setting real and saved gid too
|
|
if( setgid(gid) )
|
|
{
|
|
log << log1 << "App: I can't change real and saved gid" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
if( setuid(uid) < 0 )
|
|
{
|
|
log << log1 << "App: I can't drop privileges to user: " << user
|
|
<< " (uid:" << (int)uid << ")" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
if( getuid()==0 || geteuid()==0 )
|
|
{
|
|
log << log1 << "App: sorry, for security reasons you should not run me as the root" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool App::DropPrivileges()
|
|
{
|
|
if( getuid()!=0 && geteuid()!=0 )
|
|
return true;
|
|
|
|
log << log2 << "App: dropping privileges" << logend;
|
|
|
|
if( config.user.empty() )
|
|
{
|
|
log << log1 << "App: you should specify user name in the config file "
|
|
<< "to which I have to drop privileges" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
if( config.group.empty() )
|
|
{
|
|
log << log1 << "App: you should specify group name in the config file "
|
|
<< "to which I have to drop privileges" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
passwd * p = getpwnam(config.user.c_str());
|
|
group * g = getgrnam(config.group.c_str());
|
|
|
|
if( !p )
|
|
{
|
|
log << log1 << "App: there is no such a user as: \"" << config.user << "\"" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
if( !g )
|
|
{
|
|
log << log1 << "App: there is no such a group as: \"" << config.group << "\"" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
if( !DropPrivileges(config.user, p->pw_uid, g->gr_gid, config.additional_groups) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool App::Demonize()
|
|
{
|
|
// in linux fork() should be used twice
|
|
|
|
int pid = fork();
|
|
|
|
if( pid == -1 )
|
|
{
|
|
log << log1 << "App: I can't demonize myself (fork problem)" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
if( pid != 0 )
|
|
{
|
|
// parent
|
|
exit(0);
|
|
}
|
|
|
|
// child
|
|
if( setsid() == -1 )
|
|
{
|
|
log << log1 << "App: I can't demonize myself (setsid problem)" << logend << logsave;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|