Skip to content

Commit

Permalink
Merge branch 'main' into m36
Browse files Browse the repository at this point in the history
  • Loading branch information
sorinj committed Jun 8, 2022
2 parents 2c3e3b2 + 856d7cc commit d48887e
Show file tree
Hide file tree
Showing 27 changed files with 220 additions and 202 deletions.
8 changes: 6 additions & 2 deletions doc/ClientUpdateProtocolEcdsa.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,13 @@ The server receives an update request XML, public key id, and nonce; it performs

The server attempts to find a matching ECDSA private key for the specified public key id, returning an HTTP error if no such private key exists. Finally, it assembles the update response.

Before sending, the server stores the update response XML (also in UTF-8) in a buffer. It appends the computed SHA-256 hash of the request body+keyid+nonce to the buffer. It then calculates an ECDSA signature over that combined buffer, using the servers private key. It sends the ECDSA signature and the response body + client hash back to the user.
Before sending, the server stores the SHA-256 hash of the request body in a buffer. It appends the SHA-256 hash of the response body, then the cup2key query value (%d:%u, where the first parameter is the keypair id, and the second is the client freshness nonce). It then calculates an ECDSA signature over the SHA-256 hash of that buffer, using the server's private key. It sends the ECDSA signature and the client hash (i.e. hash of the request body) back to the user.

The client receives the response XML, observed client hash, and ECDSA signature. It concatenates its copy of the request hash to the response XML, and attempts to verify the ECDSA signature using its public key. If the signature does not match, the client recognizes that the server response has been tampered in transit, and rejects the exchange.
<img src="https://render.githubusercontent.com/render/math?math=S := \text{Encrypt}_{K_R}\left[\text{Hash}\left(\text{Hash}(\text{request\_body}) %2b \text{Hash}(\text{response\_body}) %2b \text{cup2key\_value}\right)\right]">

The client receives the response XML, observed client hash, and ECDSA signature. It creates a buffer containing the SHA-256 hash of the request body. It then appends the SHA-256 hash of the response body, then the cup2key query value (see above). It then tests whether the received ECDSA signature can be verified to match the SHA-256 hash of this buffer using the public key. If the signature does not match, the client recognizes that the server response has been tampered in transit, and rejects the exchange.

<img src="https://render.githubusercontent.com/render/math?math=\text{Decrypt}_{K_U}[S] \stackrel{?}{=} \text{Hash}\left(\text{Hash}(\text{request\_body}) %2b \text{Hash}(\text{response\_body}) %2b \text{cup2key\_value}\right)">

The client then compares the SHA-256 hash in the response to the original hash of the request. If the hashes do not match, the client recognizes that the request has been tampered in transit, and rejects the exchange.

Expand Down
2 changes: 1 addition & 1 deletion doc/DeveloperSetupGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ These instructions are intended to assist the would-be Omaha developer with sett

We are striving to make the code build with the latest Windows toolchain from Microsoft. Since there is no continuous integration for this project, the code may not build using previous versions of the toolchain.

#### Currently, the supported toolchain is Visual Studio 2019 Update 16.10.4 and Windows SDK 10.0.18362.0. ####
#### Currently, the supported toolchain is Visual Studio 2019 Update 16.11.10 and Windows SDK 10.0.22000.0. ####

The updater runs on Windows 7, 8, and 10. Windows XP is not supported in the current build configuration due to a number of issues, such as thread-safe initializing of static local variables, etc.

Expand Down
207 changes: 68 additions & 139 deletions omaha/base/build.scons
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

Import('env')

import fnmatch
import glob
import operator
import os
Expand Down Expand Up @@ -97,163 +98,92 @@ local_env.Append(ASFLAGS = ['/safeseh'])
# Build these into a library.
local_env.ComponentStaticLibraryMultiarch('base', inputs)

if 'OMAHA_PROTOBUF_BIN_DIR' not in os.environ:
""" Build the Protocol Buffer Compiler executable protoc.exe."""

def GenerateGYPRunAction(source, target, env, for_signature):
"""This Generator creates a command line that when executed runs gyp on the
specified source file to generate the specified target file.

* We explicitly change to the directory of the
source file before running gyp. Not doing this confuses some of the gyp
generators, especially the msvs generator.
* The |--generator-output| option is needed to generate the target in the
target directory. Without |--generator-output|, the target is generated
in the source directory, which is not desirable especially if the source
directory is read-only. Explicitly generating the target in a different
output directory also avoids other warnings.
* |--generator-output| needs to be specified as a
path relative to the source file path to be compatible with the gyp
generators.

Args:
source: A List containing a single .gyp file path.
target: A List containing a single target file path.
env: The Environment in which to build.
for_signature: We ignore this parameter. It indicates to the generator to
just generate the command and not actually run it.

Returns:
A valid command line that when executed runs gyp on the specified source
file to generate the specified target file.
"""

