1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200

base / mac / process_requirement.h [blame]

// 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_