| Index: cloud_print/service/win/cloud_print_service.cc
|
| diff --git a/cloud_print/service/win/cloud_print_service.cc b/cloud_print/service/win/cloud_print_service.cc
|
| index 7c885918269ac629c6b09ef18d8206d7378d0bee..99030a80863f6d17b9921680b7b89e3f855eb6de 100644
|
| --- a/cloud_print/service/win/cloud_print_service.cc
|
| +++ b/cloud_print/service/win/cloud_print_service.cc
|
| @@ -19,14 +19,13 @@
|
| #include "cloud_print/service/service_state.h"
|
| #include "cloud_print/service/service_switches.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"
|
|
|
| namespace {
|
|
|
| const wchar_t kServiceStateFileName[] = L"Service State";
|
|
|
| -const wchar_t kUserToRunService[] = L"NT AUTHORITY\\LocalService";
|
| -
|
| // The traits class for Windows Service.
|
| class ServiceHandleTraits {
|
| public:
|
| @@ -71,6 +70,8 @@ void InvalidUsage() {
|
| std::cout << "[";
|
| std::cout << " -" << kInstallSwitch;
|
| std::cout << " -" << kUserDataDirSwitch << "=DIRECTORY";
|
| + std::cout << " -" << kRunAsUser << "=USERNAME";
|
| + std::cout << " -" << kRunAsPassword << "=PASSWORD";
|
| std::cout << " [ -" << kQuietSwitch << " ]";
|
| std::cout << "]";
|
| std::cout << "]";
|
| @@ -90,6 +91,9 @@ void InvalidUsage() {
|
| { kUninstallSwitch, "Uninstalls service." },
|
| { kStartSwitch, "Starts service. May be combined with installation." },
|
| { 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) {
|
| @@ -128,36 +132,37 @@ std::string GetOption(const std::string& name, const std::string& default,
|
| return tmp;
|
| }
|
|
|
| -HRESULT WriteFileAsUser(const FilePath& path, const wchar_t* user,
|
| +HRESULT WriteFileAsUser(const FilePath& path, const string16& user,
|
| const char* data, int size) {
|
| - ATL::CAccessToken thread_token;
|
| - if (!thread_token.OpenThreadToken(TOKEN_DUPLICATE | TOKEN_IMPERSONATE)) {
|
| - LOG(ERROR) << "Failed to open thread token.";
|
| - return HResultFromLastError();
|
| - }
|
| + ATL::CAccessToken token;
|
| + ATL::CAutoRevertImpersonation auto_revert(&token);
|
| + if (!user.empty()) {
|
| + 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;
|
| - if (!local_service.LoadAccount(user)) {
|
| - LOG(ERROR) << "Failed create SID.";
|
| - return HResultFromLastError();
|
| - }
|
| + ATL::CSid local_service;
|
| + if (!local_service.LoadAccount(user.c_str())) {
|
| + LOG(ERROR) << "Failed create SID.";
|
| + return HResultFromLastError();
|
| + }
|
|
|
| - ATL::CAccessToken token;
|
| - ATL::CTokenGroups group;
|
| - group.Add(local_service, 0);
|
| + ATL::CTokenGroups group;
|
| + group.Add(local_service, 0);
|
|
|
| - const ATL::CTokenGroups empty_group;
|
| - if (!thread_token.CreateRestrictedToken(&token, empty_group, group)) {
|
| - LOG(ERROR) << "Failed to create restricted token for " << user << ".";
|
| - return HResultFromLastError();
|
| - }
|
| + const ATL::CTokenGroups empty_group;
|
| + if (!thread_token.CreateRestrictedToken(&token, empty_group, group)) {
|
| + LOG(ERROR) << "Failed to create restricted token for " << user << ".";
|
| + return HResultFromLastError();
|
| + }
|
|
|
| - if (!token.Impersonate()) {
|
| - LOG(ERROR) << "Failed to impersonate " << user << ".";
|
| - return HResultFromLastError();
|
| + if (!token.Impersonate()) {
|
| + LOG(ERROR) << "Failed to impersonate " << user << ".";
|
| + return HResultFromLastError();
|
| + }
|
| }
|
| -
|
| - ATL::CAutoRevertImpersonation auto_revert(&token);
|
| if (file_util::WriteFile(path, data, size) != size) {
|
| LOG(ERROR) << "Failed to write file " << path.value() << ".";
|
| return HResultFromLastError();
|
| @@ -182,7 +187,7 @@ class CloudPrintServiceModule
|
| return S_OK;
|
| }
|
|
|
| - HRESULT InstallService() {
|
| + HRESULT InstallService(const string16& user, const string16& password) {
|
| using namespace chrome_launcher_support;
|
|
|
| // TODO(vitalybuka): consider "lite" version if we don't want unregister
|
| @@ -192,8 +197,7 @@ class CloudPrintServiceModule
|
| return hr;
|
|
|
| if (GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION).empty()) {
|
| - LOG(ERROR) << "Found no Chrome installed for all users.";
|
| - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
| + LOG(WARNING) << "Found no Chrome installed for all users.";
|
| }
|
|
|
| hr = UpdateRegistryAppId(true);
|
| @@ -206,6 +210,19 @@ class CloudPrintServiceModule
|
| command_line.AppendSwitch(kServiceSwitch);
|
| 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;
|
| hr = OpenServiceManager(&scm);
|
| if (FAILED(hr))
|
| @@ -216,7 +233,8 @@ class CloudPrintServiceModule
|
| scm, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS,
|
| SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
|
| 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())
|
| return HResultFromLastError();
|
| @@ -277,20 +295,25 @@ class CloudPrintServiceModule
|
| return UninstallService();
|
|
|
| if (command_line.HasSwitch(kInstallSwitch)) {
|
| - if (!command_line.HasSwitch(kUserDataDirSwitch)) {
|
| + if (!command_line.HasSwitch(kUserDataDirSwitch) ||
|
| + !command_line.HasSwitch(kRunAsUser) ||
|
| + !command_line.HasSwitch(kRunAsPassword)) {
|
| InvalidUsage();
|
| return S_FALSE;
|
| }
|
|
|
| - HRESULT hr = ValidateUserDataDir();
|
| + HRESULT hr = ProcessServiceState(command_line.HasSwitch(kQuietSwitch));
|
| if (FAILED(hr))
|
| return hr;
|
|
|
| - hr = ProcessServiceState(command_line.HasSwitch(kQuietSwitch));
|
| - if (FAILED(hr))
|
| - return hr;
|
| + CommandLine::StringType run_as_user =
|
| + command_line.GetSwitchValueNative(kRunAsUser);
|
| + 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))
|
| return StartService();
|
|
|
| @@ -316,16 +339,16 @@ class CloudPrintServiceModule
|
| return S_FALSE;
|
| }
|
|
|
| - HRESULT ValidateUserDataDir() {
|
| + HRESULT ValidateUserDataDir(const string16& user) {
|
| FilePath temp_file;
|
| const char some_data[] = "1234";
|
| if (!file_util::CreateTemporaryFileInDir(user_data_dir_, &temp_file))
|
| return E_FAIL;
|
| - HRESULT hr = WriteFileAsUser(temp_file, kUserToRunService, some_data,
|
| + HRESULT hr = WriteFileAsUser(temp_file, user, some_data,
|
| sizeof(some_data));
|
| if (FAILED(hr)) {
|
| 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() << "\'.";
|
| }
|
| file_util::Delete(temp_file, false);
|
| @@ -343,6 +366,7 @@ class CloudPrintServiceModule
|
| service_state.FromString(contents);
|
|
|
| if (!quiet) {
|
| + std::cout << "\n";
|
| std::cout << file.value() << ":\n";
|
| std::cout << contents << "\n";
|
| }
|
| @@ -377,11 +401,12 @@ class CloudPrintServiceModule
|
| if (is_valid) {
|
| std::string new_contents = service_state.ToString();
|
| if (new_contents != contents) {
|
| - HRESULT hr = WriteFileAsUser(file, kUserToRunService,
|
| - new_contents.c_str(),
|
| - new_contents.size());
|
| - if (FAILED(hr))
|
| - return hr;
|
| + size_t written = file_util::WriteFile(file, new_contents.c_str(),
|
| + new_contents.size());
|
| + if (written != new_contents.size()) {
|
| + LOG(ERROR) << "Failed to write file " << file.value() << ".";
|
| + return HResultFromLastError();
|
| + }
|
| }
|
| }
|
| }
|
|
|