# We use the googleclient gyp since the google3 gyp does not work well with
# python 2.7.
gyp_bat_file = '$GOOGLECLIENT\\third_party\\gyp\\files\\gyp.bat'

# Explicitly using 'cd', since using the chdir option to env.Command() is
# not compatible with parallel builds using the -j option.
gyprun_action = 'cd %s' % source[0].dir.abspath
gyprun_action += ' && ' # Chaining the gyp generation to the 'cd' command.
gyprun_action += '%s %s --generator-output=%s -G msvs_version=2005' % (
env.File(gyp_bat_file).abspath,
source[0].name,
os.path.relpath(target[0].dir.abspath, source[0].dir.abspath))

return gyprun_action

Import('env')
local_env = env.Clone()

# Use gyp to generate protoc.sln, which in turn is fed to VCBuild to generate
# protoc.exe.
# gyp.
gyprun = Builder(generator = GenerateGYPRunAction)
local_env.Append( BUILDERS = {'GYPRun' : gyprun})
protoc_sln = local_env.GYPRun(
target='protoc.sln',
source='$GOOGLE3/net/proto2/contrib/portable/gyp/protoc.gyp')

# VCBuild.
vcbld = Builder(action =
'$GOOGLECLIENT\\third_party\\vc_80\\files\\vc\\vcpackages\\vcbuild.exe' +
' /useenv $SOURCE "Default|Win32"')
local_env.Append( BUILDERS = {'VCBuild' : vcbld})
local_env.VCBuild(target='Default/protoc.exe', source=protoc_sln)
def MultiGlobFilter(env, include, exclude=None):
"""Glob for each pattern in `includes`, filter out patterns in `excludes`."""
files = set()
for pat in include:
files.update(env.Glob(pat, strings=True))
for exclude_pat in (exclude or []):
files.difference_update(fnmatch.filter(files, exclude_pat))
return sorted(files)

""" Build libprotobuf. """

proto_env = env.Clone()

default_protobuf_src_dir = '$GOOGLE3/third_party/protobuf/src'
protobuf_src_dir = os.getenv('OMAHA_PROTOBUF_SRC_DIR', default_protobuf_src_dir)

proto_env = env.Clone()
protobuf_src_path = os.path.join(protobuf_src_dir, 'google/protobuf/')
protobuf_src_path_dir = proto_env.Dir(protobuf_src_path)
proto_env.Dir('protobuf').addRepository(protobuf_src_path_dir)

proto_env.FilterOut(CCFLAGS=['/W4', '/Wall'])

proto_env.Prepend(CCFLAGS=['/W2'])

proto_env.Append(
CPPDEFINES=['LIBPROTOBUF_EXPORTS'],
CCFLAGS=[
'/wd4005',
'/wd4018',
'/wd4065',
'/wd4100',
'/wd4125',
'/wd4146',
'/wd4242',
'/wd4244',
'/wd4267',
'/wd4305',
'/wd4309',
'/wd4310',
'/wd4355',
'/wd4388',
'/wd4389',
'/wd4456',
'/wd4506',
'/wd4548',
'/wd4647',
'/wd4701',
'/wd4702',
'/wd4703',
'/wd4715',
'/wd4798',
'/wd4800',
'/wd4946',
'/wd4309', # truncation of constant value
'/wd4244', # conversion from 'T1' to 'T2', possible loss of data
'/wd4506', # no definition for inline function
],
CPPDEFINES=[
'LIBPROTOBUF_EXPORTS',
# Force the compiler to output public accessors for Google-specific
# ctypes.
'PUBLIC_UNKNOWN_CTYPES',
# Let the compiler silently ignore unknown options in the proto files.
'ALLOW_UNKNOWN_OPTIONS',
'PROTOBUF_INTERNAL_IGNORE_FIELD_NAME_ERRORS_=1',
],
CPPPATH=[
protobuf_src_dir,
protobuf_src_path,
],
)

