File: registrycertificates.cpp

package info (click to toggle)
firefox 147.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,320 kB
  • sloc: cpp: 7,607,359; javascript: 6,533,295; ansic: 3,775,223; python: 1,415,500; xml: 634,561; asm: 438,949; java: 186,241; sh: 62,752; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (168 lines) | stat: -rw-r--r-- 6,455 bytes parent folder | download | duplicates (12)
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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#include "registrycertificates.h"
#include "pathhash.h"
#include "updatecommon.h"
#include "updatehelper.h"
#define MAX_KEY_LENGTH 255

/**
 * Verifies if the file path matches any certificate stored in the registry.
 *
 * @param  filePath
 *         The file path of the application to check if allowed.
 * @param  allowFallbackKeySkip
 *         When this is TRUE the fallback registry key can be used to skip the
 *         certificate check.  This is the default since the fallback registry
 *         key is located under HKEY_LOCAL_MACHINE which can't be written to by
 *         a low integrity process.
 *         Note: The maintenance service binary can be used to perform this
 *               check for testing or troubleshooting.
 *         Note: When this is `TRUE` and we are building with
 *               `DISABLE_UPDATER_AUTHENTICODE_CHECK`, this function will
 *               unconditionally return `TRUE` since that flag is meant to
 *               disable specifically this. We don't fall through in the `FALSE`
 *               case since currently the only time when we don't allow the
 *               fallback key is when we are running this for debugging purposes
 *               and, in that case, it's more helpful if we return something
 *               meaningful here.
 *
 * @return TRUE if the binary matches any of the allowed certificates.
 */
BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate,
                                        LPCWSTR filePath,
                                        BOOL allowFallbackKeySkip) {
#ifdef DISABLE_UPDATER_AUTHENTICODE_CHECK
  if (allowFallbackKeySkip) {
    LOG_WARN(("Skipping authenticode check"));
    return TRUE;
  } else {
    LOG(("Performing a diagnostic authenticode check"));
  }
#endif

  WCHAR maintenanceServiceKey[MAX_PATH + 1];
  if (!CalculateRegistryPathFromFilePath(basePathForUpdate,
                                         maintenanceServiceKey)) {
    return FALSE;
  }

  // We use KEY_WOW64_64KEY to always force 64-bit view.
  // The user may have both x86 and x64 applications installed
  // which each register information.  We need a consistent place
  // to put those certificate attributes in and hence why we always
  // force the non redirected registry under Wow6432Node.
  // This flag is ignored on 32bit systems.
  HKEY baseKey;
  LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, maintenanceServiceKey, 0,
                               KEY_READ | KEY_WOW64_64KEY, &baseKey);
  if (retCode != ERROR_SUCCESS) {
    LOG_WARN(("Could not open key.  (%ld)", retCode));
    // Our tests run with a different apply directory for each test.
    // We use this registry key on our test machines to store the
    // allowed name/issuers.
    retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEST_ONLY_FALLBACK_KEY_PATH, 0,
                            KEY_READ | KEY_WOW64_64KEY, &baseKey);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Could not open fallback key.  (%ld)", retCode));
      return FALSE;
    } else if (allowFallbackKeySkip) {
      LOG_WARN(
          ("Fallback key present, skipping VerifyCertificateTrustForFile "
           "check and the certificate attribute registry matching "
           "check."));
      RegCloseKey(baseKey);
      return TRUE;
    }
  }

  // Get the number of subkeys.
  DWORD subkeyCount = 0;
  retCode = RegQueryInfoKeyW(baseKey, nullptr, nullptr, nullptr, &subkeyCount,
                             nullptr, nullptr, nullptr, nullptr, nullptr,
                             nullptr, nullptr);
  if (retCode != ERROR_SUCCESS) {
    LOG_WARN(("Could not query info key.  (%ld)", retCode));
    RegCloseKey(baseKey);
    return FALSE;
  }

  // Enumerate the subkeys, each subkey represents an allowed certificate.
  for (DWORD i = 0; i < subkeyCount; i++) {
    WCHAR subkeyBuffer[MAX_KEY_LENGTH];
    DWORD subkeyBufferCount = MAX_KEY_LENGTH;
    retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer, &subkeyBufferCount,
                            nullptr, nullptr, nullptr, nullptr);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Could not enum certs.  (%ld)", retCode));
      RegCloseKey(baseKey);
      return FALSE;
    }

    // Open the subkey for the current certificate
    HKEY subKey;
    retCode = RegOpenKeyExW(baseKey, subkeyBuffer, 0,
                            KEY_READ | KEY_WOW64_64KEY, &subKey);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Could not open subkey.  (%ld)", retCode));
      continue;  // Try the next subkey
    }

    const int MAX_CHAR_COUNT = 256;
    DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
    WCHAR name[MAX_CHAR_COUNT] = {L'\0'};
    WCHAR issuer[MAX_CHAR_COUNT] = {L'\0'};

    // Get the name from the registry
    retCode = RegQueryValueExW(subKey, L"name", 0, nullptr, (LPBYTE)name,
                               &valueBufSize);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Could not obtain name from registry.  (%ld)", retCode));
      RegCloseKey(subKey);
      continue;  // Try the next subkey
    }

    // Get the issuer from the registry
    valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
    retCode = RegQueryValueExW(subKey, L"issuer", 0, nullptr, (LPBYTE)issuer,
                               &valueBufSize);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Could not obtain issuer from registry.  (%ld)", retCode));
      RegCloseKey(subKey);
      continue;  // Try the next subkey
    }

    CertificateCheckInfo allowedCertificate = {
        name,
        issuer,
    };

    retCode = CheckCertificateForPEFile(filePath, allowedCertificate);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Error on certificate check.  (%ld)", retCode));
      RegCloseKey(subKey);
      continue;  // Try the next subkey
    }

    retCode = VerifyCertificateTrustForFile(filePath);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Error on certificate trust check.  (%ld)", retCode));
      RegCloseKey(subKey);
      continue;  // Try the next subkey
    }

    RegCloseKey(baseKey);
    // Raise the roof, we found a match!
    return TRUE;
  }

  RegCloseKey(baseKey);
  // No certificates match, :'(
  return FALSE;
}