codesign.dart 5.58 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';
import 'package:path/path.dart' as path;

8 9
String get repoRoot => path.normalize(path.join(path.dirname(Platform.script.toFilePath()), '..', '..'));
String get cacheDirectory => path.normalize(path.join(repoRoot, 'bin', 'cache'));
10

11
/// Check mime-type of file at [filePath] to determine if it is binary
12 13 14 15 16 17 18 19 20 21 22 23
bool isBinary(String filePath) {
  final ProcessResult result = Process.runSync(
    'file',
    <String>[
      '--mime-type',
      '-b', // is binary
      filePath,
    ],
  );
  return (result.stdout as String).contains('application/x-mach-binary');
}

24 25 26
/// Find every binary file in the given [rootDirectory]
List<String> findBinaryPaths([String rootDirectory]) {
  rootDirectory ??= cacheDirectory;
27 28 29
  final ProcessResult result = Process.runSync(
    'find',
    <String>[
30
      rootDirectory,
31 32 33 34 35 36 37 38 39 40
      '-type',
      'f',
      '-perm',
      '+111', // is executable
    ],
  );
  final List<String> allFiles = (result.stdout as String).split('\n').where((String s) => s.isNotEmpty).toList();
  return allFiles.where(isBinary).toList();
}

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/// Given the path to a stamp file, read the contents.
///
/// Will throw if the file doesn't exist.
String readStamp(String filePath) {
  final File file = File(filePath);
  if (!file.existsSync()) {
    throw 'Error! Stamp file $filePath does not exist!';
  }
  return file.readAsStringSync().trim();
}

/// Return whether or not the flutter cache is up to date.
bool checkCacheIsCurrent() {
  try {
    final String dartSdkStamp = readStamp(path.join(cacheDirectory, 'engine-dart-sdk.stamp'));
    final String engineVersion = readStamp(path.join(repoRoot, 'bin', 'internal', 'engine.version'));
    return dartSdkStamp == engineVersion;
  } catch (e) {
    print(e);
    return false;
  }
}

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
List<String> get binariesWithEntitlements => List<String>.unmodifiable(<String>[
  'ideviceinfo',
  'idevicename',
  'idevicescreenshot',
  'idevicesyslog',
  'libimobiledevice.6.dylib',
  'libplist.3.dylib',
  'iproxy',
  'libusbmuxd.4.dylib',
  'libssl.1.0.0.dylib',
  'libcrypto.1.0.0.dylib',
  'libzip.5.0.dylib',
  'libzip.5.dylib',
  'gen_snapshot',
  'dart',
  'flutter_tester',
  'gen_snapshot_arm64',
  'gen_snapshot_armv7',
]);

List<String> get expectedEntitlements => List<String>.unmodifiable(<String>[
  'com.apple.security.cs.allow-jit',
  'com.apple.security.cs.allow-unsigned-executable-memory',
  'com.apple.security.cs.allow-dyld-environment-variables',
  'com.apple.security.network.client',
  'com.apple.security.network.server',
  'com.apple.security.cs.disable-library-validation',
]);


/// Check if the binary has the expected entitlements.
bool hasExpectedEntitlements(String binaryPath) {
  try {
    final ProcessResult entitlementResult = Process.runSync(
      'codesign',
      <String>[
        '--display',
        '--entitlements',
        ':-',
        binaryPath,
      ],
    );

    if (entitlementResult.exitCode != 0) {
      print('The `codesign --entitlements` command failed with exit code ${entitlementResult.exitCode}:\n'
        '${entitlementResult.stderr}\n');
      return false;
    }
112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    bool passes = true;
    final String output = entitlementResult.stdout as String;
    for (final String entitlement in expectedEntitlements) {
      final bool entitlementExpected = binariesWithEntitlements.contains(path.basename(binaryPath));
      if (output.contains(entitlement) != entitlementExpected) {
        print('File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} entitlement $entitlement.');
        passes = false;
      }
    }
    return passes;
  } catch (e) {
    print(e);
    return false;
  }
}

void main() {
130 131 132 133 134 135 136 137 138 139 140 141 142 143
  if (!Platform.isMacOS) {
    print('Error! Expected operating system "macos", actual operating system '
      'is: "${Platform.operatingSystem}"');
    exit(1);
  }

  if (!checkCacheIsCurrent()) {
    print(
      'Warning! Your cache is either not present or not matching your flutter\n'
      'version. Run a `flutter` command to update your cache, and re-try this\n'
      'test.');
    exit(1);
  }

144 145
  final List<String> unsignedBinaries = <String>[];
  final List<String> wrongEntitlementBinaries = <String>[];
146
  for (final String binaryPath in findBinaryPaths(cacheDirectory)) {
147
    print('Verifying the code signature of $binaryPath');
148
    final ProcessResult codeSignResult = Process.runSync(
149 150 151 152 153 154
      'codesign',
      <String>[
        '-vvv',
        binaryPath,
      ],
    );
155 156
    if (codeSignResult.exitCode != 0) {
      unsignedBinaries.add(binaryPath);
157
      print('File "$binaryPath" does not appear to be codesigned.\n'
158 159 160 161 162 163 164 165
            'The `codesign` command failed with exit code ${codeSignResult.exitCode}:\n'
            '${codeSignResult.stderr}\n');
      continue;
    } else {
      print('Verifying entitlements of $binaryPath');
      if (!hasExpectedEntitlements(binaryPath)) {
        wrongEntitlementBinaries.add(binaryPath);
      }
166 167 168
    }
  }

169 170 171 172 173 174 175 176 177 178 179 180
  if (unsignedBinaries.isNotEmpty) {
    print('Found ${unsignedBinaries.length} unsigned binaries:');
    unsignedBinaries.forEach(print);
  }

  if (wrongEntitlementBinaries.isNotEmpty) {
    print('Found ${wrongEntitlementBinaries.length} binaries with unexpected entitlements:');
    wrongEntitlementBinaries.forEach(print);
  }

  if (unsignedBinaries.isNotEmpty) {
    // TODO(jmagman): Also exit if `wrongEntitlementBinaries.isNotEmpty` after https://github.com/flutter/flutter/issues/46704 is done.
181 182 183
    exit(1);
  }

184
  print('Verified that binaries are codesigned and have expected entitlements.');
185
}