protobuf_src_path_dir = proto_env.Dir(protobuf_src_path)
proto_env.Dir('protobuf').addRepository(protobuf_src_path_dir)
cc_files = [
'protobuf/compiler/importer.cc',
'protobuf/compiler/parser.cc',
'protobuf/any.cc',
'protobuf/arena.cc',
'protobuf/arenastring.cc',
'protobuf/descriptor.cc',
'protobuf/descriptor.pb.cc',
'protobuf/descriptor_database.cc',
'protobuf/dynamic_message.cc',
'protobuf/extension_set.cc',
'protobuf/extension_set_heavy.cc',
'protobuf/generated_message_reflection.cc',
'protobuf/generated_message_util.cc',
'protobuf/implicit_weak_message.cc',
'protobuf/io/coded_stream.cc',
'protobuf/io/io_win32.cc',
'protobuf/io/printer.cc',
'protobuf/io/strtod.cc',
'protobuf/io/tokenizer.cc',
'protobuf/io/zero_copy_stream.cc',
'protobuf/io/zero_copy_stream_impl.cc',
'protobuf/io/zero_copy_stream_impl_lite.cc',
'protobuf/map_field.cc',
'protobuf/message.cc',
'protobuf/message_lite.cc',
'protobuf/parse_context.cc',
'protobuf/reflection_ops.cc',
'protobuf/repeated_field.cc',
'protobuf/service.cc',
'protobuf/stubs/common.cc',
'protobuf/stubs/int128.cc',
'protobuf/stubs/status.cc',
'protobuf/stubs/stringprintf.cc',
'protobuf/stubs/stringpiece.cc',
'protobuf/stubs/structurally_valid.cc',
'protobuf/stubs/strutil.cc',
'protobuf/stubs/substitute.cc',
'protobuf/text_format.cc',
'protobuf/unknown_field_set.cc',
'protobuf/wire_format.cc',
'protobuf/wire_format_lite.cc',
]
cc_files = MultiGlobFilter(
proto_env,
[
'protobuf/*.cc',
'protobuf/io/*.cc',
'protobuf/stubs/*.cc',
],
exclude=['protobuf/*test*.cc'],
)

proto_env.ComponentStaticLibraryMultiarch(
'libprotobuf', cc_files, COMPONENT_STATIC=True)

if 'OMAHA_PROTOBUF_BIN_DIR' not in os.environ:
""" Build the Protocol Buffer Compiler executable protoc.exe."""

protoc_env = proto_env.Clone()

protoc_env.FilterOut(LINKFLAGS=['/SUBSYSTEM:WINDOWS,5.01'])

protoc_env.Append(
LIBS=[
protoc_env['crt_libs'][protoc_env.Bit('debug')],
'libprotobuf',
],
LINKFLAGS=['/SUBSYSTEM:CONSOLE,5.01'],
)

cc_files = MultiGlobFilter(
protoc_env,
[
'protobuf/compiler/*.cc',
'protobuf/compiler/*/*.cc',
],
exclude = [
'protobuf/compiler/mock_code_generator.cc',
'protobuf/compiler/*test*.cc',
'protobuf/compiler/*/*test*.cc',
],
)
protoc_env.ComponentProgram('Default/protoc.exe', cc_files)

""" Build the CRX Verifier libraries."""

Import('env')
Expand Down Expand Up @@ -290,7 +220,6 @@ cc_files += ['zlib/' +
glob.glob(zlib_src_path_dir.path + os.sep + '*.c')]

""" Add libzip library files."""

libzip_src_path = '$GOOGLE3/third_party/libzip/lib/'
libzip_src_path_dir = local_env.Dir(libzip_src_path)
local_env.Dir('libzip').addRepository(libzip_src_path_dir)
Expand Down
1 change: 1 addition & 0 deletions omaha/base/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ const TCHAR* const kChromeAppId = CHROME_APP_ID;
#define OMAHA_REL_COMPANY_DIR PATH_COMPANY_NAME
#define OMAHA_REL_CRASH_DIR OMAHA_REL_COMPANY_DIR _T("\\CrashReports")
#define OMAHA_REL_POLICY_RESPONSES_DIR OMAHA_REL_COMPANY_DIR _T("\\Policies")
#define OMAHA_REL_TEMP_DIR OMAHA_REL_COMPANY_DIR _T("\\Temp")

// Directories relative to \Google\Update
#define OMAHA_REL_GOOPDATE_INSTALL_DIR \
Expand Down
10 changes: 0 additions & 10 deletions omaha/base/path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,6 @@
#include "omaha/base/utils.h"

