// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MAC_PROCESS_REQUIREMENT_H_
#define BASE_MAC_PROCESS_REQUIREMENT_H_
#include <Security/Security.h>
#include <mach/mach.h>
#include <optional>
#include <string>
#include <vector>
#include "base/apple/scoped_cftyperef.h"
#include "base/base_export.h"
#include "base/containers/span.h"
namespace base::mac {
enum class ValidationCategory : unsigned int;
// Represents constraints on the code signing identity of a peer process.
//
// `ProcessRequirement` is typically used to describe which processes are
// permitted to establish IPC connections, and to validate that a connecting
// process fulfills those constraints.
class BASE_EXPORT ProcessRequirement {
public:
class BASE_EXPORT Builder {
public:
Builder();
~Builder();
Builder(const Builder&) = delete;
Builder& operator=(const Builder&) = delete;
Builder(Builder&&);
Builder& operator=(Builder&&);
// The identifier in the signature must match `identifier`.
// Can be called at most once. See `IdentifierIsOneOf` if multiple
// identifiers can be accepted.
//
// The identifier is typically the executable name or bundle identifier of
// the application.
Builder Identifier(std::string identifier) &&;
// The identifier in the signature must match one of the values
// in`identifiers`.
// Can be called at most once.
//
// The identifier is typically the executable name or bundle identifier of
// the application.
Builder IdentifierIsOneOf(std::vector<std::string> identifiers) &&;
// Equivalent to HasSameTeamIdentifier().HasSameCertificateType()
Builder SignedWithSameIdentity() &&;
// The process must be signed with a certificate that uses the same
// team identifier as this process.
// Can be called at most once.
//
// Note: It is an error to call this without also limiting the certificate
// type via `HasSameCertificateType`, `DeveloperIdCertificateType`, etc.
Builder HasSameTeamIdentifier() &&;
// The process must be signed with the same type of certificate as this
// process.
// Can be called at most once.
Builder HasSameCertificateType() &&;
// The team identifier in the signing certificate matches `team_identifier`.
// Can be called at most once.
//
// Note: It is an error to call this without also limiting the certificate
// type via `HasSameCertificateType`, `DeveloperIdCertificateType`, etc.
Builder TeamIdentifier(std::string team_identifier) &&;
// The certificate used during signing is an Apple Developer ID certificate.
// Can be called at most once.
Builder DeveloperIdCertificateType() &&;
// The certificate used during signing is an Apple App Store certificate.
// Can be called at most once.
Builder AppStoreCertificateType() &&;
// The certificate used during signing is an Apple Development certificate
// that cannot be used for distributing applications.
// Can be called at most once.
Builder DevelopmentCertificateType() &&;
// Validate only the dynamic signature of the application without
// comparing it to the state of the application on disk.
//
// Note that when requesting dynamic validation it is necessary to
// supply the application's Info.plist data when performing
// code signature validation using the resulting requirement.
Builder CheckDynamicValidityOnly() &&;
// Consume the constraints and produce a ProcessRequirement.
// Returns `std::nullopt` on error.
std::optional<ProcessRequirement> Build() &&;
private:
std::vector<std::string> identifiers_;
std::string team_identifier_;
std::optional<ValidationCategory> validation_category_;
bool dynamic_validity_only_ = false;
bool failed_ = false;
bool has_same_team_identifier_called_ = false;
bool has_same_certificate_type_called_ = false;
}; // class Builder
// Use Builder::Build to construct a ProcessRequirement.
ProcessRequirement() = delete;
~ProcessRequirement();
ProcessRequirement(const ProcessRequirement&);
ProcessRequirement& operator=(const ProcessRequirement&);
ProcessRequirement(ProcessRequirement&&);
ProcessRequirement& operator=(ProcessRequirement&&);
// Validate the process represented by `audit_token` against this requirement.
//
// If this requirement was created with `CheckDynamicValidityOnly()` then
// the target process's Info.plist data must be provided in `info_plist_data`.
bool ValidateProcess(audit_token_t audit_token,
base::span<const uint8_t> info_plist_data = {}) const;
// Create a `SecRequirementRef` from the requirement.
// Will return `nullptr` if the requirement does not place any limits
// on the process, such as if `SignedWithSameIdentity()` was used
// from a process with an ad-hoc code signature.
//
// Prefer to use `ValidateProcess` when possible.
apple::ScopedCFTypeRef<SecRequirementRef> AsSecRequirement() const;
// Returns true if only the dynamic signature of the application
// should be validated without comparing it to the state of the
// application on disk.
bool ShouldCheckDynamicValidityOnly() const { return dynamic_validity_only_; }
// Gather metrics to validate the reliability of ProcessRequirement.
// Work is performed asynchronously on a background thread.
static void MaybeGatherMetrics();
static ProcessRequirement AlwaysMatchesForTesting();
static ProcessRequirement NeverMatchesForTesting();
void SetShouldCheckDynamicValidityOnlyForTesting();
struct CSOpsSystemCallProvider {
virtual ~CSOpsSystemCallProvider() = default;
virtual int csops(pid_t pid,
unsigned int ops,
void* useraddr,
size_t usersize) = 0;
virtual bool SupportsValidationCategory() const = 0;
};
// Use `csops_provider` function in place of using the default provider which
// uses the `csops` system call for retrieving code signing information.
// Pass `nullptr` to reset to the default provider.
static void SetCSOpsSystemCallProviderForTesting(
CSOpsSystemCallProvider* csops_provider);
private:
ProcessRequirement(std::vector<std::string> identifiers,
std::string team_identifier,
ValidationCategory validation_category,
bool dynamic_validity_only);
// Returns true if the code signature must be validated to enforce this
// requirement.
// This will be false for unsigned code and true for all signed code.
bool RequiresSignatureValidation() const;
// Do the work of gathering metrics. Called on a background thread.
static void GatherMetrics();
enum ForTesting {
AlwaysMatches,
NeverMatches,
};
explicit ProcessRequirement(ForTesting for_testing);
static apple::ScopedCFTypeRef<SecRequirementRef> AsSecRequirementForTesting(
ForTesting for_testing);
std::vector<std::string> identifiers_;
std::string team_identifier_;
std::optional<ForTesting> for_testing_;
ValidationCategory validation_category_;
bool dynamic_validity_only_ = false;
};
} // namespace base::mac
#endif // BASE_MAC_PROCESS_REQUIREMENT_H_