910e62b5创建于 1月15日历史提交
// 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_