namespace omaha {

const TCHAR* const kRegSvr32Cmd1 = _T("regsvr32 ");
const TCHAR* const kRegSvr32Cmd2 = _T("regsvr32.exe ");
const TCHAR* const kRunDll32Cmd1 = _T("rundll32 ");
const TCHAR* const kRunDll32Cmd2 = _T("rundll32.exe ");
const TCHAR* const kMsiExecCmd1 = _T("msiexec ");
const TCHAR* const kMsiExecCmd2 = _T("msiexec.exe ");
const TCHAR* const kDotExe = _T(".exe");


namespace detail {

typedef bool (*Filter)(const WIN32_FIND_DATA&);
Expand Down
13 changes: 13 additions & 0 deletions omaha/common/config_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,19 @@ CString ConfigManager::GetMachineGoopdateInstallDir() const {
return path;
}

CString ConfigManager::GetTempDir() const {
if (::IsUserAnAdmin()) {
CString path;
VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES,
CString(OMAHA_REL_TEMP_DIR),
true,
&path));
return path;
}

return app_util::GetTempDirForImpersonatedOrCurrentUser();
}

bool ConfigManager::IsRunningFromMachineGoopdateInstallDir() const {
return is_running_from_official_machine_dir_;
}
Expand Down
5 changes: 5 additions & 0 deletions omaha/common/config_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ class ConfigManager {
// %ProgramFiles%/Google/Update
CString GetMachineGoopdateInstallDir() const;

// Creates and returns a secure directory, %ProgramFiles%/Google/Temp, if
// running as Admin. Otherwise, returns the %TMP% for the impersonated or
// current user.
CString GetTempDir() const;

// Checks if the running program is executing from the User Goopdate dir.
bool IsRunningFromMachineGoopdateInstallDir() const;

Expand Down
17 changes: 17 additions & 0 deletions omaha/common/config_manager_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,23 @@ TEST_F(ConfigManagerNoOverrideTest, GetMachineSecureOfflineStorageDir) {
EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
}

TEST_F(ConfigManagerNoOverrideTest, GetTempDir) {
CString expected_path;

if (::IsUserAnAdmin()) {
CString program_files;
EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files));
expected_path = program_files + _T("\\") + PATH_COMPANY_NAME + _T("\\Temp");
EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
} else {
expected_path = app_util::GetTempDirForImpersonatedOrCurrentUser();
}

ASSERT_FALSE(expected_path.IsEmpty());
EXPECT_STREQ(expected_path, cm_->GetTempDir());
EXPECT_TRUE(File::Exists(expected_path));
}

TEST_F(ConfigManagerNoOverrideTest, IsRunningFromMachineGoopdateInstallDir) {
EXPECT_FALSE(cm_->IsRunningFromMachineGoopdateInstallDir());
}
Expand Down
13 changes: 9 additions & 4 deletions omaha/common/goopdate_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

#include <ntddndis.h>
#include <winioctl.h>
#include <atlpath.h>
#include <atlsecurity.h>
#include <atlstr.h>

#include "omaha/base/app_util.h"
#include "omaha/base/const_addresses.h"
Expand Down Expand Up @@ -1393,18 +1395,21 @@ bool IsAppInstallWorkerRunning(bool is_machine) {
return !processes.empty();
}

HRESULT WriteInstallerDataToTempFile(const CString& installer_data,
HRESULT WriteInstallerDataToTempFile(const CPath& directory,
const CString& installer_data,
CString* installer_data_file_path) {
ASSERT1(installer_data_file_path);

CORE_LOG(L2, (_T("[WriteInstallerDataToTempFile][directory=%s][data=%s]"),
directory.m_strPath, installer_data));

// TODO(omaha): consider eliminating the special case and simply create an
// empty file.
CORE_LOG(L2, (_T("[WriteInstallerDataToTempFile][data=%s]"), installer_data));
if (installer_data.IsEmpty()) {
if (!directory.IsDirectory() || installer_data.IsEmpty()) {
return S_FALSE;
}

CString temp_file = GetTempFilename(_T("gui"));
CString temp_file = GetTempFilenameAt(directory, _T("gui"));
if (temp_file.IsEmpty()) {
HRESULT hr = HRESULTFromLastError();
CORE_LOG(LE, (_T("[::GetTempFilename failed][0x08%x]"), hr));
Expand Down
Loading

0 comments on commit d48887e

Please sign in to comment.