Example #1
0
VerifyCertTrust(HWND hwndParent, int string_size,
                TCHAR *variables, stack_t **stacktop, void *extra)
{
  TCHAR tmp[MAX_PATH + 1] = { _T('\0') };
  WCHAR filePath[MAX_PATH + 1] = { L'\0' };

  popstring(stacktop, tmp, MAX_PATH);

#if !defined(UNICODE)
    MultiByteToWideChar(CP_ACP, 0, tmp, -1, filePath, MAX_PATH);
#else
    wcsncpy(filePath, tmp, MAX_PATH);
#endif

  LONG retCode = VerifyCertificateTrustForFile(filePath);
  if (retCode == ERROR_SUCCESS) {
    pushstring(stacktop, TEXT("1"), 2);
  } else {
    pushstring(stacktop, TEXT("0"), 2);
  }
}
/**
 * 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 will
 *   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.
 * @return TRUE if the binary matches any of the allowed certificates.
 */
BOOL
DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath,
                                   BOOL allowFallbackKeySkip)
{ 
  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.  (%d)", retCode));
    // Our tests run with a different apply directory for each test.
    // We use this registry key on our test slaves 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.  (%d)", 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.  (%d)", 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.  (%d)", 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.  (%d)", 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.  (%d)", 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.  (%d)", 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.  (%d)", retCode));
      RegCloseKey(subKey);
      continue; // Try the next subkey
    }

    retCode = VerifyCertificateTrustForFile(filePath);
    if (retCode != ERROR_SUCCESS) {
      LOG_WARN(("Error on certificate trust check.  (%d)", 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;
}
Example #3
0
int NS_main(int argc, NS_tchar **argv)
{

  if (argc < 3) {
    fprintf(stderr, \
            "\n" \
            "Application Update Service Test Helper\n" \
            "\n" \
            "Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n"  \
            "   or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \
            "   or: signature-check filepath\n" \
            "   or: setup-symlink dir1 dir2 file symlink\n" \
            "   or: remove-symlink dir1 dir2 file symlink\n" \
            "   or: check-symlink symlink\n" \
            "\n" \
            "  WORKINGDIR  \tThe relative path to the working directory to use.\n" \
            "  INFILE      \tThe relative path from the working directory for the file to\n" \
            "              \tread actions to perform such as finish.\n" \
            "  OUTFILE     \tThe relative path from the working directory for the file to\n" \
            "              \twrite status information.\n" \
            "  SECONDS     \tThe number of seconds to sleep.\n" \
            "  FILETOLOCK  \tThe relative path from the working directory to an existing\n" \
            "              \tfile to open exlusively.\n" \
            "              \tOnly available on Windows platforms and silently ignored on\n" \
            "              \tother platforms.\n" \
            "  LOGFILE     \tThe relative path from the working directory to log the\n" \
            "              \tcommand line arguments.\n" \
            "  ARG2 ARG3...\tArguments to write to the LOGFILE after the preceding command\n" \
            "              \tline arguments.\n" \
            "\n" \
            "Note: All paths must be relative.\n" \
            "\n");
    return 1;
  }

  if (!NS_tstrcmp(argv[1], NS_T("check-signature"))) {
#ifdef XP_WIN
    if (ERROR_SUCCESS == VerifyCertificateTrustForFile(argv[2])) {
      return 0;
    } else {
      return 1;
    }
#else 
    // Not implemented on non-Windows platforms
    return 1;
#endif
  }

  if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) {
#ifdef XP_UNIX
    NS_tchar path[MAXPATHLEN];
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
    mkdir(path, 0755);
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
    mkdir(path, 0755);
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
    FILE * file = NS_tfopen(path, NS_T("w"));
    if (file) {
      NS_tfputs(NS_T("test"), file);
      fclose(file);
    }
    symlink(path, argv[5]);
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
    if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) {
      chmod(path, 0644);
    }
    return 0;
#else
    // Not implemented on non-Unix platforms
    return 1;
#endif
  }

  if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) {
#ifdef XP_UNIX
    NS_tchar path[MAXPATHLEN];
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
    chmod(path, 0755);
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
    unlink(path);
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
    rmdir(path);
    NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
                 NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
    rmdir(path);
    return 0;
#else
    // Not implemented on non-Unix platforms
    return 1;
#endif
  }

  if (!NS_tstrcmp(argv[1], NS_T("check-symlink"))) {
#ifdef XP_UNIX
    struct stat ss;
    lstat(argv[2], &ss);
    return S_ISLNK(ss.st_mode) ? 0 : 1;
#else
    // Not implemented on non-Unix platforms
    return 1;
#endif
  }

  if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
#ifdef XP_WIN
    const int maxWaitSeconds = NS_ttoi(argv[3]);
    LPCWSTR serviceName = argv[2];
    DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds);
    if (SERVICE_STOPPED == serviceState) {
      return 0;
    } else {
      return serviceState;
    }
#else 
    // Not implemented on non-Windows platforms
    return 1;
#endif
  }

  if (!NS_tstrcmp(argv[1], NS_T("wait-for-application-exit"))) {
#ifdef XP_WIN
    const int maxWaitSeconds = NS_ttoi(argv[3]);
    LPCWSTR application = argv[2];
    DWORD ret = WaitForProcessExit(application, maxWaitSeconds);
    if (ERROR_SUCCESS == ret) {
      return 0;
    } else if (WAIT_TIMEOUT == ret) {
      return 1;
    } else {
      return 2;
    }
#else 
    // Not implemented on non-Windows platforms
    return 1;
#endif
  }

  int i = 0;

  if (NS_tchdir(argv[1]) != 0) {
    return 1;
  }

  // File in use test helper section
  if (!NS_tstrcmp(argv[4], NS_T("-s"))) {
    NS_tchar *cwd = NS_tgetcwd(nullptr, 0);
    NS_tchar inFilePath[MAXPATHLEN];
    NS_tsnprintf(inFilePath, sizeof(inFilePath)/sizeof(inFilePath[0]),
                 NS_T("%s/%s"), cwd, argv[2]);
    NS_tchar outFilePath[MAXPATHLEN];
    NS_tsnprintf(outFilePath, sizeof(outFilePath)/sizeof(outFilePath[0]),
                 NS_T("%s/%s"), cwd, argv[3]);

    int seconds = NS_ttoi(argv[5]);
#ifdef XP_WIN
    HANDLE hFile = INVALID_HANDLE_VALUE;
    if (argc == 7) {
      hFile = CreateFileW(argv[6],
                          DELETE | GENERIC_WRITE, 0,
                          nullptr, OPEN_EXISTING, 0, nullptr);
      if (hFile == INVALID_HANDLE_VALUE) {
        WriteMsg(outFilePath, "error_locking");
        return 1;
      }
    }

    WriteMsg(outFilePath, "sleeping");
    while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds)  {
      Sleep(1000);
    }

    if (argc == 7) {
      CloseHandle(hFile);
    }
#else
    WriteMsg(outFilePath, "sleeping");
    while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds)  {
      sleep(1);
    }
#endif
    WriteMsg(outFilePath, "finished");
    return 0;
  }

  // Command line argument test helper section
  NS_tchar logFilePath[MAXPATHLEN];
  NS_tsnprintf(logFilePath, sizeof(logFilePath)/sizeof(logFilePath[0]),
               NS_T("%s"), argv[2]);

  FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
  for (i = 1; i < argc; ++i) {
    fprintf(logFP, LOG_S "\n", argv[i]);
  }

  fclose(logFP);
  logFP = nullptr;

  return 0;
}