host/securitylib/win/setpermissionsmajor.cpp (292 lines of code) (raw):
#include <windows.h>
#include <fstream>
#include <vector>
#include <string>
#include <Aclapi.h>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>
#include "errorexception.h"
#include "scopeguard.h"
#include "createpaths.h"
#include "extendedlengthpath.h"
#include "setpermissions.h"
static SECURITY_ATTRIBUTES g_defaultFileSA;
static SECURITY_ATTRIBUTES g_defaultFileSA_WithIuser;
static SECURITY_ATTRIBUTES g_defaultDirectorySA;
static SECURITY_ATTRIBUTES g_defaultDirectorySA_WithIuser;
static bool g_defaultFileSAInitialized = false;
static bool g_defaultFileSAInitialized_WithIuser = false;
static bool g_defaultDirectorySAInitialized = false;
static bool g_defaultDirectorySAInitialized_WithIuser = false;
static boost::mutex g_defaultFileSAMutex;
static boost::mutex g_defaultFileSAMutex_WithIuser;
static boost::mutex g_defaultDirectorySAMutex;
static boost::mutex g_defaultDirectorySAMutex_WithIuser;
namespace securitylib {
typedef std::vector<char> sid_t;
int defaultFilePermissions() {
return (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
}
sid_t getIusrSid(bool ignoreWebUserChk = false)
{
SID_NAME_USE sidNameUse;
DWORD sidSize = 0;
DWORD domainSize = 0;
sid_t sid;
DWORD res = 0;
if (!LookupAccountName(0, "IUSR", 0, &sidSize, 0, &domainSize, &sidNameUse)) {
res = GetLastError();
if (ignoreWebUserChk){
return sid;
}
else {
throw ERROR_EXCEPTION << "LookupAccountName Error: " << res << '\n';
}
sid.resize(sidSize);
std::vector<char> domainName(domainSize);
if (!LookupAccountName(0, "IUSR", &sid[0], &sidSize, &domainName[0], &domainSize, &sidNameUse)) {
res = GetLastError();
throw ERROR_EXCEPTION << "LookupAccountName Error: " << res << '\n';
}
}
return sid;
}
///
/// Returns System ACL or IUSR ACL for dir or file as specified in input params.
/// Caller is required to free returned ACL after use.
///
PACL getAcl(bool isNameDirectory = false, bool allowWebServer = false, bool ignoreWebUserChk = false)
{
DWORD res = 0;
PSID adminSid = 0;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminSid)) {
res = GetLastError();
throw ERROR_EXCEPTION << "AllocateAndInitializeSid Error: " << res << '\n';
}
ON_BLOCK_EXIT(boost::bind<PVOID>(&FreeSid, adminSid));
PACL adminAcl = 0;
EXPLICIT_ACCESS explicitAccess = { 0 };
explicitAccess.grfAccessMode = SET_ACCESS;
explicitAccess.grfAccessPermissions = TRUSTEE_ACCESS_ALL;
if (isNameDirectory) {
explicitAccess.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
}
else {
explicitAccess.grfInheritance = NO_INHERITANCE;
}
explicitAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
explicitAccess.Trustee.pMultipleTrustee = 0;
explicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
explicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
explicitAccess.Trustee.ptstrName = (LPSTR)adminSid;
res = SetEntriesInAcl(1, &explicitAccess, 0, &adminAcl);
if (ERROR_SUCCESS != res)
{
throw ERROR_EXCEPTION << "SetEntriesInAcl failed for admin. Error: " << res << '\n';
}
ON_BLOCK_EXIT(boost::bind<void>(&LocalFree, adminAcl));
PSID systemSid = 0;
if (!AllocateAndInitializeSid(&SIDAuthNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &systemSid)) {
res = GetLastError();
throw ERROR_EXCEPTION << "AllocateAndInitializeSid Error: " << res << '\n';
}
ON_BLOCK_EXIT(boost::bind<PVOID>(&FreeSid, systemSid));
explicitAccess.Trustee.ptstrName = (LPSTR)systemSid;
PACL systemAcl = 0;
res = SetEntriesInAcl(1, &explicitAccess, adminAcl, &systemAcl);
if (ERROR_SUCCESS != res)
throw ERROR_EXCEPTION << "SetEntriesInAcl failed for SYSTEM. Error: " << res << '\n';
sid_t iusrSid;
PACL iusrAcl = 0;
if (allowWebServer) {
iusrSid = securitylib::getIusrSid(ignoreWebUserChk);
if (!iusrSid.empty()) {
EXPLICIT_ACCESS iusrAccess = { 0 };
iusrAccess.grfAccessMode = SET_ACCESS;
iusrAccess.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;
if (isNameDirectory) {
iusrAccess.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
}
else {
iusrAccess.grfInheritance = NO_INHERITANCE;
}
iusrAccess.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
iusrAccess.Trustee.pMultipleTrustee = 0;
iusrAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID;
iusrAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
iusrAccess.Trustee.ptstrName = (LPSTR)&iusrSid[0];
res = SetEntriesInAcl(1, &iusrAccess, systemAcl, &iusrAcl);
if (ERROR_SUCCESS != res)
{
if (systemAcl) {
ON_BLOCK_EXIT(boost::bind<void>(&LocalFree, systemAcl));
}
throw ERROR_EXCEPTION << "SetEntriesInAcl failed for iusr. Error: " << res << '\n';
}
}
}
if (iusrAcl) {
return iusrAcl;
}
return systemAcl;
}
void setAcl(std::string const& name, int setFlags)
{
PACL effectiveacl = getAcl( (SET_PERMISSIONS_NAME_IS_DIR & setFlags),
(SET_PERMISSIONS_ALLOW_WEB_SERVER & setFlags),
(SET_PERMISSIONS_IGNORE_WEBUSER_CHK & setFlags) );
ON_BLOCK_EXIT(boost::bind<void>(&LocalFree, effectiveacl));
DWORD res = 0;
// this will remove everything and replace it with what is in the acl that is used
res = SetNamedSecurityInfoW( (LPWSTR)ExtendedLengthPath::name(name).c_str(),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
0,
0,
effectiveacl,
0 );
if (ERROR_SUCCESS != res) {
res = GetLastError();
throw ERROR_EXCEPTION << "SetNamedSecurityInfo Error: " << res << '\n';
}
}
// NOTE: name can be file or directory,
// if directroy need to set SET_PERMISSIONS_NAME_IS_DIR or SET_PERMISSIONS_NO_CREATE
void setPermissions(std::string const& name, int setFlags)
{
if (name.empty())
{
throw ERROR_EXCEPTION << "name is blank." << '\n';
}
if (!boost::filesystem::exists(name)) {
if (0 == (SET_PERMISSIONS_NO_CREATE & setFlags)) {
CreatePaths::createPathsAsNeeded(name);
if (0 == (SET_PERMISSIONS_NAME_IS_DIR & setFlags)) {
HANDLE hfile = CreateFileW( ExtendedLengthPath::name(name).c_str(),
GENERIC_ALL,
defaultFilePermissions(),
defaultSecurityAttributes(false,
SET_PERMISSIONS_ALLOW_WEB_SERVER & setFlags,
SET_PERMISSIONS_IGNORE_WEBUSER_CHK & setFlags),
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hfile == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
throw ERROR_EXCEPTION << "FAILED to create file: " << name << ". Error: " << err << '\n';
}
CloseHandle(hfile);
}
else {
bool res = false;
res = CreateDirectoryW( ExtendedLengthPath::name(name).c_str(),
defaultSecurityAttributes(true,
SET_PERMISSIONS_ALLOW_WEB_SERVER & setFlags,
SET_PERMISSIONS_IGNORE_WEBUSER_CHK & setFlags) );
if (!res) {
DWORD err = GetLastError();
if (err != ERROR_ALREADY_EXISTS)
{
throw ERROR_EXCEPTION << "FAILED to create directory: " << name << ". Error: " << err << '\n';
}
}
}
}
else {
return;
}
}
else
{
setAcl(name, setFlags);
}
if (0 != (SET_PERMISSIONS_PARENT_DIR & setFlags)) {
std::string::size_type idx = name.find_last_of("/\\");
if (std::string::npos != idx) {
setAcl( name.substr(0, idx), setFlags );
}
}
}
// TODO This is conterpart of Linux function and to compile the Windows code, added
// this function. This windows code may use this function to set the correct permissions
// for world writeable files.
bool setPermissions(const std::string &path, std::string &errstr)
{
return true;
}
const PSECURITY_DESCRIPTOR getSecurityDescriptor(bool isNameDirectory = false, bool allowWebServer = false, bool ignoreWebUserChk = false)
{
PACL effectiveacl = getAcl(isNameDirectory, allowWebServer, ignoreWebUserChk); // Not freeing effectiveacl here as is referenced by SecurityDescriptor being created
DWORD res = 0;
PSECURITY_DESCRIPTOR pSD = NULL;
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
res = GetLastError();
throw ERROR_EXCEPTION << "LocalAlloc Error: " << res << '\n';
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
res = GetLastError();
ON_BLOCK_EXIT(boost::bind<void>(&LocalFree, pSD));
throw ERROR_EXCEPTION << "InitializeSecurityDescriptor Error: " << res << '\n';
}
if ( !SetSecurityDescriptorDacl(pSD, TRUE, effectiveacl, FALSE) )
{
res = GetLastError();
ON_BLOCK_EXIT(boost::bind<void>(&LocalFree, pSD));
throw ERROR_EXCEPTION << "SetSecurityDescriptorDacl Error: " << res << '\n';
}
return pSD;
}
const LPSECURITY_ATTRIBUTES defaultSecurityAttributes(bool isNameDirectory, bool allowWebServer, bool ignoreWebUserChk)
{
/// default file security attribute
if (!allowWebServer && !isNameDirectory) {
if (!g_defaultFileSAInitialized) {
boost::mutex::scoped_lock gaurd(g_defaultFileSAMutex);
if (!g_defaultFileSAInitialized) {
g_defaultFileSA.bInheritHandle = false;
g_defaultFileSA.nLength = sizeof(SECURITY_ATTRIBUTES);
g_defaultFileSA.lpSecurityDescriptor = NULL;
g_defaultFileSA.lpSecurityDescriptor = getSecurityDescriptor(isNameDirectory, allowWebServer, ignoreWebUserChk);
g_defaultFileSAInitialized = true;
}
}
return &g_defaultFileSA;
}
/// default file security attribute with default permissions to web user
if (allowWebServer && !isNameDirectory) {
if (!g_defaultFileSAInitialized_WithIuser) {
boost::mutex::scoped_lock gaurd(g_defaultFileSAMutex_WithIuser);
if (!g_defaultFileSAInitialized_WithIuser) {
g_defaultFileSA_WithIuser.bInheritHandle = false;
g_defaultFileSA_WithIuser.nLength = sizeof(SECURITY_ATTRIBUTES);
g_defaultFileSA_WithIuser.lpSecurityDescriptor = NULL;
g_defaultFileSA_WithIuser.lpSecurityDescriptor = getSecurityDescriptor(isNameDirectory, allowWebServer, ignoreWebUserChk);
g_defaultFileSAInitialized_WithIuser = true;
}
}
return &g_defaultFileSA_WithIuser;
}
/// default directory security attribute
if (!allowWebServer && isNameDirectory) {
if (!g_defaultDirectorySAInitialized) {
boost::mutex::scoped_lock gaurd(g_defaultDirectorySAMutex);
if (!g_defaultDirectorySAInitialized) {
g_defaultDirectorySA.bInheritHandle = false;
g_defaultDirectorySA.nLength = sizeof(SECURITY_ATTRIBUTES);
g_defaultDirectorySA.lpSecurityDescriptor = NULL;
g_defaultDirectorySA.lpSecurityDescriptor = getSecurityDescriptor(isNameDirectory, allowWebServer, ignoreWebUserChk);
g_defaultDirectorySAInitialized = true;
}
}
return &g_defaultDirectorySA;
}
/// default directory security attribute with default permissions to web user
if (allowWebServer && isNameDirectory) {
if (!g_defaultDirectorySAInitialized_WithIuser) {
boost::mutex::scoped_lock gaurd(g_defaultDirectorySAMutex_WithIuser);
if (!g_defaultDirectorySAInitialized_WithIuser) {
g_defaultDirectorySA_WithIuser.bInheritHandle = false;
g_defaultDirectorySA_WithIuser.nLength = sizeof(SECURITY_ATTRIBUTES);
g_defaultDirectorySA_WithIuser.lpSecurityDescriptor = NULL;
g_defaultDirectorySA_WithIuser.lpSecurityDescriptor = getSecurityDescriptor(isNameDirectory, allowWebServer, ignoreWebUserChk);
g_defaultDirectorySAInitialized_WithIuser = true;
}
}
return &g_defaultDirectorySA_WithIuser;
}
}
} // namespace securitylib