mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
1058 lines
25 KiB
C++
1058 lines
25 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2016 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include <Windows.h>
|
|
#include <LM.h>
|
|
#include <Sddl.h>
|
|
#include "../Interface/Server.h"
|
|
#include "../stringtools.h"
|
|
#include "../urbackupcommon/os_functions.h"
|
|
#include "database.h"
|
|
#include "clientdao.h"
|
|
#include "Aclapi.h"
|
|
#include "tokens.h"
|
|
#include "../common/data.h"
|
|
#include <assert.h>
|
|
#include "../Interface/Types.h"
|
|
|
|
#pragma comment(lib, "netapi32.lib")
|
|
|
|
namespace tokens
|
|
{
|
|
|
|
const DWORD max_dc_user_groups = 100;
|
|
const DWORD max_dc_users = 100;
|
|
const DWORD max_dc_groups = 100;
|
|
|
|
struct Token
|
|
{
|
|
int64 id;
|
|
bool is_user;
|
|
};
|
|
|
|
struct TokenCacheInt
|
|
{
|
|
std::map<std::vector<char>, Token> tokens;
|
|
};
|
|
|
|
|
|
TokenCache::TokenCache( const TokenCache& other )
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
TokenCache::TokenCache()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
TokenCache::~TokenCache()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
void TokenCache::operator=(const TokenCache& other )
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
TokenCacheInt* TokenCache::get()
|
|
{
|
|
return token_cache.get();
|
|
}
|
|
|
|
void TokenCache::reset(TokenCacheInt* cache)
|
|
{
|
|
token_cache.reset(cache);
|
|
}
|
|
|
|
std::wstring get_dc_name()
|
|
{
|
|
LPBYTE dc_name;
|
|
if (NetGetDCName(NULL, NULL, &dc_name) != NERR_Success)
|
|
{
|
|
return std::wstring();
|
|
}
|
|
|
|
std::wstring ret(reinterpret_cast<wchar_t*>(dc_name));
|
|
NetApiBufferFree(dc_name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string get_domain_account(const wchar_t* hostname, const wchar_t* accountname)
|
|
{
|
|
std::vector<char> sid_buffer;
|
|
sid_buffer.resize(sizeof(SID));
|
|
DWORD account_sid_size = sizeof(SID);
|
|
SID_NAME_USE sid_name_use;
|
|
std::wstring referenced_domain;
|
|
referenced_domain.resize(1);
|
|
DWORD referenced_domain_size = 1;
|
|
|
|
BOOL b = LookupAccountNameW(hostname,
|
|
accountname,
|
|
&sid_buffer[0], &account_sid_size, &referenced_domain[0],
|
|
&referenced_domain_size, &sid_name_use);
|
|
|
|
if (!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
referenced_domain.resize(referenced_domain_size);
|
|
sid_buffer.resize(account_sid_size);
|
|
b = LookupAccountNameW(hostname,
|
|
accountname,
|
|
&sid_buffer[0], &account_sid_size, &referenced_domain[0],
|
|
&referenced_domain_size, &sid_name_use);
|
|
}
|
|
|
|
if (referenced_domain.size() != referenced_domain_size)
|
|
{
|
|
referenced_domain.resize(referenced_domain_size);
|
|
}
|
|
|
|
std::string pub_accountname = Server->ConvertFromWchar(accountname);
|
|
if (referenced_domain_size>0)
|
|
{
|
|
pub_accountname = Server->ConvertFromWchar(referenced_domain) + "\\" + pub_accountname;
|
|
}
|
|
|
|
return pub_accountname;
|
|
}
|
|
|
|
std::vector<std::string> get_dc_users()
|
|
{
|
|
std::wstring dc_name = get_dc_name();
|
|
if (dc_name.empty())
|
|
{
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
LPUSER_INFO_0 buf;
|
|
DWORD prefmaxlen = max_dc_users;
|
|
DWORD entriesread = 0;
|
|
DWORD totalentries = 0;
|
|
DWORD resume_handle = 0;
|
|
NET_API_STATUS status;
|
|
std::vector<std::string> ret;
|
|
|
|
do
|
|
{
|
|
status = NetUserEnum(dc_name.c_str(), 0, 0,
|
|
(LPBYTE*)&buf, prefmaxlen, &entriesread,
|
|
&totalentries, &resume_handle);
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA)
|
|
{
|
|
for (DWORD i = 0; i<entriesread; ++i)
|
|
{
|
|
LPUSER_INFO_0 user_info = (buf + i);
|
|
ret.push_back(get_domain_account(dc_name.c_str(), user_info->usri0_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error while enumerating DC users: " + convert((int)status), LL_ERROR);
|
|
}
|
|
|
|
if (status == ERROR_MORE_DATA
|
|
&& ret.size() >= max_dc_users)
|
|
{
|
|
Server->Log("Limiting number of DC users to " + convert(ret.size()), LL_WARNING);
|
|
status = NERR_Success;
|
|
}
|
|
|
|
if (buf != NULL)
|
|
{
|
|
NetApiBufferFree(buf);
|
|
}
|
|
} while (status == ERROR_MORE_DATA);
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> get_users()
|
|
{
|
|
LPUSER_INFO_0 buf;
|
|
DWORD prefmaxlen = MAX_PREFERRED_LENGTH;
|
|
DWORD entriesread = 0;
|
|
DWORD totalentries = 0;
|
|
DWORD resume_handle = 0;
|
|
NET_API_STATUS status;
|
|
std::vector<std::string> ret;
|
|
|
|
do
|
|
{
|
|
status = NetUserEnum(NULL, 0, 0,
|
|
(LPBYTE*)&buf, prefmaxlen, &entriesread,
|
|
&totalentries, &resume_handle);
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA)
|
|
{
|
|
for(DWORD i=0;i<entriesread;++i)
|
|
{
|
|
LPUSER_INFO_0 user_info = (buf+i);
|
|
ret.push_back(Server->ConvertFromWchar(user_info->usri0_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error while enumerating users: "+ convert((int)status), LL_ERROR);
|
|
}
|
|
|
|
if(buf!=NULL)
|
|
{
|
|
NetApiBufferFree(buf);
|
|
}
|
|
}
|
|
while (status == ERROR_MORE_DATA);
|
|
|
|
std::vector<std::string> dc_users = get_dc_users();
|
|
ret.insert(ret.end(), dc_users.begin(), dc_users.end());
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
std::vector<std::string> get_dc_user_groups(std::string username)
|
|
{
|
|
std::wstring dc_name = get_dc_name();
|
|
if (dc_name.empty())
|
|
{
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
if (username.find("\\") != std::string::npos)
|
|
{
|
|
username = getafter("\\", username);
|
|
}
|
|
|
|
LPGROUP_USERS_INFO_0 buf;
|
|
DWORD prefmaxlen = max_dc_user_groups;
|
|
DWORD entriesread = 0;
|
|
DWORD totalentries = 0;
|
|
DWORD resume_handle = 0;
|
|
NET_API_STATUS status;
|
|
std::vector<std::string> ret;
|
|
|
|
status = NetUserGetGroups(dc_name.c_str(), Server->ConvertToWchar(username).c_str(), 0,
|
|
(LPBYTE*)&buf, prefmaxlen, &entriesread,
|
|
&totalentries);
|
|
|
|
if (status == NERR_Success)
|
|
{
|
|
for (DWORD i = 0; i<entriesread; ++i)
|
|
{
|
|
LPGROUP_USERS_INFO_0 user_info = (buf + i);
|
|
ret.push_back(get_domain_account(dc_name.c_str(), user_info->grui0_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error while enumerating DC user groups: " + convert((int)status), LL_ERROR);
|
|
}
|
|
|
|
if (buf != NULL)
|
|
{
|
|
NetApiBufferFree(buf);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> get_user_groups(const std::string& username)
|
|
{
|
|
LPLOCALGROUP_USERS_INFO_0 buf;
|
|
DWORD prefmaxlen = MAX_PREFERRED_LENGTH;
|
|
DWORD entriesread = 0;
|
|
DWORD totalentries = 0;
|
|
DWORD resume_handle = 0;
|
|
NET_API_STATUS status;
|
|
std::vector<std::string> ret;
|
|
|
|
status = NetUserGetLocalGroups(NULL, Server->ConvertToWchar(username).c_str(), 0, LG_INCLUDE_INDIRECT,
|
|
(LPBYTE*)&buf, prefmaxlen, &entriesread,
|
|
&totalentries);
|
|
|
|
if (status == NERR_Success)
|
|
{
|
|
for(DWORD i=0;i<entriesread;++i)
|
|
{
|
|
LPLOCALGROUP_USERS_INFO_0 user_info = (buf+i);
|
|
ret.push_back(Server->ConvertFromWchar(user_info->lgrui0_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error while enumerating user \""+username+"\" groups: "+ convert((int)status), LL_ERROR);
|
|
}
|
|
|
|
if(buf!=NULL)
|
|
{
|
|
NetApiBufferFree(buf);
|
|
}
|
|
|
|
if (username.find("\\") != std::string::npos)
|
|
{
|
|
std::vector<std::string> dc_user_groups = get_dc_user_groups(username);
|
|
ret.insert(ret.end(), dc_user_groups.begin(), dc_user_groups.end());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> get_dc_groups()
|
|
{
|
|
std::vector<std::string> ret;
|
|
std::wstring dc_name = get_dc_name();
|
|
if (!dc_name.empty())
|
|
{
|
|
std::string dc_prefix = Server->ConvertFromWchar(dc_name);
|
|
if (next(dc_prefix, 0, "\\\\"))
|
|
{
|
|
dc_prefix = dc_prefix.substr(2);
|
|
}
|
|
|
|
DWORD prefmaxlen = max_dc_groups;
|
|
DWORD entriesread = 0;
|
|
DWORD totalentries = 0;
|
|
NET_API_STATUS status;
|
|
DWORD_PTR resume_handle = 0;
|
|
LPGROUP_INFO_0 buf2;
|
|
do
|
|
{
|
|
status = NetGroupEnum(dc_name.c_str(), 0,
|
|
(LPBYTE*)&buf2, prefmaxlen, &entriesread,
|
|
&totalentries, &resume_handle);
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA)
|
|
{
|
|
for (DWORD i = 0; i < entriesread; ++i)
|
|
{
|
|
LPGROUP_INFO_0 group_info = (buf2 + i);
|
|
ret.push_back(get_domain_account(dc_name.c_str(), group_info->grpi0_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error while enumerating groups: " + convert((int)status), LL_ERROR);
|
|
}
|
|
|
|
if (status == ERROR_MORE_DATA
|
|
&& ret.size() >= max_dc_groups)
|
|
{
|
|
Server->Log("Limiting number of DC groups to "+convert(ret.size()), LL_WARNING);
|
|
status = NERR_Success;
|
|
}
|
|
|
|
if (buf2 != NULL)
|
|
{
|
|
NetApiBufferFree(buf2);
|
|
}
|
|
} while (status == ERROR_MORE_DATA);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> get_groups()
|
|
{
|
|
LPLOCALGROUP_INFO_0 buf;
|
|
DWORD prefmaxlen = MAX_PREFERRED_LENGTH;
|
|
DWORD entriesread = 0;
|
|
DWORD totalentries = 0;
|
|
NET_API_STATUS status;
|
|
std::vector<std::string> ret;
|
|
DWORD_PTR resume_handle = 0;
|
|
do
|
|
{
|
|
status = NetLocalGroupEnum(NULL, 0,
|
|
(LPBYTE*)&buf, prefmaxlen, &entriesread,
|
|
&totalentries, &resume_handle);
|
|
|
|
if (status == NERR_Success || status == ERROR_MORE_DATA)
|
|
{
|
|
for (DWORD i = 0; i<entriesread; ++i)
|
|
{
|
|
LPLOCALGROUP_INFO_0 group_info = (buf + i);
|
|
ret.push_back(Server->ConvertFromWchar(group_info->lgrpi0_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error while enumerating groups: " + convert((int)status), LL_ERROR);
|
|
}
|
|
|
|
if (buf != NULL)
|
|
{
|
|
NetApiBufferFree(buf);
|
|
}
|
|
} while (status == ERROR_MORE_DATA);
|
|
|
|
std::vector<std::string> dc_groups = get_dc_groups();
|
|
ret.insert(ret.end(), dc_groups.begin(), dc_groups.end());
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool sid_has_profile(const std::string& account_sid)
|
|
{
|
|
HKEY res;
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
Server->ConvertToWchar("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + account_sid).c_str(),
|
|
0, KEY_READ, &res) == ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(res);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool write_token( std::string hostname, bool is_user, std::string accountname, const std::string &token_fn, ClientDAO &dao, const std::string& ext_token)
|
|
{
|
|
std::vector<char> sid_buffer;
|
|
sid_buffer.resize(sizeof(SID));
|
|
|
|
DWORD account_sid_size = sizeof(SID);
|
|
SID_NAME_USE sid_name_use;
|
|
std::wstring referenced_domain;
|
|
referenced_domain.resize(1);
|
|
DWORD referenced_domain_size = 1;
|
|
|
|
BOOL b = false;
|
|
|
|
bool lookup_user = is_user;
|
|
while (!b)
|
|
{
|
|
std::wstring local_username;
|
|
if (accountname.find("\\") != std::string::npos
|
|
|| !lookup_user)
|
|
{
|
|
local_username = Server->ConvertToWchar(accountname);
|
|
}
|
|
else
|
|
{
|
|
local_username = Server->ConvertToWchar(hostname + "\\" + accountname);
|
|
}
|
|
|
|
b = LookupAccountNameW(NULL,
|
|
local_username.c_str(),
|
|
&sid_buffer[0], &account_sid_size, &referenced_domain[0],
|
|
&referenced_domain_size, &sid_name_use);
|
|
|
|
if (!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
referenced_domain.resize(referenced_domain_size);
|
|
sid_buffer.resize(account_sid_size);
|
|
b = LookupAccountNameW(NULL,
|
|
local_username.c_str(),
|
|
&sid_buffer[0], &account_sid_size, &referenced_domain[0],
|
|
&referenced_domain_size, &sid_name_use);
|
|
}
|
|
|
|
if (!b && lookup_user)
|
|
{
|
|
lookup_user = false;
|
|
sid_buffer.resize(sizeof(SID));
|
|
account_sid_size = sizeof(SID);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!b)
|
|
{
|
|
std::string errmsg;
|
|
int64 err = os_last_error(errmsg);
|
|
Server->Log("Error getting account SID of user "+accountname + ". Code: " + convert(err) + " - " + errmsg, LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (referenced_domain.size() != referenced_domain_size)
|
|
{
|
|
referenced_domain.resize(referenced_domain_size);
|
|
}
|
|
|
|
SID* account_sid = reinterpret_cast<SID*>(&sid_buffer[0]);
|
|
|
|
LPWSTR str_account_sid;
|
|
b = ConvertSidToStringSidW(account_sid, &str_account_sid);
|
|
if(!b)
|
|
{
|
|
Server->Log("Error converting SID to string SID. Errorcode: "+convert((int)GetLastError()), LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
std::wstring dacl = std::wstring(L"D:(A;OICI;GA;;;") + str_account_sid + L")"
|
|
+ L"(A;OICI;GA;;;BA)";
|
|
|
|
std::string local_account_sid = Server->ConvertFromWchar(str_account_sid);
|
|
|
|
LocalFree(str_account_sid);
|
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = FALSE;
|
|
|
|
|
|
b=ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
dacl.c_str(),
|
|
SDDL_REVISION_1,
|
|
&(sa.lpSecurityDescriptor),
|
|
NULL);
|
|
|
|
if(!b)
|
|
{
|
|
Server->Log("Error creating security descriptor. Errorcode: "+convert((int)GetLastError()), LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
HANDLE file = CreateFileW(Server->ConvertToWchar(token_fn).c_str(),
|
|
GENERIC_READ | GENERIC_WRITE, 0, &sa, CREATE_ALWAYS, 0, NULL);
|
|
|
|
if(file==INVALID_HANDLE_VALUE)
|
|
{
|
|
Server->Log("Error opening file. Errorcode: "+convert((int)GetLastError()), LL_ERROR);
|
|
LocalFree(sa.lpSecurityDescriptor);
|
|
return false;
|
|
}
|
|
|
|
std::string token;
|
|
if (ext_token.empty())
|
|
{
|
|
token = Server->secureRandomString(20);
|
|
}
|
|
else
|
|
{
|
|
token = ext_token;
|
|
}
|
|
|
|
int i_is_user;
|
|
if (is_user)
|
|
{
|
|
if (sid_has_profile(local_account_sid))
|
|
{
|
|
i_is_user = ClientDAO::c_is_user;
|
|
}
|
|
else
|
|
{
|
|
i_is_user = ClientDAO::c_is_system_user;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i_is_user = ClientDAO::c_is_group;
|
|
}
|
|
|
|
dao.updateFileAccessToken(accountname_normalize(accountname), token, i_is_user);
|
|
|
|
DWORD written=0;
|
|
while(written<token.size())
|
|
{
|
|
b = WriteFile(file, token.data()+written, static_cast<DWORD>(token.size())-written, &written, NULL);
|
|
if(!b)
|
|
{
|
|
Server->Log("Error writing to token file. Errorcode: "+convert((int)GetLastError()), LL_ERROR);
|
|
CloseHandle(file);
|
|
LocalFree(sa.lpSecurityDescriptor);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
CloseHandle(file);
|
|
LocalFree(sa.lpSecurityDescriptor);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string permissions_allow_all()
|
|
{
|
|
CWData token_info;
|
|
//allow to all
|
|
token_info.addChar(ID_GRANT_ACCESS);
|
|
token_info.addVarInt(0);
|
|
|
|
return std::string(token_info.getDataPtr(), token_info.getDataSize());
|
|
}
|
|
|
|
std::string get_hostname_noslash()
|
|
{
|
|
char hostname_c[MAX_PATH];
|
|
hostname_c[0] = 0;
|
|
gethostname(hostname_c, MAX_PATH);
|
|
|
|
return hostname_c;
|
|
}
|
|
|
|
std::string get_hostname()
|
|
{
|
|
std::string hostname = get_hostname_noslash();
|
|
if(!hostname.empty())
|
|
hostname+="\\";
|
|
|
|
return hostname;
|
|
}
|
|
|
|
std::vector<std::string> get_local_users()
|
|
{
|
|
std::string hostname = get_hostname_noslash();
|
|
|
|
HKEY hKey;
|
|
std::vector<std::string> ret;
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
Server->ConvertToWchar("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList").c_str(),
|
|
0, KEY_READ, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
DWORD subkeys;
|
|
if (RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
for (DWORD i = 0; i < subkeys; ++i)
|
|
{
|
|
DWORD key_length = 512;
|
|
wchar_t key_name[512];
|
|
*key_name = 0;
|
|
if (RegEnumKeyExW(hKey, i, key_name, &key_length, NULL, NULL, NULL, NULL)
|
|
== ERROR_SUCCESS)
|
|
{
|
|
PSID sid;
|
|
if (ConvertStringSidToSidW(key_name, &sid))
|
|
{
|
|
DWORD name_length = 0;
|
|
DWORD domain_length = 0;
|
|
SID_NAME_USE name_use;
|
|
std::wstring name;
|
|
std::wstring domain;
|
|
BOOL b = LookupAccountSidW(NULL, sid, NULL, &name_length, NULL, &domain_length, &name_use);
|
|
if (!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
name.resize(name_length);
|
|
domain.resize(domain_length);
|
|
|
|
b = LookupAccountSidW(NULL, sid, &name[0], &name_length, &domain[0], &domain_length, &name_use);
|
|
}
|
|
|
|
if (b)
|
|
{
|
|
if (name_use == SidTypeUser
|
|
|| name_use == SidTypeComputer)
|
|
{
|
|
std::string curr_domain = Server->ConvertFromWchar(domain.c_str());
|
|
if (!curr_domain.empty()
|
|
&& curr_domain != hostname )
|
|
{
|
|
ret.push_back(curr_domain + "\\" + Server->ConvertFromWchar(name.c_str()));
|
|
}
|
|
else
|
|
{
|
|
ret.push_back(Server->ConvertFromWchar(name.c_str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(sid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool read_account_sid( std::vector<char>& sid, std::string hostname, std::string accountname, bool is_user )
|
|
{
|
|
sid.resize(sizeof(SID));
|
|
DWORD account_sid_size = sizeof(SID);
|
|
SID_NAME_USE sid_name_use;
|
|
std::wstring referenced_domain;
|
|
referenced_domain.resize(1);
|
|
DWORD referenced_domain_size = 1;
|
|
|
|
BOOL b = FALSE;
|
|
|
|
std::string local_username = accountname;
|
|
if (local_username.find("\\") == std::string::npos
|
|
&& is_user )
|
|
{
|
|
local_username = hostname + accountname;
|
|
}
|
|
|
|
while (!b)
|
|
{
|
|
b = LookupAccountNameW(NULL, Server->ConvertToWchar(local_username).c_str(),
|
|
&sid[0], &account_sid_size, &referenced_domain[0],
|
|
&referenced_domain_size, &sid_name_use);
|
|
|
|
if (!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
referenced_domain.resize(referenced_domain_size);
|
|
sid.resize(account_sid_size);
|
|
b = LookupAccountNameW(NULL, Server->ConvertToWchar(local_username).c_str(),
|
|
&sid[0], &account_sid_size, &referenced_domain[0],
|
|
&referenced_domain_size, &sid_name_use);
|
|
}
|
|
|
|
if (!b && is_user)
|
|
{
|
|
is_user = false;
|
|
sid.resize(sizeof(SID));
|
|
account_sid_size = sizeof(SID);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!b)
|
|
{
|
|
sid.clear();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void read_all_tokens(ClientDAO* dao, TokenCache& token_cache)
|
|
{
|
|
TokenCacheInt* cache = new TokenCacheInt;
|
|
token_cache.reset(cache);
|
|
|
|
std::vector<std::string> users = get_users();
|
|
|
|
char hostname_c[MAX_PATH];
|
|
hostname_c[0]=0;
|
|
gethostname(hostname_c, MAX_PATH);
|
|
|
|
std::string hostname = hostname_c;
|
|
if(!hostname.empty())
|
|
hostname+="\\";
|
|
|
|
for(size_t i=0;i<users.size();++i)
|
|
{
|
|
Token new_token;
|
|
|
|
std::vector<char> sid;
|
|
if(!read_account_sid(sid, hostname, users[i], true))
|
|
{
|
|
std::string errmsg;
|
|
int64 err = os_last_error(errmsg);
|
|
Server->Log("Cannot get account SID for user "+users[i]+" code: "+convert(err)+" - "+errmsg, LL_ERROR);
|
|
continue;
|
|
}
|
|
|
|
ClientDAO::CondInt64 token_id = dao->getFileAccessTokenId2Alts(accountname_normalize(users[i]), ClientDAO::c_is_user, ClientDAO::c_is_system_user);
|
|
|
|
if(!token_id.exists)
|
|
{
|
|
Server->Log("Token id for user not found", LL_ERROR);
|
|
continue;
|
|
}
|
|
|
|
new_token.id=token_id.value;
|
|
new_token.is_user=true;
|
|
|
|
cache->tokens[sid]=new_token;
|
|
}
|
|
|
|
std::vector<std::string> groups = get_groups();
|
|
|
|
for(size_t i=0;i<groups.size();++i)
|
|
{
|
|
Token new_token;
|
|
|
|
std::vector<char> sid;
|
|
if(!read_account_sid(sid, hostname, groups[i], false))
|
|
{
|
|
std::string errmsg;
|
|
int64 err = os_last_error(errmsg);
|
|
Server->Log("Cannot get account SID for group "+groups[i] + " code: " + convert(err) + " - " + errmsg, LL_ERROR);
|
|
continue;
|
|
}
|
|
|
|
ClientDAO::CondInt64 token_id = dao->getFileAccessTokenId(accountname_normalize(groups[i]), ClientDAO::c_is_group);
|
|
|
|
if(!token_id.exists)
|
|
{
|
|
Server->Log("Token id for user not found", LL_ERROR);
|
|
continue;
|
|
}
|
|
|
|
new_token.id=token_id.value;
|
|
new_token.is_user=false;
|
|
|
|
cache->tokens[sid]=new_token;
|
|
}
|
|
}
|
|
|
|
void read_token_lazy_cache(TokenCache& token_cache, ClientDAO* dao, std::vector<char>& sid)
|
|
{
|
|
PSID psid = reinterpret_cast<PSID>(sid.data());
|
|
DWORD name_length = 0;
|
|
DWORD domain_length = 0;
|
|
SID_NAME_USE name_use;
|
|
std::wstring name;
|
|
std::wstring domain;
|
|
BOOL b = LookupAccountSidW(NULL, psid, NULL, &name_length, NULL, &domain_length, &name_use);
|
|
if (!b && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
name.resize(name_length);
|
|
domain.resize(domain_length);
|
|
|
|
b = LookupAccountSidW(NULL, psid, &name[0], &name_length, &domain[0], &domain_length, &name_use);
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
Token empty = { 0, true };
|
|
token_cache.get()->tokens[sid] = empty;
|
|
return;
|
|
}
|
|
|
|
|
|
std::string curr_domain = Server->ConvertFromWchar(domain.c_str());
|
|
bool local_user = curr_domain == get_hostname_noslash();
|
|
std::string name_full;
|
|
if (!curr_domain.empty()
|
|
&& !local_user)
|
|
{
|
|
name_full = curr_domain +"\\" + Server->ConvertFromWchar(name.c_str());
|
|
}
|
|
else
|
|
{
|
|
name_full = Server->ConvertFromWchar(name.c_str());
|
|
}
|
|
|
|
std::string name_norm = accountname_normalize(name_full);
|
|
|
|
ClientDAO::CondInt64 token_id;
|
|
|
|
bool is_user;
|
|
if (name_use == SidTypeUser
|
|
|| name_use == SidTypeComputer)
|
|
{
|
|
is_user = true;
|
|
token_id = dao->getFileAccessTokenId2Alts(name_norm,
|
|
ClientDAO::c_is_user, ClientDAO::c_is_system_user);
|
|
}
|
|
else
|
|
{
|
|
is_user = false;
|
|
token_id = dao->getFileAccessTokenId(name_norm, ClientDAO::c_is_group);
|
|
}
|
|
|
|
if (!token_id.exists)
|
|
{
|
|
std::string hostname;
|
|
if (curr_domain.empty())
|
|
{
|
|
hostname = get_hostname();
|
|
}
|
|
else if (local_user)
|
|
{
|
|
hostname = curr_domain;
|
|
}
|
|
|
|
std::string user_fn = (is_user ? "user_" : "group_") + bytesToHex(name_norm);
|
|
std::string token_fn = tokens_path + os_file_sep() + user_fn;
|
|
if (!write_token(hostname, is_user, name_full, token_fn, *dao))
|
|
{
|
|
Token empty = { 0, true };
|
|
token_cache.get()->tokens[sid] = empty;
|
|
return;
|
|
}
|
|
|
|
if(is_user)
|
|
token_id = dao->getFileAccessTokenId2Alts(name_norm,
|
|
ClientDAO::c_is_user, ClientDAO::c_is_system_user);
|
|
else
|
|
token_id = dao->getFileAccessTokenId(name_norm, ClientDAO::c_is_group);
|
|
}
|
|
|
|
if (!token_id.exists)
|
|
{
|
|
Token empty = { 0, true };
|
|
token_cache.get()->tokens[sid] = empty;
|
|
return;
|
|
}
|
|
|
|
Token new_token = { token_id.value, is_user };
|
|
token_cache.get()->tokens[sid] = new_token;
|
|
}
|
|
|
|
std::string get_file_tokens( const std::string& fn, ClientDAO* dao, ETokenRight right, TokenCache& token_cache)
|
|
{
|
|
if(token_cache.get()==NULL)
|
|
{
|
|
token_cache.reset(new TokenCacheInt);
|
|
}
|
|
|
|
PACL dacl=NULL;
|
|
PSECURITY_DESCRIPTOR sec_desc;
|
|
|
|
DWORD rc = GetNamedSecurityInfoW(Server->ConvertToWchar(fn).c_str(), SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION|PROTECTED_DACL_SECURITY_INFORMATION|UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
|
|
NULL, &sec_desc);
|
|
|
|
if(rc!=ERROR_SUCCESS)
|
|
{
|
|
Server->Log("Error getting DACL of file \""+fn+"\". Errorcode: "+convert((int)rc), LL_ERROR);
|
|
return std::string();
|
|
}
|
|
|
|
CWData token_info;
|
|
|
|
if(dacl==NULL || dacl->AceCount==0)
|
|
{
|
|
//allow to all
|
|
token_info.addChar(ID_GRANT_ACCESS);
|
|
token_info.addVarInt(0);
|
|
}
|
|
|
|
if(dacl!=NULL)
|
|
{
|
|
char* ptr=reinterpret_cast<char*>(dacl+1);
|
|
for(WORD i=0;i<dacl->AceCount;++i)
|
|
{
|
|
ACE_HEADER* header = reinterpret_cast<ACE_HEADER*>(ptr);
|
|
std::vector<char> sid;
|
|
bool allow;
|
|
bool known;
|
|
ACCESS_MASK mask;
|
|
switch(header->AceType)
|
|
{
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
{
|
|
ACCESS_ALLOWED_ACE* allowed_ace = reinterpret_cast<ACCESS_ALLOWED_ACE*>(ptr);
|
|
|
|
size_t sid_size = header->AceSize+sizeof(DWORD)-sizeof(ACCESS_ALLOWED_ACE);
|
|
sid.resize(sid_size);
|
|
memcpy(&sid[0], &allowed_ace->SidStart, sid_size);
|
|
|
|
allow=true;
|
|
known=true;
|
|
mask = allowed_ace->Mask;
|
|
}
|
|
break;
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
{
|
|
ACCESS_DENIED_ACE* denied_ace = reinterpret_cast<ACCESS_DENIED_ACE*>(ptr);
|
|
|
|
size_t sid_size = header->AceSize+sizeof(DWORD)-sizeof(ACCESS_DENIED_ACE);
|
|
sid.resize(sid_size);
|
|
memcpy(&sid[0], &denied_ace->SidStart, sid_size);
|
|
|
|
allow=false;
|
|
known=true;
|
|
mask = denied_ace->Mask;
|
|
}
|
|
break;
|
|
default:
|
|
known=false;
|
|
break;
|
|
}
|
|
|
|
if(known)
|
|
{
|
|
bool has_access = false;
|
|
|
|
if (right == ETokenRight_Read)
|
|
{
|
|
has_access = mask & GENERIC_READ || mask & GENERIC_ALL
|
|
|| mask & FILE_GENERIC_READ || mask & FILE_ALL_ACCESS;
|
|
}
|
|
else if (right == ETokenRight_Write)
|
|
{
|
|
has_access = mask & GENERIC_WRITE || mask & GENERIC_ALL
|
|
|| mask & FILE_GENERIC_WRITE || mask & FILE_ALL_ACCESS;
|
|
}
|
|
else if(right==ETokenRight_Delete)
|
|
{
|
|
has_access = mask & DELETE || mask & GENERIC_ALL
|
|
|| mask & FILE_ALL_ACCESS;
|
|
}
|
|
else if (right == ETokenRight_DeleteFromDir)
|
|
{
|
|
has_access = (mask & GENERIC_ALL)>0;
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
if (has_access)
|
|
{
|
|
std::map<std::vector<char>, Token>::iterator token_it =
|
|
token_cache.get()->tokens.find(sid);
|
|
|
|
if (token_it == token_cache.get()->tokens.end())
|
|
{
|
|
read_token_lazy_cache(token_cache, dao, sid);
|
|
token_it = token_cache.get()->tokens.find(sid);
|
|
}
|
|
|
|
if (token_it->second.id != 0)
|
|
{
|
|
if (allow)
|
|
{
|
|
token_info.addChar(ID_GRANT_ACCESS);
|
|
token_info.addVarInt(token_it->second.id);
|
|
}
|
|
else
|
|
{
|
|
token_info.addChar(ID_DENY_ACCESS);
|
|
token_info.addVarInt(token_it->second.id);
|
|
}
|
|
}
|
|
else if (!allow)
|
|
{
|
|
Server->Log("Error looking up SID of ACE entry of file \"" + fn + "\"", LL_ERROR);
|
|
LocalFree(sec_desc);
|
|
return std::string();
|
|
}
|
|
}
|
|
}
|
|
|
|
ptr+=header->AceSize;
|
|
}
|
|
}
|
|
|
|
LocalFree(sec_desc);
|
|
|
|
return std::string(token_info.getDataPtr(), token_info.getDataSize());
|
|
}
|
|
|
|
void free_tokencache( TokenCacheInt* cache )
|
|
{
|
|
delete cache;
|
|
}
|
|
|
|
std::string accountname_normalize(const std::string& accountname)
|
|
{
|
|
return strlower(accountname);
|
|
}
|
|
|
|
} //namespace tokens
|