Removed default account for service.

Added required options run-as-user and run-as-password.
Added code to set security rights required to run service.
File access and chrome binary checks changed from fatal error to just error message.

BUG=141243


Review URL: https://chromiumcodereview.appspot.com/10824294

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151652 0039d316-1c4b-4281-b951-d872f2087c98
parent b379cc5a
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
'service_switches.h', 'service_switches.h',
'win/chrome_launcher.cc', 'win/chrome_launcher.cc',
'win/chrome_launcher.h', 'win/chrome_launcher.h',
'win/local_security_policy.cc',
'win/local_security_policy.h',
], ],
'conditions': [ 'conditions': [
['OS=="win"', { ['OS=="win"', {
......
...@@ -8,6 +8,8 @@ const char kChromeTypeSwitch[] = "type"; ...@@ -8,6 +8,8 @@ const char kChromeTypeSwitch[] = "type";
const char kConsoleSwitch[] = "console"; const char kConsoleSwitch[] = "console";
const char kInstallSwitch[] = "install"; const char kInstallSwitch[] = "install";
const char kQuietSwitch[] = "quiet"; const char kQuietSwitch[] = "quiet";
const char kRunAsPassword[] = "run-as-password";
const char kRunAsUser[] = "run-as-user";
const char kServiceSwitch[] = "service"; const char kServiceSwitch[] = "service";
const char kStartSwitch[] = "start"; const char kStartSwitch[] = "start";
const char kStopSwitch[] = "stop"; const char kStopSwitch[] = "stop";
......
...@@ -9,6 +9,8 @@ extern const char kChromeTypeSwitch[]; ...@@ -9,6 +9,8 @@ extern const char kChromeTypeSwitch[];
extern const char kConsoleSwitch[]; extern const char kConsoleSwitch[];
extern const char kInstallSwitch[]; extern const char kInstallSwitch[];
extern const char kQuietSwitch[]; extern const char kQuietSwitch[];
extern const char kRunAsPassword[];
extern const char kRunAsUser[];
extern const char kServiceSwitch[]; extern const char kServiceSwitch[];
extern const char kStartSwitch[]; extern const char kStartSwitch[];
extern const char kStopSwitch[]; extern const char kStopSwitch[];
......
...@@ -19,14 +19,13 @@ ...@@ -19,14 +19,13 @@
#include "cloud_print/service/service_state.h" #include "cloud_print/service/service_state.h"
#include "cloud_print/service/service_switches.h" #include "cloud_print/service/service_switches.h"
#include "cloud_print/service/win/chrome_launcher.h" #include "cloud_print/service/win/chrome_launcher.h"
#include "cloud_print/service/win/local_security_policy.h"
#include "cloud_print/service/win/resource.h" #include "cloud_print/service/win/resource.h"
namespace { namespace {
const wchar_t kServiceStateFileName[] = L"Service State"; const wchar_t kServiceStateFileName[] = L"Service State";
const wchar_t kUserToRunService[] = L"NT AUTHORITY\\LocalService";
// The traits class for Windows Service. // The traits class for Windows Service.
class ServiceHandleTraits { class ServiceHandleTraits {
public: public:
...@@ -71,6 +70,8 @@ void InvalidUsage() { ...@@ -71,6 +70,8 @@ void InvalidUsage() {
std::cout << "["; std::cout << "[";
std::cout << " -" << kInstallSwitch; std::cout << " -" << kInstallSwitch;
std::cout << " -" << kUserDataDirSwitch << "=DIRECTORY"; std::cout << " -" << kUserDataDirSwitch << "=DIRECTORY";
std::cout << " -" << kRunAsUser << "=USERNAME";
std::cout << " -" << kRunAsPassword << "=PASSWORD";
std::cout << " [ -" << kQuietSwitch << " ]"; std::cout << " [ -" << kQuietSwitch << " ]";
std::cout << "]"; std::cout << "]";
std::cout << "]"; std::cout << "]";
...@@ -90,6 +91,9 @@ void InvalidUsage() { ...@@ -90,6 +91,9 @@ void InvalidUsage() {
{ kUninstallSwitch, "Uninstalls service." }, { kUninstallSwitch, "Uninstalls service." },
{ kStartSwitch, "Starts service. May be combined with installation." }, { kStartSwitch, "Starts service. May be combined with installation." },
{ kStopSwitch, "Stops service." }, { kStopSwitch, "Stops service." },
{ kRunAsUser, "Windows user to run the service in form DOMAIN\\USERNAME. "
"Make sure user has access to printers." },
{ kRunAsPassword, "Password for windows user to run the service" },
}; };
for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) { for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) {
...@@ -128,36 +132,37 @@ std::string GetOption(const std::string& name, const std::string& default, ...@@ -128,36 +132,37 @@ std::string GetOption(const std::string& name, const std::string& default,
return tmp; return tmp;
} }
HRESULT WriteFileAsUser(const FilePath& path, const wchar_t* user, HRESULT WriteFileAsUser(const FilePath& path, const string16& user,
const char* data, int size) { const char* data, int size) {
ATL::CAccessToken thread_token; ATL::CAccessToken token;
if (!thread_token.OpenThreadToken(TOKEN_DUPLICATE | TOKEN_IMPERSONATE)) { ATL::CAutoRevertImpersonation auto_revert(&token);
LOG(ERROR) << "Failed to open thread token."; if (!user.empty()) {
return HResultFromLastError(); ATL::CAccessToken thread_token;
} if (!thread_token.OpenThreadToken(TOKEN_DUPLICATE | TOKEN_IMPERSONATE)) {
LOG(ERROR) << "Failed to open thread token.";
return HResultFromLastError();
}
ATL::CSid local_service; ATL::CSid local_service;
if (!local_service.LoadAccount(user)) { if (!local_service.LoadAccount(user.c_str())) {
LOG(ERROR) << "Failed create SID."; LOG(ERROR) << "Failed create SID.";
return HResultFromLastError(); return HResultFromLastError();
} }
ATL::CAccessToken token; ATL::CTokenGroups group;
ATL::CTokenGroups group; group.Add(local_service, 0);
group.Add(local_service, 0);
const ATL::CTokenGroups empty_group; const ATL::CTokenGroups empty_group;
if (!thread_token.CreateRestrictedToken(&token, empty_group, group)) { if (!thread_token.CreateRestrictedToken(&token, empty_group, group)) {
LOG(ERROR) << "Failed to create restricted token for " << user << "."; LOG(ERROR) << "Failed to create restricted token for " << user << ".";
return HResultFromLastError(); return HResultFromLastError();
} }
if (!token.Impersonate()) { if (!token.Impersonate()) {
LOG(ERROR) << "Failed to impersonate " << user << "."; LOG(ERROR) << "Failed to impersonate " << user << ".";
return HResultFromLastError(); return HResultFromLastError();
}
} }
ATL::CAutoRevertImpersonation auto_revert(&token);
if (file_util::WriteFile(path, data, size) != size) { if (file_util::WriteFile(path, data, size) != size) {
LOG(ERROR) << "Failed to write file " << path.value() << "."; LOG(ERROR) << "Failed to write file " << path.value() << ".";
return HResultFromLastError(); return HResultFromLastError();
...@@ -182,7 +187,7 @@ class CloudPrintServiceModule ...@@ -182,7 +187,7 @@ class CloudPrintServiceModule
return S_OK; return S_OK;
} }
HRESULT InstallService() { HRESULT InstallService(const string16& user, const string16& password) {
using namespace chrome_launcher_support; using namespace chrome_launcher_support;
// TODO(vitalybuka): consider "lite" version if we don't want unregister // TODO(vitalybuka): consider "lite" version if we don't want unregister
...@@ -192,8 +197,7 @@ class CloudPrintServiceModule ...@@ -192,8 +197,7 @@ class CloudPrintServiceModule
return hr; return hr;
if (GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION).empty()) { if (GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION).empty()) {
LOG(ERROR) << "Found no Chrome installed for all users."; LOG(WARNING) << "Found no Chrome installed for all users.";
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
} }
hr = UpdateRegistryAppId(true); hr = UpdateRegistryAppId(true);
...@@ -206,6 +210,19 @@ class CloudPrintServiceModule ...@@ -206,6 +210,19 @@ class CloudPrintServiceModule
command_line.AppendSwitch(kServiceSwitch); command_line.AppendSwitch(kServiceSwitch);
command_line.AppendSwitchPath(kUserDataDirSwitch, user_data_dir_); command_line.AppendSwitchPath(kUserDataDirSwitch, user_data_dir_);
LocalSecurityPolicy local_security_policy;
if (local_security_policy.Open()) {
if (!local_security_policy.IsPrivilegeSet(user, kSeServiceLogonRight)) {
LOG(WARNING) << "Setting " << kSeServiceLogonRight << " for " << user;
if (!local_security_policy.SetPrivilege(user, kSeServiceLogonRight)) {
LOG(ERROR) << "Failed to set" << kSeServiceLogonRight;
LOG(ERROR) << "Make sure you can run the service with this user.";
}
}
} else {
LOG(ERROR) << "Failed to open security policy.";
}
ServiceHandle scm; ServiceHandle scm;
hr = OpenServiceManager(&scm); hr = OpenServiceManager(&scm);
if (FAILED(hr)) if (FAILED(hr))
...@@ -216,7 +233,8 @@ class CloudPrintServiceModule ...@@ -216,7 +233,8 @@ class CloudPrintServiceModule
scm, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS, scm, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
command_line.GetCommandLineString().c_str(), NULL, NULL, NULL, command_line.GetCommandLineString().c_str(), NULL, NULL, NULL,
kUserToRunService, NULL)); user.empty() ? NULL : user.c_str(),
password.empty() ? NULL : password.c_str()));
if (!service.IsValid()) if (!service.IsValid())
return HResultFromLastError(); return HResultFromLastError();
...@@ -277,20 +295,25 @@ class CloudPrintServiceModule ...@@ -277,20 +295,25 @@ class CloudPrintServiceModule
return UninstallService(); return UninstallService();
if (command_line.HasSwitch(kInstallSwitch)) { if (command_line.HasSwitch(kInstallSwitch)) {
if (!command_line.HasSwitch(kUserDataDirSwitch)) { if (!command_line.HasSwitch(kUserDataDirSwitch) ||
!command_line.HasSwitch(kRunAsUser) ||
!command_line.HasSwitch(kRunAsPassword)) {
InvalidUsage(); InvalidUsage();
return S_FALSE; return S_FALSE;
} }
HRESULT hr = ValidateUserDataDir(); HRESULT hr = ProcessServiceState(command_line.HasSwitch(kQuietSwitch));
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
hr = ProcessServiceState(command_line.HasSwitch(kQuietSwitch)); CommandLine::StringType run_as_user =
if (FAILED(hr)) command_line.GetSwitchValueNative(kRunAsUser);
return hr; CommandLine::StringType run_as_password =
command_line.GetSwitchValueNative(kRunAsPassword);
hr = ValidateUserDataDir(run_as_user.c_str());
hr = InstallService(); hr = InstallService(run_as_user.c_str(), run_as_password.c_str());
if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch)) if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch))
return StartService(); return StartService();
...@@ -316,16 +339,16 @@ class CloudPrintServiceModule ...@@ -316,16 +339,16 @@ class CloudPrintServiceModule
return S_FALSE; return S_FALSE;
} }
HRESULT ValidateUserDataDir() { HRESULT ValidateUserDataDir(const string16& user) {
FilePath temp_file; FilePath temp_file;
const char some_data[] = "1234"; const char some_data[] = "1234";
if (!file_util::CreateTemporaryFileInDir(user_data_dir_, &temp_file)) if (!file_util::CreateTemporaryFileInDir(user_data_dir_, &temp_file))
return E_FAIL; return E_FAIL;
HRESULT hr = WriteFileAsUser(temp_file, kUserToRunService, some_data, HRESULT hr = WriteFileAsUser(temp_file, user, some_data,
sizeof(some_data)); sizeof(some_data));
if (FAILED(hr)) { if (FAILED(hr)) {
LOG(ERROR) << "Failed to write user data. Make sure that account \'" << LOG(ERROR) << "Failed to write user data. Make sure that account \'" <<
kUserToRunService << "\'has full access to \'" << user << "\' has full access to \'" <<
user_data_dir_.value() << "\'."; user_data_dir_.value() << "\'.";
} }
file_util::Delete(temp_file, false); file_util::Delete(temp_file, false);
...@@ -343,6 +366,7 @@ class CloudPrintServiceModule ...@@ -343,6 +366,7 @@ class CloudPrintServiceModule
service_state.FromString(contents); service_state.FromString(contents);
if (!quiet) { if (!quiet) {
std::cout << "\n";
std::cout << file.value() << ":\n"; std::cout << file.value() << ":\n";
std::cout << contents << "\n"; std::cout << contents << "\n";
} }
...@@ -377,11 +401,12 @@ class CloudPrintServiceModule ...@@ -377,11 +401,12 @@ class CloudPrintServiceModule
if (is_valid) { if (is_valid) {
std::string new_contents = service_state.ToString(); std::string new_contents = service_state.ToString();
if (new_contents != contents) { if (new_contents != contents) {
HRESULT hr = WriteFileAsUser(file, kUserToRunService, size_t written = file_util::WriteFile(file, new_contents.c_str(),
new_contents.c_str(), new_contents.size());
new_contents.size()); if (written != new_contents.size()) {
if (FAILED(hr)) LOG(ERROR) << "Failed to write file " << file.value() << ".";
return hr; return HResultFromLastError();
}
} }
} }
} }
......
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cloud_print/service/win/local_security_policy.h"
#include <atlsecurity.h>
#include <ntsecapi.h>
#include <windows.h>
#include "base/logging.h"
#include "base/string_util.h"
const wchar_t kSeServiceLogonRight[] = L"SeServiceLogonRight";
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
namespace {
template<class T>
class ScopedLsaMemory {
public:
ScopedLsaMemory() : lsa_memory_(NULL) {
}
~ScopedLsaMemory() {
Close();
}
void Close() {
if (lsa_memory_) {
LsaFreeMemory(lsa_memory_);
lsa_memory_ = NULL;
}
}
T* Get() const {
return lsa_memory_;
}
T** Receive() {
Close();
return &lsa_memory_;
}
private:
T* lsa_memory_;
DISALLOW_COPY_AND_ASSIGN(ScopedLsaMemory);
};
} // namespace
LocalSecurityPolicy::LocalSecurityPolicy() : policy_(NULL) {
}
LocalSecurityPolicy::~LocalSecurityPolicy() {
Close();
}
void LocalSecurityPolicy::Close() {
if (policy_) {
LsaClose(policy_);
policy_ = NULL;
}
}
bool LocalSecurityPolicy::Open() {
DCHECK(!policy_);
Close();
LSA_OBJECT_ATTRIBUTES attributes = {0};
return STATUS_SUCCESS ==
::LsaOpenPolicy(NULL, &attributes,
POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES,
&policy_);
}
bool LocalSecurityPolicy::IsPrivilegeSet(const string16& username,
const string16& privilage) const {
DCHECK(policy_);
ATL::CSid user_sid;
if (!user_sid.LoadAccount(username.c_str())) {
LOG(ERROR) << "Unable to load Sid for" << username;
return false;
}
ScopedLsaMemory<LSA_UNICODE_STRING> rights;
ULONG count = 0;
NTSTATUS status = ::LsaEnumerateAccountRights(
policy_, const_cast<SID*>(user_sid.GetPSID()), rights.Receive(), &count);
if (STATUS_SUCCESS != status || !rights.Get())
return false;
for (size_t i = 0; i < count; ++i) {
if (privilage == rights.Get()[i].Buffer)
return true;
}
return false;
}
bool LocalSecurityPolicy::SetPrivilege(const string16& username,
const string16& privilage) {
DCHECK(policy_);
ATL::CSid user_sid;
if (!user_sid.LoadAccount(username.c_str())) {
LOG(ERROR) << "Unable to load Sid for" << username;
return false;
}
LSA_UNICODE_STRING privilege_string;
string16 privilage_copy(privilage);
privilege_string.Buffer = &privilage_copy[0];
privilege_string.Length = wcslen(privilege_string.Buffer) *
sizeof(privilege_string.Buffer[0]);
privilege_string.MaximumLength = privilege_string.Length +
sizeof(privilege_string.Buffer[0]);
return STATUS_SUCCESS ==
::LsaAddAccountRights(policy_, const_cast<SID*>(user_sid.GetPSID()),
&privilege_string, 1);
}
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CLOUD_PRINT_SERVICE_WIN_LOCAL_SECURITY_POLICY_H_
#define CLOUD_PRINT_SERVICE_WIN_LOCAL_SECURITY_POLICY_H_
#include <wtypes.h> // Has to be before ntsecapi.h
#include <ntsecapi.h>
#include "base/basictypes.h"
#include "base/string16.h"
extern const wchar_t kSeServiceLogonRight[];
class LocalSecurityPolicy {
public:
LocalSecurityPolicy();
~LocalSecurityPolicy();
bool Open();
void Close();
bool IsPrivilegeSet(const string16& username,
const string16& privilage) const;
bool SetPrivilege(const string16& username, const string16& privilage);
private:
LSA_HANDLE policy_;
DISALLOW_COPY_AND_ASSIGN(LocalSecurityPolicy);
};
#endif // CLOUD_PRINT_SERVICE_WIN_LOCAL_SECURITY_POLICY_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment