// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sandbox/win/src/job.h"
#include <windows.h>
#include <stddef.h>
#include <utility>
#include "sandbox/win/src/restricted_token.h"
namespace sandbox {
Job::Job() = default;
Job::~Job() = default;
DWORD Job::Init(JobLevel security_level,
DWORD ui_exceptions,
size_t memory_limit) {
if (job_handle_.is_valid())
return ERROR_ALREADY_INITIALIZED;
job_handle_.Set(::CreateJobObject(nullptr, nullptr));
if (!job_handle_.is_valid())
return ::GetLastError();
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {};
// Set the settings for the different security levels. Note: The higher levels
// inherit from the lower levels.
switch (security_level) {
case JobLevel::kLockdown: {
jeli.BasicLimitInformation.LimitFlags |=
JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD;
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS;
[[fallthrough]];
}
case JobLevel::kLimitedUser: {
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS;
jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
jeli.BasicLimitInformation.ActiveProcessLimit = 1;
[[fallthrough]];
}
case JobLevel::kInteractive: {
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS;
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP;
jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
[[fallthrough]];
}
case JobLevel::kUnprotected: {
if (memory_limit) {
jeli.BasicLimitInformation.LimitFlags |=
JOB_OBJECT_LIMIT_PROCESS_MEMORY;
jeli.ProcessMemoryLimit = memory_limit;
}
jeli.BasicLimitInformation.LimitFlags |=
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
break;
}
}
if (!::SetInformationJobObject(job_handle_.get(),
JobObjectExtendedLimitInformation, &jeli,
sizeof(jeli))) {
return ::GetLastError();
}
jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions);
if (!::SetInformationJobObject(job_handle_.get(),
JobObjectBasicUIRestrictions, &jbur,
sizeof(jbur))) {
return ::GetLastError();
}
return ERROR_SUCCESS;
}
bool Job::IsValid() {
return job_handle_.is_valid();
}
HANDLE Job::GetHandle() {
return job_handle_.get();
}
DWORD Job::SetActiveProcessLimit(DWORD processes) {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
if (!job_handle_.is_valid())
return ERROR_NO_DATA;
if (!::QueryInformationJobObject(job_handle_.get(),
JobObjectExtendedLimitInformation, &jeli,
sizeof(jeli), nullptr)) {
return ::GetLastError();
}
jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
jeli.BasicLimitInformation.ActiveProcessLimit = processes;
if (!::SetInformationJobObject(job_handle_.get(),
JobObjectExtendedLimitInformation, &jeli,
sizeof(jeli))) {
return ::GetLastError();
}
return ERROR_SUCCESS;
}
} // namespace sandbox