diff --git a/.allstar/binary_artifacts.yaml b/.allstar/binary_artifacts.yaml new file mode 100644 index 000000000..7176b965c --- /dev/null +++ b/.allstar/binary_artifacts.yaml @@ -0,0 +1,63 @@ +# Ignore reason: These artifacts are used in unit tests or they are tools +# used by the build. It's impractical to make changes at this stage of the +# project to generate the tools from source. +ignorePaths: +- omaha/internal/tools/ApplyTag.exe +- omaha/recovery/lib/bin/dbg/google_update_recovery.lib +- omaha/recovery/lib/bin/opt/google_update_recovery.lib +- omaha/testing/unittest_support/CodeRed.crx3 +- omaha/testing/unittest_support/GoogleUpdateHelper.msi +- omaha/testing/unittest_support/GoogleUpdate_corrupted.exe +- omaha/testing/unittest_support/GoogleUpdate_now_expired_cert.exe +- omaha/testing/unittest_support/GoogleUpdate_old_signature.exe +- omaha/testing/unittest_support/LongRunning.exe +- omaha/testing/unittest_support/LongRunningSilent.exe +- omaha/testing/unittest_support/Omaha_1.2.x_resources/goopdateres_ar.dll +- omaha/testing/unittest_support/Omaha_1.2.x_resources/goopdateres_bg.dll +- omaha/testing/unittest_support/Omaha_1.2.x_resources/goopdateres_ca.dll +- omaha/testing/unittest_support/SaveArguments.exe +- omaha/testing/unittest_support/SaveArguments_OmahaTestSigned.exe +- omaha/testing/unittest_support/SaveArguments_different_ou.exe +- omaha/testing/unittest_support/SaveArguments_multiple_cn.exe +- omaha/testing/unittest_support/SaveArguments_no_cn.exe +- omaha/testing/unittest_support/SaveArguments_unsigned_no_resources.exe +- omaha/testing/unittest_support/SaveArguments_unsigned_wrong_markup_size.exe +- omaha/testing/unittest_support/SaveArguments_unsigned_wrong_markup_value.exe +- omaha/testing/unittest_support/SaveArguments_unsigned_wrong_resource_name.exe +- omaha/testing/unittest_support/SaveArguments_wrong_cn.exe +- omaha/testing/unittest_support/Sha1_4c40dba5f988fae57a57d6457495f98b.exe +- omaha/testing/unittest_support/Sha1_4c40dba5f988fae57a57d6457495f98b_and_sha2_2a9c21acaaa63a3c58a7b9322bee948d.exe +- omaha/testing/unittest_support/chrome_certificate_09E28B26DB593EC4E73286B66499C370.dll +- omaha/testing/unittest_support/chrome_certificate_2912C70C9A2B8A3EF6F6074662D68B8D.dll +- omaha/testing/unittest_support/chrome_setup.exe +- omaha/testing/unittest_support/download_cache_test/{7101D597-3481-4971-AD23-455542964072}/livelysetup.exe +- omaha/testing/unittest_support/download_cache_test/{89640431-FE64-4da8-9860-1A1085A60E13}/gears-win32-opt.msi +- omaha/testing/unittest_support/download_cache_test/{C5CC8735-9BE0-45c5-804C-F117E96047C7}/GoogleUpdateSetup.exe +- omaha/testing/unittest_support/old_google_certificate.dll +- omaha/testing/unittest_support/omaha_1.0.x/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.0.x/goopdate.dll +- omaha/testing/unittest_support/omaha_1.1.x/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.1.x/goopdate.dll +- omaha/testing/unittest_support/omaha_1.1.x/goopdateres_en.dll +- omaha/testing/unittest_support/omaha_1.2.131.7_shell/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.2.183.9_shell/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.2.x/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.2.x/goopdate.dll +- omaha/testing/unittest_support/omaha_1.2.x/goopdateres_en.dll +- omaha/testing/unittest_support/omaha_1.2.x_newer/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.3.x/GoogleUpdate.exe +- omaha/testing/unittest_support/omaha_1.3.x/goopdate.dll +- omaha/testing/unittest_support/omaha_1.3.x/goopdateres_en.dll +- omaha/testing/unittest_support/sha1_06aea76bac46a9e8cfe6d29e45aaf033.sys +- omaha/testing/unittest_support/sha1_14F8FDD167F92402B1570B5DC495C815.sys +- omaha/testing/unittest_support/sha2_0c15be4a15bb0903c901b1d6c265302f.msi +- omaha/testing/unittest_support/sha2_0e4418e2dede36dd2974c3443afb5ce5.msi +- omaha/testing/unittest_support/sha2_2a9c21acaaa63a3c58a7b9322bee948d.exe +- omaha/testing/unittest_support/unsigned.crx3 +- omaha/testing/unittest_support/valid.crx2 +- omaha/testing/unittest_support/valid_no_publisher.crx3 +- omaha/testing/unittest_support/valid_publisher.crx3 +- omaha/tools/MsiTagger.exe +- omaha/tools/resmerge.exe +- third_party/lzma/files/7zr.exe +- third_party/lzma/files/lzma.exe diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..b73030536 --- /dev/null +++ b/.clang-format @@ -0,0 +1,28 @@ +# Defines the Chromium style for automatic reformatting. +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium +# This defaults to 'Auto'. Explicitly set it for a while, so that +# 'vector >' in existing files gets formatted to +# 'vector>'. ('Auto' means that clang-format will only use +# 'int>>' if the file already contains at least one such instance.) +Standard: Cpp11 + +# Make sure code like: +# IPC_BEGIN_MESSAGE_MAP() +# IPC_MESSAGE_HANDLER(WidgetHostViewHost_Update, OnUpdate) +# IPC_END_MESSAGE_MAP() +# gets correctly indented. +MacroBlockBegin: "^\ +BEGIN_COM_MAP|\ +BEGIN_MSG_MAP|\ +BEGIN_OBJECT_MAP|\ +BEGIN_PROP_MAP|\ +BEGIN_REGISTRY_MAP|\ +BEGIN_SERVICE_MAP$" +MacroBlockEnd: "^\ +END_COM_MAP|\ +END_MSG_MAP|\ +END_OBJECT_MAP|\ +END_PROP_MAP|\ +END_REGISTRY_MAP|\ +END_SERVICE_MAP$" \ No newline at end of file diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..412e0f411 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,73 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '35 20 * * 0' + push: + branches: [ "main" ] + workflow_dispatch: + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@v2.3.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@v4 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@v3.24.10 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 7b66acb4a..6b4540c91 100644 --- a/.gitignore +++ b/.gitignore @@ -16,11 +16,9 @@ *.idb *.pdb *.pyc +.vs/ .vscode/ omaha/common/omaha_customization_proxy_clsid.h omaha/proxy_clsids.txt -omaha/scons-out/** -third_party/breakpad/** -third_party/googletest/** -third_party/libzip/** -third_party/zlib/** +omaha/scons-out/ +third_party/lzma/files/**/obj/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..08b70f182 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "third_party/breakpad"] + path = third_party/breakpad + url = https://github.com/google/breakpad.git +[submodule "third_party/googletest"] + path = third_party/googletest + url = https://github.com/google/googletest.git +[submodule "third_party/libzip"] + path = third_party/libzip + url = https://github.com/nih-at/libzip.git +[submodule "third_party/zlib"] + path = third_party/zlib + url = https://github.com/madler/zlib.git diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2759653a0..36acb9c6c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,6 @@ +# CHANGELOG.txt is not going to be be updated beyond this point +# To see what is being changed, please inspect the git log. + ## 2019-06-05 @247089589,251531419 ### Changes diff --git a/README.md b/README.md index 4084e4148..44c7f70e8 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ## This is not an official Google product. -Omaha is the open-source version of Google Update, a program to install requested software and keep it up to date. The Google-branded version of Omaha is used to support software patching (both background updating, and on-demand update checks) for Google Chrome, Earth, and a variety of other Google products on Windows. +Omaha is the open-source version of Google Update, a program to install requested software and keep it up to date. The Google-branded version of Omaha is used to support software patching (both background updating, and on-demand update checks) for Google Chrome, Earth, and a variety of other Google products on Windows 7, 8, and 10. -We know that keeping software updated is both important and hard, and so by open-sourcing this project, our hope is that perhaps we can help others solve this problem. So, if you'd like to get involved, or even use Omaha to support your own software projects, then just follow the instructions in the [Developer Setup Guide](https://github.com/google/omaha/blob/master/doc/DeveloperSetupGuide.md), and you'll be good to go! +For a quick overview of how Omaha works, you can see [this unofficial tutorial](https://fman.io/blog/google-omaha-tutorial/). Please note that it was written by a third party so we cannot guarantee its availability, accuracy or safety. -There is also an unofficial [tutorial](https://fman.io/blog/google-omaha-tutorial/). Please note that it was written by a third party so we cannot guarantee its availability, accuracy or safety. +We know that keeping software updated is both important and hard, and so by open-sourcing this project, our hope is that perhaps we can help others solve this problem. So, if you'd like to get involved, or even use Omaha to support your own software projects, then just follow the instructions in the [Developer Setup Guide](https://github.com/google/omaha/blob/master/doc/DeveloperSetupGuide.md), and you'll be good to go! diff --git a/common/certificate_tag/README b/common/certificate_tag/README index 02007c809..2b5aa02a1 100644 --- a/common/certificate_tag/README +++ b/common/certificate_tag/README @@ -13,3 +13,9 @@ arbitrary data in extensions. Since they are also not hashed when verifying signatures, that data can also be changed without invalidating it. More details are here: http://b/12236017 + +The tool was updated in 2020 to support MSI files: b/172261939, b/165818147. + +The test file is integrated from google3, but is modified here to make it +easier to run outside of google3; see the comment near the beginning of +certificate_tag_test.go diff --git a/common/certificate_tag/certificate_tag.go b/common/certificate_tag/certificate_tag.go index aba87523d..b5c29e97c 100644 --- a/common/certificate_tag/certificate_tag.go +++ b/common/certificate_tag/certificate_tag.go @@ -13,7 +13,7 @@ // limitations under the License. // ======================================================================== // -// certificate_tag.go is a tool for manipulating "tags" in Authenticode-signed, +// Program certificate_tag manipulates "tags" in Authenticode-signed // Windows binaries. // // Traditionally we have inserted tag data after the PKCS#7 blob in the file @@ -26,7 +26,8 @@ // certificates, inserted into the PKCS#7 certificate chain, that can contain // arbitrary data in extensions. Since they are also not hashed when verifying // signatures, that data can also be changed without invalidating it. - +// +// The tool supports PE32 exe files and MSI files. package main import ( @@ -247,6 +248,150 @@ func getAttributeCertificates(bin []byte) (offset, size, sizeOffset int, err err return } +func lengthAsn1(asn1 []byte) (asn1Length int, err error) { + // Read the ASN.1 length of the object. + if asn1[1]&0x80 == 0 { + // Short form length. + asn1Length = int(asn1[1]) + 2 + } else { + numBytes := int(asn1[1] & 0x7f) + if numBytes == 0 || numBytes > 2 { + err = fmt.Errorf("bad number of bytes in ASN.1 length: %d", numBytes) + return + } + if len(asn1) < numBytes+2 { + err = errors.New("ASN.1 structure truncated") + return + } + asn1Length = int(asn1[2]) + if numBytes == 2 { + asn1Length <<= 8 + asn1Length |= int(asn1[3]) + } + asn1Length += 2 + numBytes + } + return +} + +func parseSignedData(asn1Data []byte) (*signedData, error) { + var signedData signedData + if _, err := asn1.Unmarshal(asn1Data, &signedData); err != nil { + return nil, errors.New("authenticodetag: error while parsing SignedData structure: " + err.Error()) + } + + der, err := asn1.Marshal(signedData) + if err != nil { + return nil, errors.New("authenticodetag: error while marshaling SignedData structure: " + err.Error()) + } + + if !bytes.Equal(der, asn1Data) { + return nil, errors.New("authenticodetag: ASN.1 parse/unparse test failed") + } + return &signedData, nil +} + +func getSuperfluousCert(signedData *signedData) (cert *x509.Certificate, index int, err error) { + n := len(signedData.PKCS7.Certs) + if n == 0 { + return nil, -1, nil + } + + for index, certASN1 := range signedData.PKCS7.Certs { + if cert, err = x509.ParseCertificate(certASN1.FullBytes); err != nil { + return nil, -1, err + } + + for _, ext := range cert.Extensions { + if !ext.Critical && ext.Id.Equal(oidChromeTag) { + return cert, index, nil + } + } + } + + return nil, -1, nil +} + +// SetSuperfluousCertTag modifies signedData, adding the superfluous cert with the given tag. +// It returns the asn1 serialization of the modified signedData. +func SetSuperfluousCertTag(signedData *signedData, tag []byte) ([]byte, error) { + cert, index, err := getSuperfluousCert(signedData) + if err != nil { + return nil, fmt.Errorf("couldn't identity if any existing certificates are superfluous because of parse error: %w", err) + } + + if cert != nil { + pkcs7 := &signedData.PKCS7 + certs := pkcs7.Certs + + var newCerts []asn1.RawValue + newCerts = append(newCerts, certs[:index]...) + newCerts = append(newCerts, certs[index+1:]...) + pkcs7.Certs = newCerts + } + + notBefore := parseUnixTimeOrDie(notBeforeTime) + notAfter := parseUnixTimeOrDie(notAfterTime) + + priv, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) + if err != nil { + return nil, err + } + + issuerTemplate := x509.Certificate{ + SerialNumber: new(big.Int).SetInt64(1), + Subject: pkix.Name{ + CommonName: "Unknown issuer", + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + SignatureAlgorithm: x509.SHA1WithRSA, + BasicConstraintsValid: true, + IsCA: true, + } + + template := x509.Certificate{ + SerialNumber: new(big.Int).SetInt64(1), + Subject: pkix.Name{ + CommonName: "Dummy certificate", + }, + Issuer: pkix.Name{ + CommonName: "Unknown issuer", + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + SignatureAlgorithm: x509.SHA1WithRSA, + BasicConstraintsValid: true, + IsCA: false, + ExtraExtensions: []pkix.Extension{ + { + // This includes the tag in an extension in the + // certificate. + Id: oidChromeTag, + Value: tag, + }, + }, + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &issuerTemplate, &priv.PublicKey, priv) + if err != nil { + return nil, err + } + + signedData.PKCS7.Certs = append(signedData.PKCS7.Certs, asn1.RawValue{ + FullBytes: derBytes, + }) + + asn1Bytes, err := asn1.Marshal(*signedData) + if err != nil { + return nil, err + } + return asn1Bytes, nil +} + // Certificate constants. See // http://msdn.microsoft.com/en-us/library/ms920091.aspx. const ( @@ -293,29 +438,10 @@ func processAttributeCertificates(certs []byte) (asn1, appendedTag []byte, err e return } - // Read the ASN.1 length of the object. - var asn1Length int - if asn1[1]&0x80 == 0 { - // Short form length. - asn1Length = int(asn1[1]) + 2 - } else { - numBytes := int(asn1[1] & 0x7f) - if numBytes == 0 || numBytes > 2 { - err = fmt.Errorf("bad number of bytes in ASN.1 length: %d", numBytes) - return - } - if len(asn1) < numBytes+2 { - err = errors.New("ASN.1 structure truncated") - return - } - asn1Length = int(asn1[2]) - if numBytes == 2 { - asn1Length <<= 8 - asn1Length |= int(asn1[3]) - } - asn1Length += 2 + numBytes + asn1Length, err := lengthAsn1(asn1) + if err != nil { + return } - appendedTag = asn1[asn1Length:] asn1 = asn1[:asn1Length] @@ -334,19 +460,44 @@ type signedData struct { } `asn1:"explicit,tag:0"` } -// Binary represents a PE binary. -type Binary struct { +// Binary represents a taggable binary of any format. +type Binary interface { + AppendedTag() (data []byte, ok bool) + asn1Data() []byte + buildBinary(asn1Data, tag []byte) ([]byte, error) // the tag argument is a legacy-style tag. + RemoveAppendedTag() (contents []byte, err error) + SetAppendedTag(tagContents []byte) (contents []byte, err error) + getSuperfluousCert() (cert *x509.Certificate, index int, err error) + SetSuperfluousCertTag(tag []byte) (contents []byte, err error) + certificateOffset() int64 +} + +// PE32Binary represents a PE binary. +type PE32Binary struct { contents []byte // the full file attrCertOffset int // the offset to the attribute certificates table certSizeOffset int // the offset to the size of the attribute certificates table - asn1Data []byte // the PKCS#7, SignedData in DER form. + asn1Bytes []byte // the PKCS#7, SignedData in DER form. appendedTag []byte // the appended tag, if any. - signedData *signedData // the parsed, SignedData structure. + signedData *signedData // the parsed SignedData structure. +} + +// NewBinary returns a Binary that contains details of the PE32 or MSI binary given in |contents|. +// |contents| is modified if it is an MSI file. +func NewBinary(contents []byte) (Binary, error) { + pe, peErr := NewPE32Binary(contents) + if peErr == nil { + return pe, peErr + } + msi, msiErr := NewMSIBinary(contents) + if msiErr == nil { + return msi, msiErr + } + return nil, errors.New("Could not parse input as either PE32 or MSI:\nPE32: " + peErr.Error() + "\nMSI: " + msiErr.Error()) } -// NewBinary returns a Binary that contains details of the PE binary given in -// contents. -func NewBinary(contents []byte) (*Binary, error) { +// NewPE32Binary returns a Binary that contains details of the PE32 binary given in contents. +func NewPE32Binary(contents []byte) (*PE32Binary, error) { offset, size, certSizeOffset, err := getAttributeCertificates(contents) if err != nil { return nil, errors.New("authenticodetag: error parsing headers: " + err.Error()) @@ -358,32 +509,31 @@ func NewBinary(contents []byte) (*Binary, error) { return nil, errors.New("authenticodetag: error parsing attribute certificate section: " + err.Error()) } - var signedData signedData - if _, err := asn1.Unmarshal(asn1Data, &signedData); err != nil { - return nil, errors.New("authenticodetag: error while parsing SignedData structure: " + err.Error()) - } - - der, err := asn1.Marshal(signedData) + signedData, err := parseSignedData(asn1Data) if err != nil { - return nil, errors.New("authenticodetag: error while marshaling SignedData structure: " + err.Error()) - } - - if !bytes.Equal(der, asn1Data) { - return nil, errors.New("authenticodetag: ASN.1 parse/unparse test failed: " + err.Error()) + return nil, err } - return &Binary{ + return &PE32Binary{ contents: contents, attrCertOffset: offset, certSizeOffset: certSizeOffset, - asn1Data: asn1Data, + asn1Bytes: asn1Data, appendedTag: appendedTag, - signedData: &signedData, + signedData: signedData, }, nil } +func (bin *PE32Binary) certificateOffset() int64 { + return int64(bin.attrCertOffset) +} + +func (bin *PE32Binary) asn1Data() []byte { + return bin.asn1Bytes +} + // AppendedTag returns the appended tag, if any. -func (bin *Binary) AppendedTag() (data []byte, ok bool) { +func (bin *PE32Binary) AppendedTag() (data []byte, ok bool) { isAllZero := true for _, b := range bin.appendedTag { if b != 0 { @@ -400,7 +550,7 @@ func (bin *Binary) AppendedTag() (data []byte, ok bool) { // buildBinary builds a PE binary based on bin but with the given SignedData // and appended tag. -func (bin *Binary) buildBinary(asn1Data, tag []byte) (contents []byte) { +func (bin *PE32Binary) buildBinary(asn1Data, tag []byte) (contents []byte, err error) { contents = append(contents, bin.contents[:bin.certSizeOffset]...) for (len(asn1Data)+len(tag))&7 > 0 { tag = append(tag, 0) @@ -417,42 +567,35 @@ func (bin *Binary) buildBinary(asn1Data, tag []byte) (contents []byte) { binary.LittleEndian.PutUint16(header[6:], attributeCertificateTypePKCS7SignedData) contents = append(contents, header[:]...) contents = append(contents, asn1Data...) - return append(contents, tag...) + return append(contents, tag...), nil } -func (bin *Binary) RemoveAppendedTag() (contents []byte, err error) { +// RemoveAppendedTag removes a legacy-style tag from the end of the signedData container. +func (bin *PE32Binary) RemoveAppendedTag() (contents []byte, err error) { if _, ok := bin.AppendedTag(); !ok { return nil, errors.New("authenticodetag: no appended tag found") } - return bin.buildBinary(bin.asn1Data, nil), nil + return bin.buildBinary(bin.asn1Data(), nil) } -func (bin *Binary) SetAppendedTag(tagContents []byte) (contents []byte, err error) { - return bin.buildBinary(bin.asn1Data, tagContents), nil +// SetAppendedTag adds a legacy-style tag at the end of the signedData container. +func (bin *PE32Binary) SetAppendedTag(tagContents []byte) (contents []byte, err error) { + return bin.buildBinary(bin.asn1Data(), tagContents) } // oidChromeTag is an OID that we use for the extension in the superfluous // certificate. It's in the Google arc, but not officially assigned. var oidChromeTag = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 11129, 2, 1, 9999}) -func (bin *Binary) getSuperfluousCert() (cert *x509.Certificate, err error) { - n := len(bin.signedData.PKCS7.Certs) - if n == 0 { - return nil, nil - } - - if cert, err = x509.ParseCertificate(bin.signedData.PKCS7.Certs[n-1].FullBytes); err != nil { - return nil, err - } +// oidChromeTagSearchBytes is used to find the final location of the tag buffer. +// This is followed by the 2-byte length of the buffer, and then the buffer itself. +// x060b - OID and length; 11 bytes of OID; x0482 - Octet string, 2-byte length prefix. +// (In practice our tags are 8206 bytes, so the size fits in two bytes.) +var oidChromeTagSearchBytes = []byte{0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0xce, 0x0f, 0x04, 0x82} - for _, ext := range cert.Extensions { - if !ext.Critical && ext.Id.Equal(oidChromeTag) { - return cert, nil - } - } - - return nil, nil +func (bin *PE32Binary) getSuperfluousCert() (cert *x509.Certificate, index int, err error) { + return getSuperfluousCert(bin.signedData) } func parseUnixTimeOrDie(unixTime string) time.Time { @@ -465,75 +608,610 @@ func parseUnixTimeOrDie(unixTime string) time.Time { // SetSuperfluousCertTag returns a PE binary based on bin, but where the // superfluous certificate contains the given tag data. -func (bin *Binary) SetSuperfluousCertTag(tag []byte) (contents []byte, err error) { - cert, err := bin.getSuperfluousCert() - if cert != nil { - pkcs7 := &bin.signedData.PKCS7 - pkcs7.Certs = pkcs7.Certs[:len(pkcs7.Certs)-1] +// The (parsed) bin.signedData is modified; but bin.asn1Bytes, which contains +// the raw original bytes, is not. +func (bin *PE32Binary) SetSuperfluousCertTag(tag []byte) (contents []byte, err error) { + asn1Bytes, err := SetSuperfluousCertTag(bin.signedData, tag) + if err != nil { + return nil, err } - notBefore := parseUnixTimeOrDie(notBeforeTime) - notAfter := parseUnixTimeOrDie(notAfterTime) + return bin.buildBinary(asn1Bytes, bin.appendedTag) +} - priv, err := rsa.GenerateKey(rand.Reader, rsaKeyBits) +// Variables now defined as secT and offT were initially hardcoded as |int| for simplicity, +// but this produced errors when run on a Windows machine, which defaulted to a 32-bit arch. +// See b/172261939. + +// secT is the type of a sector ID, or an index into the FAT (which describes what is in +// that sector), or a number of sectors. +type secT uint32 + +// offT is the type of an offset into the MSI file contents, or a number of bytes. +type offT uint64 + +// MSIBinary represents an MSI binary. +// |headerBytes| and |contents| are non-overlapping slices of the same backing array. +type MSIBinary struct { + headerBytes []byte // the header (512 bytes). + header *MSIHeader // the parsed msi header. + sector SectorFormat // sector parameters. + contents []byte // the file content (no header), with SignedData removed. + sigDirOffset offT // the offset of the signedData stream directory in |contents|. + sigDirEntry *MSIDirEntry // the parsed contents of the signedData stream directory. + signedDataBytes []byte // the PKCS#7, SignedData in asn1 DER form. + signedData *signedData // the parsed SignedData structure. + fatEntries []secT // a copy of the FAT entries in one list. + difatEntries []secT // a copy of the DIFAT entries in one list. + difatSectors []secT // a list of the dedicated DIFAT sectors (if any), for convenience. +} + +// MSIHeader represents a parsed MSI header. +type MSIHeader struct { + Magic [8]byte + Clsid [16]byte + MinorVersion uint16 + DllVersion uint16 + ByteOrder uint16 + SectorShift uint16 + MiniSectorShift uint16 + Reserved [6]byte + NumDirSectors uint32 + NumFatSectors uint32 + FirstDirSector uint32 + TransactionSignatureNumber uint32 + MiniStreamCutoffSize uint32 + FirstMiniFatSector uint32 + NumMiniFatSectors uint32 + FirstDifatSector uint32 + NumDifatSectors uint32 +} + +// MSIDirEntry represents a parsed MSI directory entry for a stream. +type MSIDirEntry struct { + Name [64]byte + NumNameBytes uint16 + ObjectType uint8 + ColorFlag uint8 + Left uint32 + Right uint32 + Child uint32 + Clsid [16]byte + StateFlags uint32 + CreateTime uint64 + ModifyTime uint64 + StreamFirstSector uint32 + StreamSize uint64 +} + +// SectorFormat represents parameters of an MSI file sector. +type SectorFormat struct { + Size offT // the size of a sector in bytes; 512 for dll v3 and 4096 for v4. + Ints int // the number of int32s in a sector. +} + +const ( + numHeaderContentBytes = 76 + numHeaderTotalBytes = 512 + numDifatHeaderEntries = 109 + numDirEntryBytes = 128 + miniStreamSectorSize = 64 + miniStreamCutoffSize = 4096 + // Constants and names from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/ + fatFreesect = 0xFFFFFFFF // An unallocated sector (used in the FAT or DIFAT). + fatEndofchain = 0xFFFFFFFE // End of a linked chain (in the FAT); or end of DIFAT sector chain. + fatFatsect = 0xFFFFFFFD // A FAT sector (used in the FAT). + fatDifsect = 0xFFFFFFFC // A DIFAT sector (used in the FAT). + fatReserved = 0xFFFFFFFB // Reserved value. +) + +func newSectorFormat(sectorShift uint16) (format SectorFormat, err error) { + sectorSize := offT(1) << sectorShift + if sectorSize != 4096 && sectorSize != 512 { + return format, fmt.Errorf("unexpected msi sector shift, wanted sector size 4096 or 512, got %d", sectorSize) + } + return SectorFormat{ + Size: sectorSize, + Ints: int(sectorSize / 4), + }, nil +} + +// isLastInSector returns whether the index into difatEntries corresponds to the last entry in +// a sector. +// +// The last entry in each difat sector is a pointer to the next difat sector. +// (Or is an end-of-chain marker.) +// This does not apply to the last entry stored in the MSI header. +func (format SectorFormat) isLastInSector(index int) bool { + return index > numDifatHeaderEntries && (index-numDifatHeaderEntries+1)%format.Ints == 0 +} + +// readStream reads the stream starting at the given start sector. The name is optional, +// it is only used for error reporting. +func (bin *MSIBinary) readStream(name string, start secT, streamSize offT, forceFAT, freeData bool) (stream []byte, err error) { + var sectorSize offT + var fatEntries []secT // May be FAT or mini FAT. + var contents []byte // May be file contents or mini stream. + if forceFAT || streamSize >= miniStreamCutoffSize { + fatEntries = bin.fatEntries + contents = bin.contents + sectorSize = bin.sector.Size + } else { + // Load the mini FAT. + s, err := bin.readStream("mini FAT", secT(bin.header.FirstMiniFatSector), offT(bin.header.NumMiniFatSectors)*bin.sector.Size, true, false) + if err != nil { + return nil, err + } + for offset := 0; offset < len(s); offset += 4 { + fatEntries = append(fatEntries, secT(binary.LittleEndian.Uint32(s[offset:]))) + } + // Load the mini stream. (root directory's stream, root must be dir entry zero) + root := &MSIDirEntry{} + offset := offT(bin.header.FirstDirSector) * bin.sector.Size + binary.Read(bytes.NewBuffer(bin.contents[offset:]), binary.LittleEndian, root) + contents, err = bin.readStream("mini stream", secT(root.StreamFirstSector), offT(root.StreamSize), true, false) + if err != nil { + return nil, err + } + sectorSize = miniStreamSectorSize + } + sector := start + size := streamSize + for size > 0 { + if sector == fatEndofchain || sector == fatFreesect { + return nil, fmt.Errorf("msi readStream: ran out of sectors in copying stream %q", name) + } + n := size + if n > sectorSize { + n = sectorSize + } + offset := sectorSize * offT(sector) + stream = append(stream, contents[offset:offset+n]...) + size -= n + + // Zero out the existing stream bytes, if requested. + // For example, new signedData will be written at the end of + // the file (which may be where the existing stream is, but this works regardless). + // The stream bytes could be left as unused junk, but unused bytes in an MSI file are + // typically zeroed. + + // Set the data in the sector to zero. + if freeData { + for i := offT(0); i < n; i++ { + contents[offset+i] = 0 + } + } + // Find the next sector, then free the FAT entry of the current sector. + old := sector + sector = fatEntries[sector] + if freeData { + fatEntries[old] = fatFreesect + } + } + return stream, nil +} + +// Parse-time functionality is broken out into populate*() methods for clarity. + +// populateFatEntries does what it says and should only be called from NewMSIBinary(). +func (bin *MSIBinary) populateFatEntries() error { + var fatEntries []secT + for i, sector := range bin.difatEntries { + // The last entry in a difat sector is a chaining entry. + isLastInSector := bin.sector.isLastInSector(i) + if sector == fatFreesect || sector == fatEndofchain || isLastInSector { + continue + } + offset := offT(sector) * bin.sector.Size + for i := 0; i < bin.sector.Ints; i++ { + fatEntries = append(fatEntries, secT(binary.LittleEndian.Uint32(bin.contents[offset+offT(i)*4:]))) + } + } + bin.fatEntries = fatEntries + return nil +} + +// populateDifatEntries does what it says and should only be called from NewMSIBinary(). +func (bin *MSIBinary) populateDifatEntries() error { + // Copy the difat entries and make a list of difat sectors (if any). + // The first 109 difat entries must exist and are read from the MSI header, the rest come from + // optional additional sectors. + difatEntries := make([]secT, numDifatHeaderEntries, numDifatHeaderEntries+int(bin.header.NumDifatSectors)*bin.sector.Ints) + for i := 0; i < numDifatHeaderEntries; i++ { + difatEntries[i] = secT(binary.LittleEndian.Uint32(bin.headerBytes[numHeaderContentBytes+i*4:])) + } + + // Code (here and elsewhere) that manages additional difat sectors probably won't run in prod, + // but is implemented to avoid a hidden scaling limit. + // (109 difat sector entries) x (1024 fat sector entries/difat sector) x (4096 bytes/ fat sector) + // => files up to ~457 MB in size don't require additional difat sectors. + var difatSectors []secT + for i := 0; i < int(bin.header.NumDifatSectors); i++ { + var sector secT + if i == 0 { + sector = secT(bin.header.FirstDifatSector) + } else { + sector = difatEntries[len(difatEntries)-1] + } + difatSectors = append(difatSectors, sector) + start := offT(sector) * bin.sector.Size + for j := 0; j < bin.sector.Ints; j++ { + difatEntries = append(difatEntries, secT(binary.LittleEndian.Uint32(bin.contents[start+offT(j)*4:]))) + } + } + bin.difatEntries = difatEntries + bin.difatSectors = difatSectors + return nil +} + +var ( + // UTF-16 for "\05DigitalSignature" + signatureName = []byte{0x05, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67, 0x00, 0x69, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x53, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00} +) + +// signedDataDirFromSector returns the directory entry for the signedData stream, +// if it exists in the given sector. +func (bin *MSIBinary) signedDataDirFromSector(dirSector secT) (sigDirEntry *MSIDirEntry, offset offT, found bool) { + sigDirEntry = &MSIDirEntry{} + // Fixed 128 byte directory entry size. + for i := offT(0); i < bin.sector.Size/numDirEntryBytes; i++ { + offset = offT(dirSector)*bin.sector.Size + i*numDirEntryBytes + binary.Read(bytes.NewBuffer(bin.contents[offset:]), binary.LittleEndian, sigDirEntry) + if bytes.Equal(sigDirEntry.Name[:sigDirEntry.NumNameBytes], signatureName) { + return sigDirEntry, offset, true + } + } + return +} + +// populateSignatureDirEntry does what it says and should only be called from NewMSIBinary(). +func (bin *MSIBinary) populateSignatureDirEntry() error { + dirSector := secT(bin.header.FirstDirSector) + for { + if sigDirEntry, sigDirOffset, found := bin.signedDataDirFromSector(dirSector); found { + bin.sigDirEntry = sigDirEntry + bin.sigDirOffset = sigDirOffset + return nil + } + // Did not find the entry, go to the next directory sector. + // This is run on MSIs that Google creates, so don't worry about a malicious infinite loop + // in the entries. + dirSector = bin.fatEntries[dirSector] + if dirSector == fatEndofchain { + return errors.New("did not find signature stream in MSI file") + } + } +} + +// populateSignedData does what it says and should only be called from NewMSIBinary(). +func (bin *MSIBinary) populateSignedData() (err error) { + sector := secT(bin.sigDirEntry.StreamFirstSector) + size := offT(bin.sigDirEntry.StreamSize) + if bin.header.DllVersion == 3 { + size = size & 0x7FFFFFFF + } + stream, err := bin.readStream("signedData", sector, size, false, true) if err != nil { - return nil, err + return err + } + bin.signedDataBytes = stream + bin.signedData, err = parseSignedData(bin.signedDataBytes) + if err != nil { + return err } + return nil +} - issuerTemplate := x509.Certificate{ - SerialNumber: new(big.Int).SetInt64(1), - Subject: pkix.Name{ - CommonName: "Unknown issuer", - }, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - SignatureAlgorithm: x509.SHA1WithRSA, - BasicConstraintsValid: true, - IsCA: true, +var ( + msiHeaderSignature = []byte{0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1} + msiHeaderClsid = make([]byte, 16) +) + +// NewMSIBinary returns a Binary that contains details of the MSI binary given in |contents|. +// |contents| is modified; the region occupied by the cert section is zeroed out. +func NewMSIBinary(fileContents []byte) (*MSIBinary, error) { + // Parses the MSI header, the directory entry for the SignedData, and the SignedData itself. + // Makes copies of the list of FAT and DIFAT entries, for easier manipulation. + // Zeroes out the SignedData stream in |contents|, as it may move. + // When writing, the elements: (header, dir entry, SignedData, FAT and DIFAT entries) + // are considered dirty (modified), and written back into fileContents. + if len(fileContents) < numHeaderTotalBytes { + return nil, fmt.Errorf("msi file is too short to contain header, want >= %d bytes got %d bytes", numHeaderTotalBytes, len(fileContents)) } - template := x509.Certificate{ - SerialNumber: new(big.Int).SetInt64(1), - Subject: pkix.Name{ - CommonName: "Dummy certificate", - }, - Issuer: pkix.Name{ - CommonName: "Unknown issuer", - }, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - SignatureAlgorithm: x509.SHA1WithRSA, - BasicConstraintsValid: true, - IsCA: false, - ExtraExtensions: []pkix.Extension{ - { - // This includes the tag in an extension in the - // certificate. - Id: oidChromeTag, - Value: tag, - }, - }, + // Parse the header. + headerBytes := fileContents[:numHeaderTotalBytes] + var header MSIHeader + binary.Read(bytes.NewBuffer(headerBytes[:numHeaderContentBytes]), binary.LittleEndian, &header) + if !bytes.Equal(header.Magic[:], msiHeaderSignature) || !bytes.Equal(header.Clsid[:], msiHeaderClsid) { + return nil, fmt.Errorf("msi file is not an msi file: either the header signature is missing or the clsid is not zero as required") } - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &issuerTemplate, &priv.PublicKey, priv) + format, err := newSectorFormat(header.SectorShift) if err != nil { return nil, err } + if offT(len(fileContents)) < format.Size { + return nil, fmt.Errorf("msi file is too short to contain a full header sector, want >= %d bytes got %d bytes", format.Size, len(fileContents)) + } + contents := fileContents[format.Size:] - bin.signedData.PKCS7.Certs = append(bin.signedData.PKCS7.Certs, asn1.RawValue{ - FullBytes: derBytes, - }) + bin := &MSIBinary{ + headerBytes: headerBytes, + header: &header, + sector: format, + contents: contents, + } + + // The difat entries must be populated before the fat entries. + if err := bin.populateDifatEntries(); err != nil { + return nil, err + } + if err := bin.populateFatEntries(); err != nil { + return nil, err + } + // The signature dir entry must be populated before the signed data. + if err := bin.populateSignatureDirEntry(); err != nil { + return nil, err + } + if err := bin.populateSignedData(); err != nil { + return nil, err + } + return bin, nil +} + +// firstFreeFatEntry returns the index of the first free entry at the end of a slice of fat entries. +// It returns one past the end of list if there are no free entries at the end. +func firstFreeFatEntry(entries []secT) secT { + firstFreeIndex := secT(len(entries)) + for entries[firstFreeIndex-1] == fatFreesect { + firstFreeIndex-- + } + return firstFreeIndex +} + +func (bin *MSIBinary) firstFreeFatEntry() secT { + return firstFreeFatEntry(bin.fatEntries) +} + +// ensureFreeFatEntries ensures there are at least n free entries at the end of the FAT list, +// and returns the first free entry. +// +// The bin.fatEntry slice may be modified, any local references to the slice are invalidated. +// bin.fatEntry elements may be assigned, so any local references to entries (such as the +// first free index) are also invalidated. +// The function is re-entrant. +func (bin *MSIBinary) ensureFreeFatEntries(n secT) secT { + sizeFat := secT(len(bin.fatEntries)) + firstFreeIndex := bin.firstFreeFatEntry() // Is past end of slice if there are no free entries. + if sizeFat-firstFreeIndex >= n { + // Nothing to do, there were already enough free sectors. + return firstFreeIndex + } + // Append another FAT sector. + for i := 0; i < bin.sector.Ints; i++ { + bin.fatEntries = append(bin.fatEntries, fatFreesect) + } + // firstFreeIndex is free; assign it to the created FAT sector. + // (Do not change the order of these calls; assignDifatEntry() could invalidate firstFreeIndex.) + bin.fatEntries[firstFreeIndex] = fatFatsect + bin.assignDifatEntry(firstFreeIndex) + + // Update the MSI header. + bin.header.NumFatSectors++ + + // If n is large enough, it's possible adding an additional sector was insufficient. + // This won't happen for our use case; but the call to verify or fix it is cheap. + bin.ensureFreeFatEntries(n) + + return bin.firstFreeFatEntry() +} + +// assignDifatEntries assigns an entry (the sector# of a FAT sector) to the end of the difat list. +// +// The bin.fatEntry slice may be modified, any local references to the slice are invalidated. +// bin.fatEntry elements may be assigned, so any local references to entries (such as the +// first free index) are also invalidated. +func (bin *MSIBinary) assignDifatEntry(fatSector secT) { + bin.ensureFreeDifatEntry() + // Find first free entry at end of list. + i := len(bin.difatEntries) - 1 + + // If there are sectors, i could be pointing to a fatEndofchain marker, but in that case + // it is guaranteed (by ensureFreeDifatEntry()) that the prior element is a free sector, + // and the following loop works. + + // As long as the prior element is a free sector, decrement i. + // If the prior element is at the end of a difat sector, skip over it. + for bin.difatEntries[i-1] == fatFreesect || + (bin.sector.isLastInSector(i-1) && bin.difatEntries[i-2] == fatFreesect) { + i-- + } + bin.difatEntries[i] = fatSector +} + +// ensureFreeDifatEntry ensures there is at least one free entry at the end of the DIFAT list. +// +// The bin.fatEntry slice may be modified, any local references to the slice are invalidated. +// bin.fatEntry elements may be assigned, so any local references to entries (such as the +// first free index) are also invalidated. +func (bin *MSIBinary) ensureFreeDifatEntry() { + // By construction, difatEntries is at least numDifatHeaderEntries (109) long. + i := len(bin.difatEntries) - 1 + if bin.difatEntries[i] == fatEndofchain { + i-- + } + if bin.difatEntries[i] == fatFreesect { + return // There is at least one free entry. + } + + oldDifatTail := len(bin.difatEntries) - 1 + + // Allocate another sector of difat entries. + for i := 0; i < bin.sector.Ints; i++ { + bin.difatEntries = append(bin.difatEntries, fatFreesect) + } + bin.difatEntries[len(bin.difatEntries)-1] = fatEndofchain + + // Assign the new difat sector in the FAT. + sector := bin.ensureFreeFatEntries(1) + bin.fatEntries[sector] = fatDifsect + + // Assign the "next sector" pointer in the previous sector or header. + if bin.header.NumDifatSectors == 0 { + bin.header.FirstDifatSector = uint32(sector) + } else { + bin.difatEntries[oldDifatTail] = sector + } + bin.header.NumDifatSectors++ + bin.difatSectors = append(bin.difatSectors, sector) // A helper slice. +} - asn1Bytes, err := asn1.Marshal(*bin.signedData) +// AppendedTag is not supported for MSI files. +func (bin *MSIBinary) AppendedTag() (data []byte, ok bool) { + return nil, false +} + +func (bin *MSIBinary) asn1Data() []byte { + return bin.signedDataBytes +} + +// buildBinary builds an MSI binary based on bin but with the given SignedData and appended tag. +// Appended tag is not supported for MSI. +// buildBinary may add free sectors to |bin|, but otherwise does not modify it. +func (bin *MSIBinary) buildBinary(signedData, tag []byte) ([]byte, error) { + if len(tag) > 0 { + return nil, errors.New("appended tags not supported in MSI files") + } + // Writing to the mini FAT is not supported. + if len(signedData) < miniStreamCutoffSize { + return nil, fmt.Errorf("writing SignedData less than %d bytes is not supported", len(signedData)) + } + // Ensure enough free FAT entries for the signedData. + numSignedDataSectors := secT((offT(len(signedData))-1)/bin.sector.Size) + 1 + firstSignedDataSector := bin.ensureFreeFatEntries(numSignedDataSectors) + + // Allocate sectors for the signedData, in a copy of the FAT entries. + newFatEntries := make([]secT, len(bin.fatEntries)) + copy(newFatEntries, bin.fatEntries) + for i := secT(0); i < numSignedDataSectors-1; i++ { + newFatEntries[firstSignedDataSector+i] = firstSignedDataSector + i + 1 + } + newFatEntries[firstSignedDataSector+numSignedDataSectors-1] = fatEndofchain + + // Update the signedData stream's directory entry (location and size), in copy of dir entry. + newSigDirEntry := *bin.sigDirEntry + newSigDirEntry.StreamFirstSector = uint32(firstSignedDataSector) + newSigDirEntry.StreamSize = uint64(len(signedData)) + + // Write out the... + // ...header, + headerSectorBytes := make([]byte, bin.sector.Size) + out := new(bytes.Buffer) + binary.Write(out, binary.LittleEndian, bin.header) + copy(headerSectorBytes[:], out.Bytes()) + for i := 0; i < numDifatHeaderEntries; i++ { + binary.LittleEndian.PutUint32(headerSectorBytes[numHeaderContentBytes+i*4:], uint32(bin.difatEntries[i])) + } + // ...content, + // Make a copy of the content bytes, since new data will be overlaid on it. + // The new content slice should accommodate the new content size. + firstFreeSector := firstFreeFatEntry(newFatEntries) + contents := make([]byte, bin.sector.Size*offT(firstFreeSector)) // zero-based sector counting. + copy(contents, bin.contents) + + // ...signedData directory entry (from local modified copy), + out.Reset() + binary.Write(out, binary.LittleEndian, &newSigDirEntry) + copy(contents[bin.sigDirOffset:], out.Bytes()) + + // ...difat entries, + // They might have been modified, although usually not. + for i, sector := range bin.difatSectors { + index := numDifatHeaderEntries + i*bin.sector.Ints + offset := offT(sector) * bin.sector.Size + for j := 0; j < bin.sector.Ints; j++ { + binary.LittleEndian.PutUint32(contents[offset+offT(j)*4:], uint32(bin.difatEntries[index+j])) + } + } + // ...fat entries (from local modified copy), + index := 0 + for i, sector := range bin.difatEntries { + // The last entry in each difat sector is a pointer to the next difat sector. + // This does not apply to the header entries. + isLastInSector := bin.sector.isLastInSector(i) + if sector != fatFreesect && sector != fatEndofchain && !isLastInSector { + offset := offT(sector) * bin.sector.Size + for i := 0; i < bin.sector.Ints; i++ { + binary.LittleEndian.PutUint32(contents[offset+offT(i)*4:], uint32(newFatEntries[index+i])) + } + index += bin.sector.Ints + } + } + // ...signedData + // |contents| was zero-initialized, so no need to add padding to end of sector. + // The sectors allocated for signedData were guaranteed contiguous. + copy(contents[offT(firstSignedDataSector)*bin.sector.Size:], signedData) + + return append(headerSectorBytes, contents...), nil +} + +// RemoveAppendedTag is not supported for MSI files. +func (bin *MSIBinary) RemoveAppendedTag() (contents []byte, err error) { + return nil, errors.New("authenticodetag: appended tags not supported in MSI files") +} + +// SetAppendedTag is not supported for MSI files. +func (bin *MSIBinary) SetAppendedTag(tagContents []byte) (contents []byte, err error) { + return nil, errors.New("authenticodetag: appended tags not supported in MSI files") +} + +func (bin *MSIBinary) getSuperfluousCert() (cert *x509.Certificate, index int, err error) { + return getSuperfluousCert(bin.signedData) +} + +// SetSuperfluousCertTag returns an MSI binary based on bin, but where the +// superfluous certificate contains the given tag data. +// The (parsed) bin.signedData is modified; but bin.signedDataBytes, which contains +// the raw original bytes, is not. +func (bin *MSIBinary) SetSuperfluousCertTag(tag []byte) (contents []byte, err error) { + asn1Bytes, err := SetSuperfluousCertTag(bin.signedData, tag) if err != nil { return nil, err } - return bin.buildBinary(asn1Bytes, bin.appendedTag), nil + return bin.buildBinary(asn1Bytes, nil) +} + +func (bin *MSIBinary) certificateOffset() int64 { + // The signedData will be written at the first free sector at the end of file. + return int64(offT(bin.firstFreeFatEntry()) * bin.sector.Size) +} + +// findTag returns the offset of the superfluous-cert tag in |contents|, or (-1, 0) if not found. +// The caller should restrict the search to the certificate section of the contents, if known. +func findTag(contents []byte, start int64) (offset, length int64, err error) { + // An MSI can have a tagged Omaha inside of it, but that is the wrong tag -- it should be the + // one on the outermost container, or none. + contents = contents[start:] + lenContents := int64(len(contents)) + + // Find the oidChromeTag in the contents. The search string includes everything up to the + // asn1 length specification right before the Omaha2.0 marker. + offset = int64(bytes.LastIndex(contents, oidChromeTagSearchBytes)) + if offset < 0 { // Not an error, simply not found. + return -1, 0, nil + } + offset += int64(len(oidChromeTagSearchBytes)) + if offset > lenContents-2 { + return -1, 0, fmt.Errorf("failed in findTag, want offset plus tag size bytes to fit in file size %d, but offset %d is too large", lenContents, offset) + } + length = int64(binary.BigEndian.Uint16(contents[offset:])) + offset += 2 + if offset+length > lenContents { + return -1, 0, fmt.Errorf("failed in findTag, want tag buffer to fit in file size %d, but offset (%d) plus length (%d) is %d", lenContents, offset, length, offset+length) + } + return start + offset, length, nil } var ( @@ -544,6 +1222,7 @@ var ( paddedLength *int = flag.Int("padded-length", 0, "A superfluous cert tag will be padded with zeros to at least this number of bytes") savePKCS7 *string = flag.String("save-pkcs7", "", "If set to a filename, the PKCS7 data from the original binary will be written to that file.") outFilename *string = flag.String("out", "", "If set, the updated binary is written to this file. Otherwise the binary is updated in place.") + printTagDetails *bool = flag.Bool("print-tag-details", false, "IF set, print to stdout the location and size of the superfluous cert's Gact2.0 marker plus buffer.") ) func main() { @@ -569,10 +1248,11 @@ func main() { os.Exit(1) } + var finalContents []byte didSomething := false if len(*savePKCS7) > 0 { - if err := ioutil.WriteFile(*savePKCS7, bin.asn1Data, 0644); err != nil { + if err := ioutil.WriteFile(*savePKCS7, bin.asn1Data(), 0644); err != nil { fmt.Fprintf(os.Stderr, "Error while writing file: %s\n", err) os.Exit(1) } @@ -599,6 +1279,7 @@ func main() { fmt.Fprintf(os.Stderr, "Error while writing updated file: %s\n", err) os.Exit(1) } + finalContents = contents didSomething = true } @@ -609,10 +1290,15 @@ func main() { os.Exit(1) } contents, err := bin.SetAppendedTag(tagContents) + if err != nil { + fmt.Fprintf(os.Stderr, "Error while setting appended tag: %s\n", err) + os.Exit(1) + } if err := ioutil.WriteFile(*outFilename, contents, 0644); err != nil { fmt.Fprintf(os.Stderr, "Error while writing updated file: %s\n", err) os.Exit(1) } + finalContents = contents didSomething = true } @@ -620,7 +1306,7 @@ func main() { var tagContents []byte if strings.HasPrefix(*setSuperfluousCertTag, "0x") { - tagContents, err = hex.DecodeString(*setSuperfluousCertTag) + tagContents, err = hex.DecodeString((*setSuperfluousCertTag)[2:]) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse tag contents from command line: %s\n", err) os.Exit(1) @@ -632,6 +1318,15 @@ func main() { for len(tagContents) < *paddedLength { tagContents = append(tagContents, 0) } + // print-tag-details only works if the length requires 2 bytes to specify. (The length bytes + // length is part of the search string.) + // Lorry only tags properly (aside from tag-in-zip) if the length is 8206 or more. b/173139534 + // Omaha may or may not have a practical buffer size limit; 8206 is known to work. + if len(tagContents) < 0x100 || len(tagContents) > 0xffff { + fmt.Fprintf(os.Stderr, "Want final tag length in range [256, 65535], got %d\n", len(tagContents)) + os.Exit(1) + } + contents, err := bin.SetSuperfluousCertTag(tagContents) if err != nil { fmt.Fprintf(os.Stderr, "Error while setting superfluous certificate tag: %s\n", err) @@ -641,6 +1336,24 @@ func main() { fmt.Fprintf(os.Stderr, "Error while writing updated file: %s\n", err) os.Exit(1) } + finalContents = contents + didSomething = true + } + + if *printTagDetails { + if finalContents == nil { + // Re-read the input, as NewBinary() may modify it. + finalContents, err = ioutil.ReadFile(inFilename) + if err != nil { + panic(err) + } + } + offset, length, err := findTag(finalContents, bin.certificateOffset()) + if err != nil { + fmt.Fprintf(os.Stderr, "Error while searching for tag in file bytes: %s\n", err) + os.Exit(1) + } + fmt.Printf("Omaha Tag offset, length: (%d, %d)\n", offset, length) didSomething = true } diff --git a/common/certificate_tag/certificate_tag_test.go b/common/certificate_tag/certificate_tag_test.go index 5c19d66bf..84b22ef0f 100644 --- a/common/certificate_tag/certificate_tag_test.go +++ b/common/certificate_tag/certificate_tag_test.go @@ -17,47 +17,73 @@ package main import ( "bytes" + "encoding/binary" + "flag" + "fmt" "io/ioutil" "os/exec" "path/filepath" "strings" "testing" - - "google3/testing/gobase/googletest" ) -const directory = "google3/googleclient/installer/tools" +// Modified from the google3/googleclient/tools version so it can run outside of google3. +// Build certificate_tag separately, and point flag tag-binary-dir to the build location. +// +// Here is an example of testing the 32-bit version on Linux: +// +// $ GOARCH=386 CC=gcc go build -o /tmp/certificate_tag common/certificate_tag/certificate_tag.go +// $ GOARCH=386 CC=gcc go test common/certificate_tag/certificate_tag_test.go common/certificate_tag/certificate_tag.go +// +// Here is an example of testing the 32-bit version on Windows 10. +// +// $ go build -o C:/tmp/certificate_tag common/certificate_tag/certificate_tag.go +// $ go test common/certificate_tag/certificate_tag_test.go common/certificate_tag/certificate_tag.go -tag-binary-dir "C:/tmp" var ( + tagBinaryDir *string = flag.String("tag-binary-dir", "/tmp", "Path to directory with the tag binary.") // tagBinary contains the path to the certificate_tag program. tagBinary string // sourceExe contains the path to a Chrome installer exe file. sourceExe string + // sourceMSI* contains the path to a signed MSI file. + sourceMSI1, sourceMSI2, sourceMSI3, sourceMSI4 string ) -func init() { - tagBinary = filepath.Join(googletest.TestSrcDir, directory, "certificate_tag") - sourceExe = filepath.Join(googletest.TestSrcDir, directory, "testdata/ChromeSetup.exe") +// existingTagSubstring is a segment of the superfluous-cert tag that's already +// in ChromeSetup.exe. +const existingTagSubstring = ".....Gact.?omah" + +func TestMain(m *testing.M) { + flag.Parse() + + tagBinary = filepath.Join(*tagBinaryDir, "certificate_tag") + sourceExe = filepath.Join("testdata/ChromeSetup.exe") + sourceMSI1 = filepath.Join("testdata/googlechromestandaloneenterprise.msi") + sourceMSI2 = filepath.Join("testdata/test7zSigned.msi") + sourceMSI3 = filepath.Join("testdata/OmahaTestSigned.msi") + sourceMSI4 = filepath.Join("testdata/test7zSigned-smallcert.msi") + + m.Run() } func TestPrintAppendedTag(t *testing.T) { cmd := exec.Command(tagBinary, "--dump-appended-tag", sourceExe) output, err := cmd.CombinedOutput() if err != nil { - t.Fatal(err) + t.Fatalf("Error executing %q: %v; output:\n%s", tagBinary, err, output) } - const expected = ".....Gact.?omah" - if out := string(output); !strings.Contains(out, expected) { - t.Errorf("Output of --dump-appended-tag didn't contain %s, as expected. Got:\n%s", expected, out) + if out := string(output); !strings.Contains(out, existingTagSubstring) { + t.Errorf("Output of --dump-appended-tag didn't contain %s, as expected. Got:\n%s", existingTagSubstring, out) } } // tempFileName returns a path that can be used as temp file. This is only safe // because we know that only our process can write in the test's temp // directory. -func tempFileName() string { - f, err := ioutil.TempFile(googletest.TestTmpDir, "certificate_tag_test") +func tempFileName(t *testing.T) string { + f, err := ioutil.TempFile(t.TempDir(), "certificate_tag_test") if err != nil { panic(err) } @@ -66,34 +92,638 @@ func tempFileName() string { return path } -func TestSetSuperfluousCertTag(t *testing.T) { - out := tempFileName() +func SetSuperfluousCertTagHelper(t *testing.T, source string) { + out := tempFileName(t) - const expected = "34cf251b916a54dc9351b832bb0ac7ce" - cmd := exec.Command(tagBinary, "--out", out, "--set-superfluous-cert-tag", expected, sourceExe) - if err := cmd.Run(); err != nil { - t.Fatal(err) + expected := "34cf251b916a54dc9351b832bb0ac7ce" + strings.Repeat(" ", 256) + cmd := exec.Command(tagBinary, "--out", out, "--set-superfluous-cert-tag", expected, source) + if output, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("Test input %s, error executing %q: %v; output:\n%s", source, tagBinary, err, output) } contents, err := ioutil.ReadFile(out) if err != nil { - t.Fatalf("Failed to read output file: %s", err) + t.Fatalf("Test input %s, failed to read output file: %s", source, err) } if !bytes.Contains(contents, []byte(expected)) { - t.Error("Output doesn't contain expected bytes") + t.Errorf("Test input %s, output doesn't contain expected bytes", source) + } + if bytes.Contains(contents, []byte(existingTagSubstring)) { + t.Errorf("Test input %s, output still contains old tag that should have been replaced", source) } - cmd = exec.Command(tagBinary, "--out", out, "--set-superfluous-cert-tag", expected, "--padded-length", "256", sourceExe) - if err = cmd.Run(); err != nil { - t.Fatal(err) + cmd = exec.Command(tagBinary, "--out", out, "--set-superfluous-cert-tag", expected, "--padded-length", "512", source) + if output, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("Test input %s, error executing %q: %v; output:\n%s", source, tagBinary, err, output) } contents, err = ioutil.ReadFile(out) if err != nil { - t.Fatalf("Failed to read output file: %s", err) + t.Fatalf("Test input %s, failed to read output file: %s", source, err) } var zeros [16]byte if !bytes.Contains(contents, append([]byte(expected), zeros[:]...)) { - t.Error("Output doesn't contain expected bytes with padding") + t.Errorf("Test input %s, output doesn't contain expected bytes with padding", source) + } +} + +func TestSetSuperfluousCertTag(t *testing.T) { + expect := []struct { + infile string + }{ + {sourceExe}, + {sourceMSI1}, + {sourceMSI2}, + {sourceMSI3}, + } + for _, e := range expect { + SetSuperfluousCertTagHelper(t, e.infile) + } +} + +func TestIsLastInSector(t *testing.T) { + expect := []struct { + in int + shift uint16 // 12 for 4096-byte sectors, 9 for 512-byte sectors. + want bool + }{ + {0, 12, false}, + {1, 12, false}, + {107, 12, false}, + {108, 12, false}, + {109, 12, false}, + {1131, 12, false}, + {1132, 12, true}, + {1133, 12, false}, + {2156, 12, true}, + {0, 9, false}, + {1, 9, false}, + {107, 9, false}, + {108, 9, false}, + {109, 9, false}, + {236, 9, true}, + {364, 9, true}, + } + for _, e := range expect { + format, _ := newSectorFormat(e.shift) + got := format.isLastInSector(e.in) + if got != e.want { + t.Errorf("Arguments (%d, %d): got %t, want %t", e.in, e.shift, got, e.want) + } + } +} + +func TestFirstFreeFatEntry(t *testing.T) { + expect := []struct { + in int + want secT + }{ + {1023, 1024}, + {1000, 1001}, + {10, 11}, + {0, 1}, + } + for _, e := range expect { + entries := make([]secT, 0, 1024) + for i := 0; i < 1024; i++ { + entries = append(entries, fatFreesect) + } + entries[e.in] = 1 + got := firstFreeFatEntry(entries) + if got != e.want { + t.Errorf("Argument %d, got %d, want %d", e.in, got, e.want) + } + } +} + +func getFat(sectors, free int) []secT { + // Zero is a valid sector. In a valid file, though, the sector number wouldn't repeat like this. + used := 1024*sectors - free // 1024 int32 entries per sector. + entries := make([]secT, used, used+free) + + // Set a non-contiguous free sector before the end, which shouldn't affect anything. + if used > 2 { + entries[used-2] = fatFreesect + } + for i := 0; i < free; i++ { + entries = append(entries, fatFreesect) + } + return entries +} + +func getDifat(sectors, free int) []secT { + // Similar to getFat, but there are always 109 (non-sector) elements from the header; + // and the last element of any sectors should be fatEndofchain or a sector #. + entries := make([]secT, 109, 109+sectors*1024) + sentinel := secT(123) // Some sector number + for ; sectors > 0; sectors-- { + new := make([]secT, 1024) + if sectors == 1 { + new[1023] = fatEndofchain + } else { + new[1023] = sentinel + } + entries = append(entries, new...) + } + i := len(entries) - 1 + for free > 0 { + if entries[i] != fatEndofchain && entries[i] != sentinel { + entries[i] = fatFreesect + free-- + } + i-- + } + return entries +} + +func getBin(fatEntries, difatEntries []secT) *MSIBinary { + // Uses dll version 4, 4096-byte sectors. + // There are difat sectors only if len(difatEntries) > 109. + n := 0 + if len(difatEntries) > numDifatHeaderEntries { + n = (len(difatEntries)-numDifatHeaderEntries-1)/1024 + 1 + } + // If n>0, zero is a fine sector number. + difatSectors := make([]secT, n) + + header := &MSIHeader{ + DllVersion: 4, + SectorShift: 12, + NumDifatSectors: uint32(n), + } + + // Make copies so we can compare before and after method calls. + fat := make([]secT, len(fatEntries)) + copy(fat, fatEntries) + difat := make([]secT, len(difatEntries)) + copy(difat, difatEntries) + + format, _ := newSectorFormat(12) + return &MSIBinary{ + headerBytes: nil, + header: header, + sector: format, + contents: nil, + sigDirOffset: 0, + sigDirEntry: nil, + signedDataBytes: nil, + signedData: nil, + fatEntries: fat, + difatEntries: difat, + difatSectors: difatSectors, + } +} + +func verifyAndRemoveDifatChaining(t *testing.T, entries []secT, which, name string, id int) []secT { + format, _ := newSectorFormat(12) + for i := len(entries) - 1; i >= 0; i-- { + if format.isLastInSector(i) { + if i == len(entries)-1 && entries[i] != fatEndofchain { + t.Errorf("%s end of chain %s was modified, case %d, i %d: wanted %d (fatEndofchain), got %d", which, name, id, i, secT(fatEndofchain), entries[i]) + } + if i != len(entries)-1 && entries[i] >= fatReserved { + t.Errorf("%s %s entries weren't chained, case %d: wanted (< %d) (fatReserved), got %d", which, name, id, secT(fatReserved), entries[i]) + } + if i == len(entries)-1 { + entries = entries[:i] + } else { + entries = append(entries[:i], entries[i+1:]...) + } + } + } + return entries +} + +func verifyEntries(t *testing.T, name string, id, added int, changed, old, new []secT, isDifat bool) { + if len(new)-len(old) != added { + t.Errorf("Wrong num added %s entries, case %d: wanted %d, got %d", name, id, added, len(new)-len(old)) + } + // If this is difat, check and remove the chaining entries. This simplifies the checks below. + if isDifat { + // If there is an error in "old", the test case wasn't set up correctly. + old = verifyAndRemoveDifatChaining(t, old, "old", name, id) + new = verifyAndRemoveDifatChaining(t, new, "new", name, id) + } + firstFree := len(old) // Can be past end of slice. + for firstFree > 0 && old[firstFree-1] == fatFreesect { + firstFree-- + } + same := new[:firstFree] + diff := new[firstFree : firstFree+len(changed)] + free := new[firstFree+len(changed):] + for i := 0; i < len(same); i++ { + if old[i] != same[i] { + t.Errorf("Entry in %s should not be changed, case %d, i %d: wanted %d, got %d", name, id, i, old[i], same[i]) + } + } + for i := 0; i < len(diff); i++ { + if changed[i] != diff[i] { + t.Errorf("Entry in %s is not changed or not changed to correct value, case %d, offset %d, i %d: wanted %d, got %d", name, id, firstFree, i, changed[i], diff[i]) + } + } + for i := 0; i < len(free); i++ { + if free[i] != fatFreesect { + t.Errorf("Entry in %s should be free but isn't, case %d, offset %d, i %d: wanted %d (fatFreesect), got %d", name, id, firstFree+len(changed), i, secT(fatFreesect), free[i]) + } + } +} + +func TestEnsureFreeDifatEntry(t *testing.T) { + expect := []struct { + id int // case id + difatSectors int // in: # difat sectors + difatFree int // in: # free difat entries + changedDifat []secT // expect: value of changed difat entries + addedDifat int // expect: # difat entries added + fatSectors int // in: # fat sectors + fatFree int // in: # free fat entries + changedFat []secT // expect: value of changed fat entries + addedFat int // expect: # fat entries added + }{ + // Note: The number of difat used entries should imply the # of fat sectors. + // But that inconsistency doesn't affect these tests. + + // Free difat entry in header, no change. + {0, 0, 108, []secT{}, 0, 1, 40, []secT{}, 0}, + // No free difat entry, add a difat sector (1024 entries). + {1, 0, 0, []secT{}, 1024, 1, 40, []secT{fatDifsect}, 0}, + // Free difat entry in sector, no change. + {2, 1, 1, []secT{}, 0, 1, 40, []secT{}, 0}, + // No free difat entry, add a difat sector. + {3, 1, 0, []secT{}, 1024, 1, 40, []secT{fatDifsect}, 0}, + // Additional sector is completely empty, no change. + {4, 1, 1023, []secT{}, 0, 1, 40, []secT{}, 0}, + // Free difat entry; No free fat entry. No change to either. + {5, 0, 10, []secT{}, 0, 1, 0, []secT{}, 0}, + // No free difat entry; add a difat sector. No free fat entry; add a fat sector. + {6, 0, 0, []secT{1024}, 1024, 1, 0, []secT{fatFatsect, fatDifsect}, 1024}, + {7, 1, 0, []secT{1024}, 1024, 1, 0, []secT{fatFatsect, fatDifsect}, 1024}, + } + + for _, e := range expect { + fat := getFat(e.fatSectors, e.fatFree) + difat := getDifat(e.difatSectors, e.difatFree) + bin := getBin(fat, difat) + bin.ensureFreeDifatEntry() + + // Check added entries. + verifyEntries(t, "difat", e.id, e.addedDifat, e.changedDifat, difat, bin.difatEntries, true) + verifyEntries(t, "fat", e.id, e.addedFat, e.changedFat, fat, bin.fatEntries, false) + } +} + +func TestEnsureFreeFatEntries(t *testing.T) { + expect := []struct { + id int // case id + difatSectors int // in: # difat sectors + difatFree int // in: # free difat entries + changedDifat []secT // expect: value of changed difat entries + addedDifat int // expect: # difat entries added + fatSectors int // in: # fat sectors + fatFree int // in: # free fat entries available + fatRequest secT // in: # free fat entries requested + changedFat []secT // expect: value of changed fat entries + addedFat int // expect: # fat entries added + }{ + // Note: The number of difat used entries should imply the # of fat sectors. + // But that inconsistency doesn't affect these tests. + + {0, 0, 1, []secT{}, 0, 1, 2, 2, []secT{}, 0}, + {1, 0, 0, []secT{}, 0, 1, 2, 2, []secT{}, 0}, + {2, 0, 1, []secT{1022}, 0, 1, 2, 4, []secT{fatFatsect}, 1024}, + {3, 0, 0, []secT{1022}, 1024, 1, 2, 4, []secT{fatFatsect, fatDifsect}, 1024}, + {4, 0, 1, []secT{1024}, 0, 1, 0, 4, []secT{fatFatsect}, 1024}, + {5, 0, 0, []secT{1024}, 1024, 1, 0, 4, []secT{fatFatsect, fatDifsect}, 1024}, + {6, 1, 1, []secT{1022}, 0, 1, 2, 4, []secT{fatFatsect}, 1024}, + {7, 1, 0, []secT{1022}, 1024, 1, 2, 4, []secT{fatFatsect, fatDifsect}, 1024}, + {8, 2, 1, []secT{2046}, 0, 2, 2, 4, []secT{fatFatsect}, 1024}, + {9, 2, 0, []secT{2046}, 1024, 2, 2, 4, []secT{fatFatsect, fatDifsect}, 1024}, + + // These are unlikely cases, but they should work. + // Request exactly one more sector free. (The difat sector will consume a fat entry as well.) + {10, 0, 1, []secT{1022}, 0, 1, 2, 1025, []secT{fatFatsect}, 1024}, + // Request more than one more sector. + {11, 0, 2, []secT{1022, 1023}, 0, 1, 2, 1026, []secT{fatFatsect, fatFatsect}, 2048}, + // Request more than one sector because of additional difat sector. + {12, 0, 0, []secT{1022, 1024}, 1024, 1, 2, 1025, []secT{fatFatsect, fatDifsect, fatFatsect}, 2048}, + } + + for _, e := range expect { + fat := getFat(e.fatSectors, e.fatFree) + difat := getDifat(e.difatSectors, e.difatFree) + bin := getBin(fat, difat) + bin.ensureFreeFatEntries(e.fatRequest) + + // Check added entries. + verifyEntries(t, "difat", e.id, e.addedDifat, e.changedDifat, difat, bin.difatEntries, true) + verifyEntries(t, "fat", e.id, e.addedFat, e.changedFat, fat, bin.fatEntries, false) + } +} + +func TestAssignDifatEntry(t *testing.T) { + expect := []struct { + id int // case id + difatSectors int // in: # difat sectors + difatFree int // in: # free difat entries + assignedIndex int // expect: which difat index assigned + assignedValue secT // in/expect: value assigned + fatSectors int // in: # fat sectors + fatFree int // in: # free fat entries + }{ + {1, 0, 1, 108, 1000, 1, 23}, + {2, 0, 0, 109, 1000, 1, 23}, + {3, 1, 1, 1131, 1000, 1, 23}, + {4, 1, 0, 1133, 1000, 1, 23}, + } + for _, e := range expect { + fat := getFat(e.fatSectors, e.fatFree) + difat := getDifat(e.difatSectors, e.difatFree) + bin := getBin(fat, difat) + bin.assignDifatEntry(e.assignedValue) + + if len(bin.difatEntries) < e.assignedIndex+1 { + t.Errorf("Slice too short, index not valid, case %d. Wanted index %d, got slice length %d", e.id, e.assignedIndex, len(bin.difatEntries)) + } else { + if bin.difatEntries[e.assignedIndex] != e.assignedValue { + t.Errorf("Wrong index assigned, case %d. At index %d, wanted %d, got %d", e.id, e.assignedIndex, e.assignedValue, bin.difatEntries[e.assignedIndex]) + } + } + } +} + +// Validate returns an error if the MSI doesn't pass internal consistency checks. +// If another MSIBinary is provided, Validate checks that data streams are bitwise identical. +// It also returns whether the dummy certificate was found. +func (bin MSIBinary) Validate(other *MSIBinary) (bool, error) { + // Check that fat sectors are marked as such in the fat. + for i, s := range bin.difatEntries { + if s != fatFreesect && !bin.sector.isLastInSector(i) && bin.fatEntries[s] != fatFatsect { + return false, fmt.Errorf("fat sector %d (index %d) is not marked as such in the fat", s, i) + } + } + // Check that difat sectors are marked as such in the fat. + s := secT(bin.header.FirstDifatSector) + i := numDifatHeaderEntries - 1 + num := 0 + for s != fatEndofchain { + if bin.fatEntries[s] != fatDifsect { + return false, fmt.Errorf("difat sector %d (offset %d in chain) is not marked as such in the fat", s, num) + } + i += int(bin.sector.Ints) + s = bin.difatEntries[i] + num++ + } + if num != int(bin.header.NumDifatSectors) { + return false, fmt.Errorf("wrong number of difat sectors found, wanted %d got %d", bin.header.NumDifatSectors, num) + } + + // Enumerate the directory entries. + // 1) Validate streams in the fat: Walk the chain, validate the stream length, + // and mark sectors in a copy of the fat so we can tell if any sectors are re-used. + // 2) Compare bytes in the data streams, to validate none of them changed. + // In principle we should match stream names, but in practice the directory entries are not + // reordered and the streams are not moved. + fatEntries := make([]secT, len(bin.fatEntries)) + copy(fatEntries, bin.fatEntries) + dirSector := secT(bin.header.FirstDirSector) + var entry MSIDirEntry + for { + // Fixed 128 byte directory entry size. + for i := offT(0); i < bin.sector.Size/numDirEntryBytes; i++ { + offset := offT(dirSector)*bin.sector.Size + i*numDirEntryBytes + binary.Read(bytes.NewBuffer(bin.contents[offset:]), binary.LittleEndian, &entry) + + // The mini fat hasn't been parsed, so skip those. The size check also skips non-stream + // entries. The signature stream has been freed, so skip that one too. + if entry.StreamSize < miniStreamCutoffSize || + bytes.Equal(entry.Name[:entry.NumNameBytes], signatureName) { + continue + } + allocatedSize := offT(0) + sector := secT(entry.StreamFirstSector) + for { + allocatedSize += bin.sector.Size + if fatEntries[sector] != fatEndofchain && fatEntries[sector] >= fatReserved { + return false, fmt.Errorf("Found bad/reused fat entry at sector %d; wanted value < %d (fatReserved), got %d", sector, secT(fatReserved), fatEntries[sector]) + } + // Technically we need not check beyond the end of stream data, but these sectors + // should not be modified at all. + if other != nil { + offset := offT(sector) * bin.sector.Size + if !bytes.Equal(bin.contents[offset:offset+bin.sector.Size], other.contents[offset:offset+bin.sector.Size]) { + return false, fmt.Errorf("Found difference in streams at sector %d", sector) + } + } + next := fatEntries[sector] + fatEntries[sector] = fatReserved // Detect if this is re-used. + if next == fatEndofchain { + break + } + sector = next + } + if uint64(allocatedSize) < entry.StreamSize { + return false, fmt.Errorf("Found stream with size greater than allocation, starting sector %d", entry.StreamFirstSector) + } + } + // Go to the next directory sector. + dirSector = bin.fatEntries[dirSector] + if dirSector == fatEndofchain { + break + } + } + + // Compare certs and signatures (other than dummy). + cert, index, err := getSuperfluousCert(bin.signedData) + if err != nil { + return false, fmt.Errorf("parse error in bin.signedData: %w", err) + } + if other != nil { + _, index2, err := getSuperfluousCert(other.signedData) + if err != nil { + return false, fmt.Errorf("parse error in other.signedData: %w", err) + } + pkcs7 := bin.signedData.PKCS7 + pkcs7Other := other.signedData.PKCS7 + i := 0 + i2 := 0 + for { + if i == index { + i++ + } + if i2 == index2 { + i2++ + } + if i >= len(pkcs7.Certs) || i2 >= len(pkcs7Other.Certs) { + if i < len(pkcs7.Certs) || i2 < len(pkcs7Other.Certs) { + return false, fmt.Errorf("number of certs mismatch, compare other %d vs this %d (possibly including dummy cert)", len(pkcs7Other.Certs), len(pkcs7.Certs)) + } + break + } + if !bytes.Equal(pkcs7.Certs[i].FullBytes, pkcs7Other.Certs[i2].FullBytes) { + return false, fmt.Errorf("cert contents mismatch, other cert index %d vs this cert index %d", i2, i) + } + i++ + i2++ + } + } + + return cert != nil, nil +} + +func TestMsiSuperfluousCert(t *testing.T) { + const tag = "258c 6320 e4c4 0258 169b 481a def0 8856" // Random data + expect := []struct { + infile string + }{ + {sourceMSI1}, + {sourceMSI2}, + {sourceMSI3}, + } + for _, e := range expect { + contents, err := ioutil.ReadFile(e.infile) + if err != nil { + t.Fatalf("Error reading test input %s: %v", e.infile, err) + } + + bin, err := NewBinary(contents) + if err != nil { + t.Fatalf("Error creating MSIBinary from test input %s: %v", e.infile, err) + } + msiBin := bin.(*MSIBinary) + hasDummy, err := msiBin.Validate(nil) + if err != nil { + t.Errorf("Input binary doesn't validate, created from test input %s: %v", e.infile, err) + } else if hasDummy { + t.Errorf("Input binary has the dummy cert (it shouldn't), created from test input %s", e.infile) + } + + // Note this adds the dummy cert to |bin|. + contents, err = bin.SetSuperfluousCertTag([]byte(tag)) + if err != nil { + t.Errorf("Error tagging test input %s: %v", e.infile, err) + continue + } + binTagged, err := NewBinary(contents) + if err != nil { + t.Errorf("Error parsing tagged binary from test input %s: %v", e.infile, err) + continue + } + + msiBinTagged := binTagged.(*MSIBinary) + hasDummy, err = msiBinTagged.Validate(msiBin) + if err != nil { + t.Errorf("Tagged binary doesn't validate, created from test input %s: %v", e.infile, err) + } else if !hasDummy { + t.Errorf("Tagged binary doesn't have the dummy cert (it should), created from test input %s", e.infile) + } + } +} + +func TestFindTag(t *testing.T) { + oid := []byte{ + 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0xce, 0x0f, 0x04, 0x82} + oidStr := string(oid) + oidSize := int64(len(oid) + 2) // includes size bytes + marker := []byte{0x47, 0x61, 0x63, 0x74, 0x32, 0x2e, 0x30, 0x4f, 0x6d, 0x61, 0x68, 0x61} + + // Create test strings. + expect := []struct { + name string + in string + start int64 + offset int64 + length int64 + hasErr bool + }{ + {"no padding", oidStr + "\x00\x10" + strings.Repeat("0", 16), 0, oidSize, 16, false}, + {"start padding", "1111" + oidStr + "\x00\x10" + strings.Repeat("1", 16), 0, 4 + oidSize, 16, false}, + {"start and end padding", "2222" + oidStr + "\x00\x10" + strings.Repeat("2", 20), 0, 4 + oidSize, 16, false}, + {"no tag", "3333" + "\x00\x10" + strings.Repeat("3", 20), 0, -1, 0, false}, + {"tag prior to search start", "4444" + oidStr + "\x00\x10" + strings.Repeat("4", 20), 10, -1, 0, false}, + {"error no length bytes", "5555" + oidStr, 0, -1, 0, true}, + {"error length too long", "6666" + oidStr + "\x00\x10" + strings.Repeat("6", 15), 0, -1, 0, true}, + } + + for _, e := range expect { + offset, length, err := findTag([]byte(e.in), e.start) + if offset != e.offset { + t.Errorf("test %s, got offset %d, want %d", e.name, offset, e.offset) + } + if length != e.length { + t.Errorf("test %s, got length %d, want %d", e.name, length, e.length) + } + if (err != nil) != e.hasErr { + t.Errorf("test %s, got error %v, want %v", e.name, err, e.hasErr) + } + } + + // Test end-to-end with testdata files. + expect2 := []struct { + infile string + size int64 + }{ + {sourceExe, 2048}, + {sourceExe, 1000}, + {sourceExe, 256}, + {sourceMSI1, 2048}, + {sourceMSI2, 2048}, + {sourceMSI2, 1000}, + {sourceMSI2, 256}, + {sourceMSI3, 2048}, + {sourceMSI4, 2048}, + } + for i, e := range expect2 { + contents, err := ioutil.ReadFile(e.infile) + if err != nil { + t.Fatalf("Case %d, error reading test input %s: %v", i, e.infile, err) + } + bin, err := NewBinary(contents) + if err != nil { + t.Fatalf("Case %d, error creating MSIBinary from test input %s: %v", i, e.infile, err) + } + // NewBinary may modify |contents|. + contents, err = ioutil.ReadFile(e.infile) + if err != nil { + t.Fatalf("Case %d, error reading test input %s: %v", i, e.infile, err) + } + + // No tag before tagging. + offset, _, err := findTag(contents, bin.certificateOffset()) + if err != nil { + t.Errorf("Case %d, error in findTag for untagged source %s: %v", i, e.infile, err) + } + if offset != -1 { + t.Errorf("Case %d, found tag in untagged input %s, want offset -1 got %d", i, e.infile, offset) + } + + // Apply a tag; find tag in contents; verify it's at marker with right size. + tag := make([]byte, e.size) + copy(tag[:], marker) + contents, err = bin.SetSuperfluousCertTag(tag) + if err != nil { + t.Fatalf("Case %d, error tagging source %s: %v", i, e.infile, err) + } + offset, length, err := findTag(contents, bin.certificateOffset()) + if err != nil { + t.Errorf("Case %d, error in findTag for source %s: %v", i, e.infile, err) + } + if length != e.size { + t.Errorf("Case %d, error in findTag for source %s: wanted returned length %d, got %d", i, e.infile, e.size, length) + } + if offset < 0 { + t.Errorf("Case %d, error in findTag for source %s: wanted returned offset >=0, got %d", i, e.infile, offset) + } else { + // Expect to find size bytes just prior to offset. + size := int64(binary.BigEndian.Uint16(contents[offset-2:])) + if size != e.size { + // Either the size is wrong, or (more likely) we found the wrong offset. + t.Errorf("Case %d, error in findTag for source %s, offset %d: wanted embedded size %d, got %d", i, e.infile, offset, e.size, size) + } + // Expect to find the marker at |offset| + idx := bytes.Index(contents[offset:], marker) + if idx != 0 { + t.Errorf("Case %d, error in findTag for source %s: after offset %d, wanted to find marker at idx 0, got %d", i, e.infile, offset, idx) + } + } } } diff --git a/common/certificate_tag/testdata/ChromeSetup.exe b/common/certificate_tag/testdata/ChromeSetup.exe index 716a9e282..57940628f 100644 Binary files a/common/certificate_tag/testdata/ChromeSetup.exe and b/common/certificate_tag/testdata/ChromeSetup.exe differ diff --git a/common/certificate_tag/testdata/OmahaTestSigned.msi b/common/certificate_tag/testdata/OmahaTestSigned.msi new file mode 100644 index 000000000..bc1b91388 Binary files /dev/null and b/common/certificate_tag/testdata/OmahaTestSigned.msi differ diff --git a/common/certificate_tag/testdata/googlechromestandaloneenterprise.msi b/common/certificate_tag/testdata/googlechromestandaloneenterprise.msi new file mode 100644 index 000000000..bcbe4f216 Binary files /dev/null and b/common/certificate_tag/testdata/googlechromestandaloneenterprise.msi differ diff --git a/common/certificate_tag/testdata/test7zSigned-smallcert.msi b/common/certificate_tag/testdata/test7zSigned-smallcert.msi new file mode 100644 index 000000000..66f134d63 Binary files /dev/null and b/common/certificate_tag/testdata/test7zSigned-smallcert.msi differ diff --git a/common/certificate_tag/testdata/test7zSigned.msi b/common/certificate_tag/testdata/test7zSigned.msi new file mode 100644 index 000000000..ba081c66a Binary files /dev/null and b/common/certificate_tag/testdata/test7zSigned.msi differ diff --git a/doc/ClientLog.md b/doc/ClientLog.md index dee260cec..d7634f684 100644 --- a/doc/ClientLog.md +++ b/doc/ClientLog.md @@ -27,7 +27,6 @@ Non-opt builds (dbg-win and coverage-win) allow provide much more logging and ha [LoggingLevel] LC_CORE=5 LC_NET=4 -LC_PLUGIN=3 LC_SERVICE=3 LC_SETUP=3 LC_SHELL=3 @@ -38,7 +37,6 @@ LC_REPORT=3 [LoggingSettings] EnableLogging=1 -LogFilePath="C:\foo\GoogleUpdate.log" MaxLogFileSize=10000000 ShowTime=1 @@ -53,4 +51,4 @@ NoSendDumpToServer=1 NoSendStackToServer=1 ``` # Log Size Limits # -Omaha tries to archive the log when the log size is greater than 10 MB. When the log is in use by more than one instance of Omaha the archiving operation will fail. However, there is a 100 MB limit to how big the log can be to prevent overfilling the hard drive. When this limit is reached the log file is cleared and the logging starts from the beginning. \ No newline at end of file +Omaha tries to archive the log when the log size is greater than 10 MB. When the log is in use by more than one instance of Omaha the archiving operation will fail. However, there is a 100 MB limit to how big the log can be to prevent overfilling the hard drive. When this limit is reached the log file is cleared and the logging starts from the beginning. diff --git a/doc/ClientUpdateProtocolEcdsa.md b/doc/ClientUpdateProtocolEcdsa.md index 4d91b49a4..c4b29d809 100644 --- a/doc/ClientUpdateProtocolEcdsa.md +++ b/doc/ClientUpdateProtocolEcdsa.md @@ -39,7 +39,7 @@ The server publishes an elliptic curve field/equation and a public key curve poi For each request, the client assembles three components: * The message body (the update request to be sent to the server). - * A small random number to be used as a client nonce for freshness (at least 32 bits). + * A small random number to be used as a client nonce for freshness (at least 256 bits). * A code to identify the public key the client will use to verify this request. The client converts the public key id and nonce to a string: the public key is converted to decimal, and the nonce to hexadecimal (lowercase a-f). @@ -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 server’s 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. + + +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. + + 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. @@ -74,4 +78,4 @@ The server should return the ECDSA signature and client SHA-256 hash in the **ET * The signature consists of two 256-bit integers (“R†and “Sâ€), in a ASN.1 sequence, encoded in DER; the hash is 256 bits. * Convert the DER-encoded signature to lowercase hex. The SHA-256 hash will be standard hex representation. - * Concatenate them with a colon as a delimiter: “signature:hashâ€. The final ETag value will max out at 194 characters (plus \n), which is a bit long, but shouldn’t be risking the 8k limit on HTTP headers. \ No newline at end of file + * Concatenate them with a colon as a delimiter: “signature:hashâ€. The final ETag value will max out at 194 characters (plus \n), which is a bit long, but shouldn’t be risking the 8k limit on HTTP headers. diff --git a/doc/CustomizingOmaha.md b/doc/CustomizingOmaha.md index 82dd951f9..bc7679142 100644 --- a/doc/CustomizingOmaha.md +++ b/doc/CustomizingOmaha.md @@ -20,7 +20,7 @@ The following items **MUST** be changed before releasing a fork of Omaha. Prefe > Modify **`kGlobalPrefix`** at the top of the file to contain your company name. - * **`omaha\base\const_goopdate.h`** + * **`omaha\common\const_goopdate.h`** > Modify the names of the service names (examples: **`omaha_task_name_c`**, **`omaham_service_name`**, etc.) to contain your product's name. @@ -32,7 +32,6 @@ The following items **MUST** be changed before releasing a fork of Omaha. Prefe > Generate new GUIDs for every interface and coclass. Changing the descriptive names for them isn't a bad idea either. (Do not, however, change code-level names such as `IAppBundle` or `GoogleUpdate3UserClass`.) - * **`omaha\plugins\update\activex\update_control_idl.idl`** > Generate new GUIDs for every interface and coclass. @@ -62,4 +61,4 @@ We strongly recommend making these changes before you release: The version number stored in all outputs is set in the file **`omaha\VERSION`**. Omaha has some functionality in it from Google Update related to bug workarounds when upgrading from prior versions, so don't set the VERSION to any lower than 1.3.23.0. -When releasing your fork of Omaha, we recommend starting the version at 1.3.25.0. Remember to bump the version up whenever releasing an updated version. \ No newline at end of file +When releasing your fork of Omaha, we recommend starting the version at 1.3.25.0. Remember to bump the version up whenever releasing an updated version. diff --git a/doc/DeveloperSetupGuide.md b/doc/DeveloperSetupGuide.md index d7b530ec1..e685eaa84 100644 --- a/doc/DeveloperSetupGuide.md +++ b/doc/DeveloperSetupGuide.md @@ -4,47 +4,50 @@ 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.1.1 and Windows SDK 10.0.17763.0. #### +#### Currently, the supported toolchain is Visual Studio 2022 Update 17.8.3 and Windows SDK 10.0.22621.0. #### -Visual Studio 2017 Update 15.9.12 should work too. +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. # Required Downloads/Tools # The following packages are required to build Omaha: * A copy of the Omaha source code. This can be done by cloning this repository. - * Microsoft Visual Studio 2017 or 2019. The free Visual Studio Community edition is sufficient to build. + * Microsoft Visual Studio 2022. The free Visual Studio Community edition is sufficient to build. * Download [here](https://visualstudio.microsoft.com/downloads) - * ATL Server headers - * Download [here](http://atlserver.codeplex.com). Omaha needs this library for regular expression support. * Windows 10 SDK. - * Download Windows 10 SDK [here](https://dev.windows.com/en-us/downloads/windows-10-sdk). - * Microsoft .NET Framework 2.0 - * This should be pre-installed on Windows Vista and Windows 7. This old version of SDK is needed for click-once compatibility with Windows XP systems. - * To verify, see if the file %WINDIR%\Microsoft.NET\Framework\v2.0.50727\csc.exe exists on your system. - * Download [here](https://www.microsoft.com/en-us/download/details.aspx?id=19988). - * The Windows Template Library (WTL) + * Visual Studio copy of Windows 10 SDK is sufficient to build with, if desired. + * Optionally, download and intall Windows 10 SDK [here](https://dev.windows.com/en-us/downloads/windows-10-sdk). + * The Windows Template Library (WTL) - WTL 10.0.10320 Release * Download WTL [here](http://sourceforge.net/projects/wtl/). + * hammer.bat has `OMAHA_WTL_DIR` set to `C:\wtl\files`. Change this if you unpacked to a different location. * The Windows Install XML (WiX) Toolkit, version 3.0 or later. * Download any of the v3 binaries packages [here](http://wix.sourceforge.net/). + * Set the `WIX` environment variable to the directory where you unpacked WiX. * Python 2.7.x * Download Python [here](https://www.python.org/downloads/release/python-2716). It can coexist with newer Python installs on a system. * You'll also need the pywin32 (Python for Windows) extensions for Python 2.7. - You can install with pip: `> python -m pip install pywin32` - assuming `python` is added to your `PATH` environmental variable. - It can also be downloaded [here](https://github.com/mhammond/pywin32/releases/download/b224/pywin32-224.win-amd64-py2.7.exe). + * The `OMAHA_PYTHON_DIR` is set to `C:\Python27`. Change this if you installed to a different location. * SCons 1.3.x (Be sure to use **1.3**, the 2.0 series is not backwards-compatible!) * Download SCons [here](http://sourceforge.net/projects/scons/files/scons/1.3.1/). + * Change this line in hammer.bat if you installed to a different location: `SCONS_DIR=C:\Python27\scons-1.3.1`. * Google Software Construction Toolkit - * Get the SCT source [here](http://code.google.com/p/swtoolkit/), either via direct download or via SVN checkout. + * Get the SCT source [here](https://code.google.com/archive/p/swtoolkit/downloads), either via direct download or via SVN checkout. + * Change this line in hammer.bat if you installed to a different location: `set SCT_DIR=C:\swtoolkit`. * The GO programming language * Download [here](https://golang.org/dl/) - * Google Protocol Buffers (3.6.0 or higher) [here](https://github.com/google/protobuf/releases). - * From the [release page](https://github.com/google/protobuf/releases), download the zip file protoc-$VERSION-win32.zip. It contains the protoc binary. Unzip the contents under C:\protobuf. After that, download the zip file protobuf-cpp-$VERSION.zip. Unzip the "src" sub-directory contents to C:\protobuf\src. If other directory is used, please edit the environment variables in the hammer.bat, specifically, OMAHA_PROTOBUF_BIN_DIR and OMAHA_PROTOBUF_SRC_DIR. + * Change this line in hammer.bat if you installed to a different location: `set GOROOT=C:\go`. + * Google Protocol Buffers (currently tested with v3.17.3) [here](https://github.com/protocolbuffers/protobuf/releases). + * From the [release page](https://github.com/protocolbuffers/protobuf/releases), download the zip file `protoc-$VERSION-win32.zip`. It contains the protoc binary. Unzip the contents under `C:\protobuf`. After that, download the zip file `protobuf-cpp-$VERSION.zip`. Unzip the `src` sub-directory contents to `C:\protobuf\src`. If other directory is used, please edit the environment variables in the hammer.bat, specifically, `OMAHA_PROTOBUF_BIN_DIR` and `OMAHA_PROTOBUF_SRC_DIR`. * Third-party dependencies: - * breakpad. Source code [here](https://code.google.com/p/google-breakpad/source/checkout) - * googletest. Source code [here](https://github.com/google/googletest). This includes both gtest and gmock frameworks. - * Use git clone, git svn clone, or other way to get the source code for these projects into the third_party directory in the root of this repository. - * libzip 1.5.2. Source code [here](https://libzip.org/download/libzip-1.5.2.tar.xz). Unzip the contents of libzip-1.5.2.tar.gz\libzip-1.5.2.tar\libzip-1.5.2\ into the directory googleclient\third_party\libzip. The Omaha repository contains two generated configuration files in `base\libzip`, or one could build the libzip library and generate the files. A change has been made to config.h to disable zip crypto `#undef HAVE_CRYPTO`, or else the zip code won't build because of a compile time bug. - * zlib 1.2.11. Source code [here](https://zlib.net/zlib-1.2.11.tar.gz). Unzip the contents of zlib-1.2.11.tar.gz\zlib-1.2.11.tar\zlib-1.2.11\ into the directory googleclient\third_party\zlib\v1_2_11. + * breakpad. Download [here](https://github.com/google/breakpad/archive/refs/heads/main.zip). Tested with commit [11ec9c](https://github.com/google/breakpad/commit/11ec9c32888c06665b8838f709bd66c0be9789a6) from Dec 11, 2023. + - Unzip everything inside `breakpad-master.zip\breakpad-master` to `third_party\breakpad`. + * googletest. Download [here](https://github.com/google/googletest/archive/refs/heads/master.zip). Tested with commit [96eadf +](https://github.com/google/googletest/commit/96eadf659fb75ecda943bd97413c71d4c17c4f43) from Dec 22, 2023. This includes both gtest and gmock frameworks. + - Unzip everything inside `googletest-master.zip\googletest-master` to `third_party\googletest`. + * libzip 1.7.3. Source code [here](https://libzip.org/download/libzip-1.7.3.tar.xz). Unzip the contents of `libzip-1.7.3.tar.gz\libzip-1.7.3.tar\libzip-1.7.3\` into the directory `third_party\libzip`. The Omaha repository contains two generated configuration files in `base\libzip`, or one could build the libzip library and generate the files. A change has been made to config.h to disable zip crypto `#undef HAVE_CRYPTO`, or else the zip code won't build because of a compile time bug. + * zlib 1.2.11. Source code [here](https://zlib.net/zlib-1.2.11.tar.gz). Unzip the contents of `zlib-1.2.11.tar.gz\zlib-1.2.11.tar\zlib-1.2.11\` into the directory `third_party\zlib`. To run the unit tests, one more package is needed. Download the Windows Sysinternals PSTools suite [here](https://technet.microsoft.com/en-us/sysinternals/bb897553) and save psexec.exe somewhere. Then, set a system environment variable named OMAHA_PSEXEC_DIR to the directory containing psexec.exe. @@ -66,10 +69,11 @@ To run the unit tests, one more package is needed. Download the Windows Sysinter d---rwx---+ 1 sorin Domain Users 0 Jun 30 17:58 third_party d:\src\omahaopensource\omaha>ls -l third_party - total 16 - d---rwx---+ 1 sorin Domain Users 0 Jul 14 12:52 breakpad - drwxrwx---+ 1 Administrators Domain Users 0 Sep 1 11:52 googletest - d---rwx---+ 1 sorin Domain Users 0 Aug 7 18:58 lzma + drwxrwxrwx 1 sorin sorin 4096 Mar  1 19:37 breakpad + drwxrwxrwx 1 sorin sorin 4096 Mar  1 19:41 googletest + drwxrwxrwx 1 sorin sorin 4096 Mar  1 19:58 libzip + drwxrwxrwx 1 sorin sorin 4096 Mar  1 16:30 lzma + drwxrwxrwx 1 sorin sorin 4096 Mar  1 20:07 zlib ``` ## Environment Variables ## @@ -91,7 +95,7 @@ A larger suite of unit tests is also included in the Omaha source. ## Running Unit Tests ## -The Omaha build proces includes building an automated unit test suite, based on the [GTest](https://github.com/google/googletest) framework. In order to run it, there are two pieces of preparation you must do: +The Omaha build process includes building an automated unit test suite, based on the [GTest](https://github.com/google/googletest) framework. In order to run it, there are two pieces of preparation you must do: * Create the following registry key: `HKEY_LOCAL_MACHINE\SOFTWARE\OmahaCompanyName\UpdateDev`. Then, add a string value named `TestSource` with the value `ossdev`. (Note: If you are on 64 bit Windows and are using `regedit` to create the value then you need to place it in `HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\OmahaCompanyName\UpdateDev`. [This allows 32 bit processes to read it.](https://support.microsoft.com/en-us/kb/305097)). * Download the Windows Sysinternals PSTools suite (available [here](http://technet.microsoft.com/en-us/sysinternals/bb897553)) and save `psexec.exe` somewhere. Then, set an environment variable named `OMAHA_PSEXEC_DIR` to the directory containing `psexec.exe`. diff --git a/doc/Omaha3SourceOrganization.md b/doc/Omaha3SourceOrganization.md index 8e9004e9b..d18e6d4fd 100644 --- a/doc/Omaha3SourceOrganization.md +++ b/doc/Omaha3SourceOrganization.md @@ -31,7 +31,6 @@ omaha\ mi_exe_stub\ Produces a stub EXE, mi_exe_stub.exe, that will be combined with a TAR to produce the untagged meta-installer. (The script to actually do the merge lives in installers\, mentioned below.) -plugins\ Produces the browser plugin, npGoogleUpdate3.dll. recovery\ Produces tools for “Code Red†- a mechanism that the apps being managed by Omaha can use to check Omaha’s integrity, and restore it if it appears broken. diff --git a/doc/Omaha3Walkthrough.md b/doc/Omaha3Walkthrough.md index 484ab5894..fa03aeb17 100644 --- a/doc/Omaha3Walkthrough.md +++ b/doc/Omaha3Walkthrough.md @@ -35,7 +35,7 @@ The Omaha Client always operates at user privilege levels and owns the UI of Oma * Setup - Create or update a permanent Omaha install, of either user or machine variety. * Install - Invoke the COM server to create a state machine object, fill it out with apps to be managed, and call a suitable function on it such as `checkForUpdate()`, `download()`, or `install()`. From that point onwards, poll the state object as the COM server does the work for you, and update the UI as the states advance. -In general, when referring to “the clientâ€, we’re referring to the official Google Update client, which happens to live in the same executable as the COM Server; the role that the executable plays is decided simply by which command line is passed to it. However, there are other clients that may access the COM server; some of them we own (the web browser plugins), and some we do not own (partner applications which access our COM APIs directly). The server must stay as secure as possible, and sanitize all input. +In general, when referring to “the clientâ€, we’re referring to the official Google Update client, which happens to live in the same executable as the COM Server; the role that the executable plays is decided simply by which command line is passed to it. However, there are other clients that may access the COM server; The server must stay as secure as possible, and sanitize all input. ## Example Code Flow ## @@ -50,7 +50,6 @@ appname=Google%20Chrome&needsadmin=False&lang=en" * We check the machine to see if there’s already a user Omaha installed with a version newer than or equal to ours. (If it’s equal to ours, we will do some supplementary checking to make sure that the installed copy is sane and working properly, and if not, we over-install.) Let’s assume that there is no user Omaha installed. We will create the direct directory in `AppData`, copy over the files, and then make entries in the Registry to do the following: * Register our COM servers * Create scheduled tasks to check for an update every five hours - * Expose our web browser plugins to IE/Firefox/Chrome/Safari/Opera * Store initial configuration/state for Omaha itself in the Registry * Register Omaha itself as an Omaha-managed application, so it can check for updates for itself * The client then starts a new copy of itself in its permanent installed location, modifying the command line from `/install` to `/handoff`. Once again, the constant shell loads Goopdate and passes the command line along - this time, however, we’re using the constant shell in the newly-created permanent install of Omaha, rather than the one in the temp directory. @@ -68,25 +67,26 @@ A crucial thing to pick up here is that, since one file (goopdate.dll) does many So, what files are actually in a permanent install of Omaha once it’s completed? -| `GoogleUpdate.exe` | The Constant Shell. Just takes the command line given to it and passes it to goopdate.dll; if necessary, it will validate that goopdate has an intact digital signature from Google. | +| Filename | Description | |:-------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `GoogleCrashHandler.exe` | A copy of the constant shell, renamed for Omaha2 compatibility reasons. Expected to always be started with /crashhandler. | -| `GoogleUpdateBroker.exe`
GoogleUpdateOnDemand.exe - - - -
COM Forwarders. Both of these are small EXEs whose sole purpose is to take their own command line, append a command line switch to the end, and pass it to the Constant Shell.
goopdate.dll The central Omaha3 binary.
goopdateres_*.dll Resource-only DLLs, one per language, containing localized strings. As part of its startup, Goopdate will read the “lang†extra-args parameter if one exists (or the Registry) and select a language to load.
npGoogleUpdate3.dll Our web browser plugin. (It actually contains two plugins: ActiveX plugins for IE, and an NPAPI plugin for Firefox, Chrome, and other browsers that use that.) Allows Javascript on selected subdomains of google.com to access and use the COM Server.
psmachine.dll
psuser.dll
Custom marshaling stubs used by the COM Server. Used in order to work around some Windows bugs that are triggered by having both Machine and User Omaha installed simultaneously.
- -The directory tree typically looks like this:
-
-
Google\
- Update\
- 1.3.21.53\ The install location for the current version of Omaha.
- ... the files listed above ...
- Download\ Temp area for installers currently being downloaded.
- Install\ Temp area for installers that are verified and about to be launched.
- GoogleUpdate.exe A copy of the the constant shell. This will look into the registry for
- the most recently successfully installed version of Omaha, and
- use the goopdate.dll there.
-
+| `GoogleUpdate.exe` | **The Constant Shell.** Just takes the command line given to it and passes it to goopdate.dll; if necessary, it will validate that goopdate has an intact digital signature from Google. | +| `GoogleCrashHandler.exe` | A copy of the constant shell, renamed for Omaha2 compatibility reasons. Expected to always be started with /crashhandler. | +| `GoogleUpdateBroker.exe`
`GoogleUpdateOnDemand.exe` | **COM Forwarders**. Both of these are small EXEs whose sole purpose is to take their own command line, append a command line switch to the end, and pass it to the Constant Shell. +| `goopdate.dll` | The central Omaha3 binary. | +| `goopdateres_*.dll` | **Resource-only DLLs, one per language, containing localized strings**. As part of its startup, Goopdate will read the “lang†extra-args parameter if one exists (or the Registry) and select a language to load. | +| `psmachine.dll`
`psuser.dll` | **Custom marshaling stubs used by the COM Server**. Used in order to work around some Windows bugs that are triggered by having both Machine and User Omaha installed simultaneously.| + + +The directory tree typically looks like this: +``` +Google\ + Update\ + 1.3.21.53\ The install location for the current version of Omaha. + ... the files listed above ... + Download\ Temp area for installers currently being downloaded. + Install\ Temp area for installers that are verified and about to be launched. + GoogleUpdate.exe A copy of the the constant shell. This will look into the registry for + the most recently successfully installed version of Omaha, and + use the goopdate.dll there. +``` At this point, the value of the Constant Shell becomes obvious - we can modify or change the location of goopdate.dll, without having to touch GoogleUpdate.exe in most cases. This means that minor changes or bugfixes in Omaha can be pushed out, in the form of an update to goopdate.dll, without triggering a prompt from firewalls, virus scanners, or process whitelisters that may be in place on a machine. diff --git a/doc/OmahaOverview.html b/doc/OmahaOverview.html index bc0aedfe2..20b858204 100644 --- a/doc/OmahaOverview.html +++ b/doc/OmahaOverview.html @@ -1143,7 +1143,7 @@

The Google Update server is not part of the Omaha open source project. Providing updates for applications requires a server that implements the -Omaha Server Protocol.
+Omaha Server Protocol.

Omaha has two server components: autoupdate and download. This separation of server responsibilities is desirable because the requirements are different: downloading large binaries takes high bandwidth and can tolerate relatively high latency, whereas small transactions such as update pings may be much more frequent but require extremely low bandwidth per transaction. These differences allow Google to provision the two servers appropriately. diff --git a/doc/ServerProtocolV2.md b/doc/ServerProtocolV2.md index 09db2b743..ddc594070 100644 --- a/doc/ServerProtocolV2.md +++ b/doc/ServerProtocolV2.md @@ -180,7 +180,7 @@ _Request Attributes_ * client (optional): Similar to brand code. * iid (optional): A random GUID used to uniquely count install attempts. For example, if a user fails to install then re-runs the installer and succeeds, we might want to count that as one "attempt". * installage (optional): The number of days since the app was first installed. - * installsource (optional): Specifies the source of the request. Examples include "oneclick", "clickonce", "ondemandupdate", "ondemandcheckforupdate", "offline", "scheduler", "core". This value is specified to the Omaha client on the command line. + * installsource (optional): Specifies the source of the request. Examples include "clickonce", "ondemandupdate", "ondemandcheckforupdate", "offline", "scheduler", "core". This value is specified to the Omaha client on the command line. * fp (optional): Specifies a version-agnostic identifier for the last downloaded binary for this app. Usually "1.X" where X is the sha-256 hash of the downloaded binary. _Response Attributes_ diff --git a/doc/ServerProtocolV3.md b/doc/ServerProtocolV3.md index 379dfa46c..58cc1553d 100644 --- a/doc/ServerProtocolV3.md +++ b/doc/ServerProtocolV3.md @@ -2,7 +2,7 @@ This document describes version 3 of the Omaha client-server protocol. Omaha launched on Windows with this version of the protocol in May 2011. (Version 2 of the protocol launched in May 2007 on Windows and May 2008 on Mac; Version 1 of the protocol was never deployed publicly.) -Version 2 is documented [here](ServerProtocolV2.md). An older description of the V3 protocol is [here](ServerProtocol.md). +Version 2 is documented [here](ServerProtocolV2.md). An older description of the V3 protocol is [here](ServerProtocol.md). Version 3.1 is documented [here](https://chromium.googlesource.com/chromium/src.git/+/master/docs/updater/protocol_3_1.md). ## Introduction ## The Omaha protocol is designed to facilitate the acquisition, delivery, and metrics of software updates over the Internet. It is an application-layer protocol on top of HTTP. @@ -47,7 +47,7 @@ Compatible clients and servers SHOULD implement at minimum the following version Versions are members of an ordered set. A version `A` is greater than a version `B` if and only if there is at least one element in `A` that is greater than the corresponding element in `B`, and all elements preceding that element in `A` are equal to their corresponding elements in `B`. Two versions are equal if and only if all their elements are equal. ## GUIDs ## -The Omaha protocol deals with globally-unique identifiers in multiple places. For the purpose of the protocol, a GUID one of the following formats: +The Omaha protocol deals with globally-unique identifiers in multiple places. For the purpose of the protocol, a GUID has one of the following formats: 1. a 128-bit value, serialized as a string of hexadecimal digits as follows: "`{00000000-1111-2222-3333-444444444444}`" (e.g. "`{430FD4D0-B729-4F61-AA34-91526481799D}`"). 1. A lowercase Mac bundle ID (for example `com.google.chrome`). This format is only allowed to identify a product. @@ -166,11 +166,12 @@ Each product that is contained in the request is represented by exactly one `` level. See [#Packages\_&\_Fingerprints](#packages--fingerprints). Default: "". - * `cohort`: A machine-readable string identifying the release cohort (channel) that the app belongs to. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. Default: "". - * `cohorthint`: An machine-readable enum indicating that the client has a desire to switch to a different release cohort. The exact legal values are app-specific and should be shared between the server and app implementations. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. Default: "". + * `cohort`: A machine-readable string identifying the release cohort (channel) that the app belongs to. Limited to ASCII characters 32 to 126 (inclusive) and a maximum length of 1024 characters. Default: "". + * `cohorthint`: An machine-readable enum indicating that the client has a desire to switch to a different release cohort. The exact legal values are app-specific and should be shared between the server and app implementations. Limited to ASCII characters 32 to 126 (inclusive) and a maximum length of 1024 characters. Default: "". * `cohortname`: A stable non-localized human-readable enum indicating which (if any) set of messages the app should display to the user. For example, an app with a cohortname of "beta" might display beta-specific branding to the user. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. Default: "". + * `release_channel`: A string indicating to the server which release channel this installation of the application should receive future updates from. Examples include "stable", "beta", "dev". An empty string indicates no preference. Default: "". ##### Legal Child Elements ##### * Any number of ``. @@ -237,7 +238,7 @@ New clients are recommended to use the `ad` and `rd` attributes of the ``, ##### Attributes ##### * `active`: "1" if the app was active since the previous request that contained a ``. Otherwise, "0". If `a` or `ad` is explicitly transmitted, `active` may be omitted. Default: "0". * `a`: If transmitted, the app was active since the request that contained a ``. In this case, the value is the number of integral 24-hour periods that have elapsed since the start of the America/Los\_Angeles calendar day that the previous active ping was sent on. See [#Client-Regulated\_Counting\_(Days-Based)](#client-regulated-Counting-days-based). A value of "-1" signifies that there was no previous active ping. - * `r`: The number of integral 24-hour periods that have elapsed sine the start of the America/Los\_Angeles calendar day that the previous ping was sent on. See [#Client-Regulated\_Counting\_(Days-Based)](#client-regulated-counting-days-based). A value of "-1" signifies that there was no previous active ping. Default: "0". + * `r`: The number of integral 24-hour periods that have elapsed since the start of the America/Los\_Angeles calendar day that the previous ping was sent on. See [#Client-Regulated\_Counting\_(Days-Based)](#client-regulated-counting-days-based). A value of "-1" signifies that there was no previous active ping. Default: "0". * `ad`: The value of the `elapsed_days` attribute of the `` element in the server's reply to the previous active ping. See [#Client-Regulated\_Counting\_(Date-Based)](#client-regulated-counting-date-based). A value of "-1" signifies that there was no such previous request. A value of "-2" signifies that the value is not known. Default: "-2". * `rd`: The value of the `elapsed_days` attribute of the `` element in the server's reply to the previous ping. See [#Client-Regulated\_Counting\_(Date-Based)](#client-regulated-counting-date-based). A value of "-1" signifies that there was no such previous request. A value of "-2" signifies that the value is not known. Default: "-2". * `ping_freshness`: A random 128-bit number. See [#Unreliable\_Client\_Storage](#unreliable-client-storage) The client SHOULD store the per-product value alongside whatever data it uses to track `ad`, `rd`, `a`, and `r`. The client MUST rotate the value to a new random 128-bit number whenever the data used to track `ad`, `rd`, `a`, or `r` is updated. The server MAY interpret duplicate `pingfresh` values as indicating that the client has been re-imaged to a previous state. A value of "" signifies that no value was available. Default: "". @@ -343,6 +344,7 @@ None. * `tttoken`: An opaque access token that can be used to identify the requesting client as a member of a trusted-tester group. If non-empty, the request SHOULD be sent over SSL or another. Default: "". * `updatedisabled`: An indication of whether the client will honor an update response, if it recieves one. Legal values are "true" (indicating that the client will ignore any update instruction) and "false" (indicating that the client will attempt an update if one is instructed). Default: "false". * `targetversionprefix`: A component-wise prefix of a version number, or a complete version number suffixed with the `$` character. The server SHOULD NOT return an update instruction to a version number that does not match the prefix or complete version number. The prefix is interpreted a dotted-tuple that specifies the exactly-matching elements; it is not a lexical prefix. (For example, "1.2.3" MUST match "1.2.3.4" but MUST NOT match "1.2.34".) Default: "". + * `sameversionupdate`: An indication of whether an update should be offered if the client has the same version as the update. Legal values are "true" (indicating that same version update should be offered) and "false" (indicating that same version update should not be offered). Default: "false". ##### Legal Child Elements ##### None. @@ -387,9 +389,9 @@ Each product that is contained in the response is represented by exactly one ``. If this attribute is transmitted in the response (even if the value is empty-string), the client should overwrite the current cohort of this app with the sent value. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. No default value: the lack of a transmitted value has a different meaning than any transmitted value. - * `cohorthint`: See cohorthint in the ``. If sent (even if the value is empty-string), the client should overwrite the current cohorthint of this app with the sent value. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. No default value: the lack of a transmitted value has a different meaning than any transmitted value. - * `cohortname`: See cohortname in the ``. If sent (even if the value is empty-string), the client should overwrite the current cohortname of this app with the sent value. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. No default value: the lack of a transmitted value has a different meaning than any transmitted value. + * `cohort`: See cohort in the ``. If and only if this attribute is transmitted in the response (even if the value is empty-string), the client should overwrite the current cohort of this app with the sent value. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. No default value: the lack of a transmitted value has a different meaning than any transmitted value. + * `cohorthint`: See cohorthint in the ``. If and only if sent (even if the value is empty-string), the client should overwrite the current cohorthint of this app with the sent value. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. No default value: the lack of a transmitted value has a different meaning than any transmitted value. + * `cohortname`: See cohortname in the ``. If and only if sent (even if the value is empty-string), the client should overwrite the current cohortname of this app with the sent value. Limited to ASCII characters 32 to 127 (inclusive) and a maximum length of 1024 characters. No default value: the lack of a transmitted value has a different meaning than any transmitted value. ##### Legal Child Elements ##### * At most one ``. diff --git a/doc/UsingOmaha.md b/doc/UsingOmaha.md index 5742c5849..fc5b7887e 100644 --- a/doc/UsingOmaha.md +++ b/doc/UsingOmaha.md @@ -22,10 +22,10 @@ The /silent flag can be used to install with no user interaction. /silent needs **Note: Before you can use Omaha for your own application(s), you must have an update server that supports the Omaha ServerProtocol.** Google does not currently provide an update server for external applications. -See CustomizingOmaha for information on customizing the Omaha client to work with your server and applications. +See [CustomizingOmaha](CustomizingOmaha.md) for information on customizing the Omaha client to work with your server and applications. # Create a Metainstaller for Your Application # The quoted string after `/install` in the command line above is called the tag. This tag can be added to the metainstaller (`GoogleUpdateSetup.exe`) so that double-clicking the metainstaller will install the application specified by the tag. -See TaggedMetainstallers. \ No newline at end of file +See [TaggedMetainstallers](TaggedMetainstallers.md). diff --git a/omaha/VERSION b/omaha/VERSION index b4a3be28e..36eb2b3ab 100644 --- a/omaha/VERSION +++ b/omaha/VERSION @@ -6,6 +6,3 @@ version_major = 1 # 1-65535 version_minor = 3 # 0-65535 version_build = 99 # 1-65535 version_patch = 0 # 0-65535 - -oneclick_plugin_version = 9 -update_plugin_version = 3 diff --git a/omaha/base/app_util.cc b/omaha/base/app_util.cc index d5fcfb1c9..da9c04a78 100644 --- a/omaha/base/app_util.cc +++ b/omaha/base/app_util.cc @@ -220,7 +220,7 @@ DWORD DllGetVersion(const CString& dll_path) { HRESULT hr = (*pfn)(&dvi); if (SUCCEEDED(hr)) { // Since we're fitting both the major and minor versions into a DWORD, - // let's sanity check that we're not in an overflow situation here + // check that we're not in an overflow situation here ASSERT1(dvi.dwMajorVersion <= 0xFFFF); ASSERT1(dvi.dwMinorVersion <= 0xFFFF); dwVersion = MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion); diff --git a/omaha/base/app_util_unittest.cc b/omaha/base/app_util_unittest.cc index 6dd32b89b..333320a29 100644 --- a/omaha/base/app_util_unittest.cc +++ b/omaha/base/app_util_unittest.cc @@ -71,7 +71,7 @@ TEST(AppUtilTest, AppUtil) { // Use kernel32.dll // Get the loading address of kernel32. - HMODULE kernel32Module = ::LoadLibrary(kKernel32Name); + HMODULE kernel32Module = LoadSystemLibrary(kKernel32Name); EXPECT_TRUE(kernel32Module != NULL); // Test the dll module handle using an address. @@ -90,7 +90,7 @@ TEST(AppUtilTest, AppUtil) { // DLL versioning. // For the tests to succeed, shell32.dll must be loaded in memory. - HMODULE shell32Module = ::LoadLibrary(kShell32Name); + HMODULE shell32Module = LoadSystemLibrary(kShell32Name); EXPECT_NE(0, DllGetVersion(GetSystemDir() + L"\\" + kShell32Name)); EXPECT_NE(0, DllGetVersion(kShell32Name)); EXPECT_NE(0, SystemDllGetVersion(kShell32Name)); @@ -98,7 +98,7 @@ TEST(AppUtilTest, AppUtil) { // For the tests to succeed, comctl32.dll must be loaded in memory. // ComCtl32 may be loaded from a side-by-side (WinSxS) directory, so it is not // practical to do a full-path or SystemDllGetVersion test with it. - HMODULE comctl32_module = ::LoadLibrary(kComCtl32Name); + HMODULE comctl32_module = LoadSystemLibrary(kComCtl32Name); EXPECT_NE(0, DllGetVersion(kComCtl32Name)); // kernel32 does not export DllGetVersion. @@ -145,7 +145,7 @@ TEST(AppUtilTest, GetModuleHandleFromAddress) { EXPECT_EQ(GetModuleHandle(NULL), module); // Get the address of a function in kernel32. - HMODULE kernel32_module = ::LoadLibrary(_T("kernel32")); + HMODULE kernel32_module = LoadSystemLibrary(_T("kernel32")); ASSERT_TRUE(kernel32_module); HMODULE readfile_module = GetModuleHandleFromAddress( diff --git a/omaha/base/apply_tag.cc b/omaha/base/apply_tag.cc index cc50e3ae0..c8809103d 100644 --- a/omaha/base/apply_tag.cc +++ b/omaha/base/apply_tag.cc @@ -18,8 +18,8 @@ #include "omaha/base/apply_tag.h" #include -#include #include +#include #include #include "omaha/base/utils.h" @@ -40,15 +40,7 @@ ApplyTag::ApplyTag() bool ApplyTag::IsValidTagString(const char* tag_string) { ASSERT1(tag_string); - - CAtlRegExp regex; - REParseError error = regex.Parse(kValidTagStringRegEx); - if (error != REPARSE_ERROR_OK) { - return false; - } - - CAtlREMatchContext context; - return !!regex.Match(tag_string, &context); + return std::regex_match(tag_string, std::regex(kValidTagStringRegEx)); } HRESULT ApplyTag::Init(const TCHAR* signed_exe_file, diff --git a/omaha/base/atl_regexp.cc b/omaha/base/atl_regexp.cc deleted file mode 100644 index ac0babba5..000000000 --- a/omaha/base/atl_regexp.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2005-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/base/atl_regexp.h" - -#include -#include - -namespace omaha { - -constexpr int kMaxArgs = 16; - -AtlRE::AtlRE(const TCHAR* pattern, bool case_sensitive) { - ASSERT(pattern, (L"")); - REParseError status = re_.Parse(pattern, case_sensitive); - ASSERT(status == REPARSE_ERROR_OK, (L"")); -} - -AtlRE::~AtlRE() { -} - -bool AtlRE::DoMatchImpl(const TCHAR* text, - CString* args[], - int n, - const TCHAR** match_end) const { - // text may be NULL. - ASSERT(args, (L"")); - - if (!text) { - return false; - } - - AtlMatchContext matches; - BOOL b = re_.Match(text, &matches, match_end); - if (!b || matches.m_uNumGroups < static_cast(n)) { - return false; - } - - // Oddly enough, the Match call will make match_end - // point off the end of the string if the result is at the - // end of the string. We check this and handle it. - if (match_end) { - if ((*match_end - text) >= lstrlen(text)) { - *match_end = NULL; - } - } - - const TCHAR* start = 0; - const TCHAR* end = 0; - for (int i = 0; i < n; ++i) { - matches.GetMatch(i, &start, &end); - const ptrdiff_t size = end - start; - if (size > INT_MAX) { - return false; - } - const int len = static_cast(size); - ASSERT(args[i], (L"")); - // len+1 for the NULL character that's placed by lstrlen - VERIFY1(lstrcpyn(args[i]->GetBufferSetLength(len), start, len + 1) != NULL); - } - return true; -} - -} // namespace omaha diff --git a/omaha/base/atl_regexp.h b/omaha/base/atl_regexp.h deleted file mode 100644 index 1353a16d3..000000000 --- a/omaha/base/atl_regexp.h +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2005-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// ATL Regular expression class that implements the RE interface. -// See MSDN help for example patterns (lookup help on CAtlRegExp) -// NOTE: This class adds about 8k to release builds. -// TODO(omaha): add a unit test, showing examples, testing functionality -// and perf. - -#ifndef OMAHA_COMMON_ATL_REGEXP_H__ -#define OMAHA_COMMON_ATL_REGEXP_H__ - -#include -#pragma warning(push) -// enumerator 'identifier' in switch of enum 'enumeration' is not explicitly -// handled by a case label -#pragma warning(disable:4061) -#include -#pragma warning(pop) -#include -#include "base/basictypes.h" -#include "omaha/base/regexp.h" -#include "omaha/base/string.h" - -namespace omaha { - -// This class is essentially a copy of CAtlRECharTraitsWide from , but -// I've replaced CRT functions that are not in our minicrt. -// -// TODO(omaha): do we need this? -class CAtlRECharTraitsWideNoCrt -{ -public: - typedef WCHAR RECHARTYPE; - - // ATL80 addition. - static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw() - { -#ifndef ATL_NO_CHECK_BIT_FIELD - ATLASSERT(UseBitFieldForRange()); -#endif - return static_cast(*sz); - } - - static RECHARTYPE *Next(const RECHARTYPE *sz) throw() - { - return (RECHARTYPE *) (sz+1); - } - - static int Strncmp(const RECHARTYPE *szLeft, - const RECHARTYPE *szRight, size_t nCount) throw() - { - return String_StrNCmp(szLeft, szRight, nCount, false); - } - - static int Strnicmp(const RECHARTYPE *szLeft, - const RECHARTYPE *szRight, size_t nCount) throw() - { - return String_StrNCmp(szLeft, szRight, nCount,true); - } - - static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw() - { - return String_FastToLower(sz); - } - - // In ATL 80 Strlwr must be passed a buffer size for security reasons. - // TODO(omaha): Implement the function to consider the nSize param. - static RECHARTYPE *Strlwr(RECHARTYPE *sz, int) throw() - { - return Strlwr(sz); - } - - static long Strtol(const RECHARTYPE *sz, - RECHARTYPE **szEnd, int nBase) throw() - { - return wcstol(sz, szEnd, nBase); - } - - static int Isdigit(RECHARTYPE ch) throw() - { - return String_IsDigit(ch) ? 1 : 0; - } - - static const RECHARTYPE** GetAbbrevs() - { - static const RECHARTYPE *s_szAbbrevs[] = - { - L"a([a-zA-Z0-9])", // alpha numeric - L"b([ \\t])", // white space (blank) - L"c([a-zA-Z])", // alpha - L"d([0-9])", // digit - L"h([0-9a-fA-F])", // hex digit - L"n(\r|(\r?\n))", // newline - L"q(\"[^\"]*\")|(\'[^\']*\')", // quoted string - L"w([a-zA-Z]+)", // simple word - L"z([0-9]+)", // integer - NULL - }; - - return s_szAbbrevs; - } - - static BOOL UseBitFieldForRange() throw() - { - return FALSE; - } - - static int ByteLen(const RECHARTYPE *sz) throw() - { - return int(lstrlen(sz)*sizeof(WCHAR)); - } -}; - -typedef CAtlRegExp AtlRegExp; -typedef CAtlREMatchContext AtlMatchContext; - -// implements the RE class using the ATL Regular Expressions class -class AtlRE : public RE { - public: - - AtlRE(const TCHAR* pattern, bool case_sensitive = true); - virtual ~AtlRE(); - - protected: - // See regexp.h for an explanation. - virtual bool DoMatchImpl(const TCHAR* text, - CString* args[], - int n, - const TCHAR** match_end) const; - - private: - mutable AtlRegExp re_; - DISALLOW_COPY_AND_ASSIGN(AtlRE); -}; - -} // namespace omaha - -#endif // OMAHA_COMMON_ATL_REGEXP_H__ diff --git a/omaha/base/atl_regexp_unittest.cc b/omaha/base/atl_regexp_unittest.cc deleted file mode 100644 index a1c426133..000000000 --- a/omaha/base/atl_regexp_unittest.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2005-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/base/atl_regexp.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -TEST(AtlRETest, AtlRE) { - AtlRE newline_test_re(_T("ab{\\n}cd")); - const TCHAR* newline_strings[] = { _T("\n"), _T("\r"), _T("\r\n") }; - for (size_t i = 0; i < arraysize(newline_strings); ++i) { - CString content(_T("ab")); - content.Append(newline_strings[i]); - content.Append(_T("cd")); - const TCHAR* content_ptr = content.GetString(); - CString newline; - EXPECT_TRUE(RE::FindAndConsume(&content_ptr, newline_test_re, &newline)); - EXPECT_STREQ(newline, newline_strings[i]); - } - - // Check that AtlRE works with Unicode characters. - AtlRE one_two_three_four(_T("\x1234")); - EXPECT_TRUE(RE::PartialMatch(_T("\x4321\x1234\x4321"), one_two_three_four)); -} - -} // namespace omaha diff --git a/omaha/base/atlassert.h b/omaha/base/atlassert.h deleted file mode 100644 index 975955dbe..000000000 --- a/omaha/base/atlassert.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2005-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Take over ATLASSERT -// - -#ifndef OMAHA_BASE_ATLASSERT_H_ -#define OMAHA_BASE_ATLASSERT_H_ - -#include - -#ifdef _DEBUG -#ifndef DEBUG -#error DEBUG and _DEBUG must be in sync -#endif -#endif - -namespace omaha { - -enum ReportType { - R_INFO = 1, // Not an error, used for accumulating statistics. - R_WARNING, // May or may not be an error. - R_ERROR, // Definitely an error. - R_FATAL // halt program == ASSERT for release mode. -}; - -enum DebugReportKind { - DEBUGREPORT_NONE = 0, - DEBUGREPORT_ASSERT = 1, - DEBUGREPORT_REPORT = 2, - DEBUGREPORT_ABORT = 3 -}; - -#ifdef DEBUG -extern "C" bool DebugReport(unsigned int id, - omaha::ReportType type, - const char* expr, - const TCHAR* message, - const char* filename, - int linenumber, - omaha::DebugReportKind debug_report_kind); - #ifndef ATLASSERT - #define ATLASSERT(expr) \ - do { \ - if (!(expr)) { \ - DebugReport(0, \ - omaha::R_FATAL, \ - #expr, \ - _T("ATL assertion"), \ - __FILE__, \ - __LINE__, \ - omaha::DEBUGREPORT_ASSERT); \ - } \ - } while (0) - #endif -#else - #ifndef ATLASSERT - #define ATLASSERT(expr) ((void)0) - #endif -#endif - -} // namespace omaha - -#endif // OMAHA_BASE_ATLASSERT_H_ diff --git a/omaha/base/atlassert_unittest.cc b/omaha/base/atlassert_unittest.cc deleted file mode 100644 index c5cd83f8b..000000000 --- a/omaha/base/atlassert_unittest.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2003-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/base/debug.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -// Test what happens when we hit an ATLASSERT within ATL code. -// The & operator of CComPtr class expects the parameter to be NULL, otherwise -// the object will leak. -TEST(AtlAssertTest, AtlAssert) { - ExpectAsserts expect_asserts; - CComPtr p; - EXPECT_HRESULT_SUCCEEDED(::CoGetMalloc(1, &p)); - EXPECT_TRUE(p != NULL); - ::CoGetMalloc(1, &p); // This line is expected to assert. -} - -} // namespace omaha diff --git a/omaha/base/browser_utils.cc b/omaha/base/browser_utils.cc index 7a377a1ac..2292067be 100644 --- a/omaha/base/browser_utils.cc +++ b/omaha/base/browser_utils.cc @@ -152,7 +152,7 @@ class CloseIeUsingShellRunnable : public Runnable { UTIL_LOG(L3, (_T("[CloseIeUsingShellRunnable][%s]"), sid_)); scoped_co_init co_init(COINIT_MULTITHREADED); - VERIFY1(SUCCEEDED(co_init.hresult())); + VERIFY_SUCCEEDED(co_init.hresult()); CloseIeUsingShell(sid_); delete this; @@ -378,99 +378,6 @@ HRESULT GetDefaultBrowserPath(CString* path) { return GetBrowserImagePath(default_type, path); } -HRESULT GetFirefoxDefaultProfile(CString* name, CString* path) { - ASSERT1(name); - ASSERT1(path); - - const TCHAR kFirefoxAppDataPath[] = _T("\\Mozilla\\Firefox\\"); - const TCHAR kFirefoxProfileIni[] = _T("profiles.ini"); - const TCHAR kFirefoxDefaultProfileSecName[] = _T("Profile0"); - const TCHAR kFirefoxProfileIniNameKey[] = _T("Name"); - const TCHAR kFirefoxProfileIniIsRelativeKey[] = _T("IsRelative"); - const TCHAR kFirefoxProfileIniPathKey[] = _T("Path"); - const TCHAR kFirefoxProfileIniDefaultKey[] = _T("Default"); - - name->Empty(); - path->Empty(); - - // Get appdata path for storing Firefox settings. - CString appdata_path; - RET_IF_FAILED(Shell::GetSpecialFolder(CSIDL_APPDATA, false, &appdata_path)); - appdata_path += kFirefoxAppDataPath; - - // Get profile.ini. - CString profile_ini = appdata_path + kFirefoxProfileIni; - UTIL_LOG(L3, (_T("[FireFox profile.ini][%s]"), profile_ini)); - - if (!File::Exists(profile_ini)) { - UTIL_LOG(LE, (_T("[File does not exist][%s]"), profile_ini)); - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - } - - // Read all section names in profile.ini. - // The buffer is filled with one or more null-terminated strings; the last - // string is followed by a second null character. - const int kMaxProfileSecNamesLength = 2048; - CString profile_sec_names; - DWORD char_returned = ::GetPrivateProfileSectionNames( - profile_sec_names.GetBufferSetLength(kMaxProfileSecNamesLength), - kMaxProfileSecNamesLength, - profile_ini); - if (char_returned == kMaxProfileSecNamesLength - 2) { - UTIL_LOG(LW, (_T("[FireFox profile.ini contains too many sections]"))); - } - profile_sec_names.ReleaseBuffer(char_returned); - - // Iterate through all the sections to find the default profile. - const TCHAR* default_profile_sec = NULL; - const TCHAR* ptr = profile_sec_names.GetString(); - const TCHAR* end = ptr + char_returned; - while (ptr < end && *ptr) { - if (::GetPrivateProfileInt(ptr, - kFirefoxProfileIniDefaultKey, - 0, - profile_ini)) { - default_profile_sec = ptr; - break; - } - - for (; ptr < end && *ptr; ++ptr) { - } - ++ptr; - } - - if (!default_profile_sec) { - default_profile_sec = kFirefoxDefaultProfileSecName; - } - - DWORD name_len = ::GetPrivateProfileString(default_profile_sec, - kFirefoxProfileIniNameKey, _T(""), - name->GetBufferSetLength(256), - 256, - profile_ini); - name->ReleaseBuffer(name_len); - - DWORD path_len = ::GetPrivateProfileString(default_profile_sec, - kFirefoxProfileIniPathKey, - _T(""), - path->GetBufferSetLength(1024), - 1024, - profile_ini); - path->ReleaseBuffer(path_len); - path->Replace(_T('/'), _T('\\')); - - bool is_relative = ::GetPrivateProfileInt(default_profile_sec, - kFirefoxProfileIniIsRelativeKey, - 0, - profile_ini) != 0; - - if (is_relative && !path->IsEmpty()) { - path->Insert(0, appdata_path); - } - - return S_OK; -} - HRESULT GetIEPath(CString* path) { ASSERT1(path); diff --git a/omaha/base/browser_utils.h b/omaha/base/browser_utils.h index 0ddb7d788..14cba504b 100644 --- a/omaha/base/browser_utils.h +++ b/omaha/base/browser_utils.h @@ -53,9 +53,6 @@ HRESULT GetDefaultBrowserType(BrowserType* type); // Get the default browser path HRESULT GetDefaultBrowserPath(CString* path); -// Get the default profile of Firefox -HRESULT GetFirefoxDefaultProfile(CString* name, CString* path); - // Returns the IExplore.exe path. HRESULT GetIEPath(CString* path); diff --git a/omaha/base/browser_utils_unittest.cc b/omaha/base/browser_utils_unittest.cc index de20fdd9c..74fcf84b2 100644 --- a/omaha/base/browser_utils_unittest.cc +++ b/omaha/base/browser_utils_unittest.cc @@ -39,9 +39,7 @@ class BrowserUtilsDefaultBrowserSavedTest : public testing::Test { return; } - EXPECT_SUCCEEDED(RegKey::GetValue(kRegKeyUserDefaultBrowser, - NULL, - &default_browser_name_)); + RegKey::GetValue(kRegKeyUserDefaultBrowser, NULL, &default_browser_name_); } virtual void TearDown() { diff --git a/omaha/base/build.scons b/omaha/base/build.scons index 0cac4c453..978d1e06d 100644 --- a/omaha/base/build.scons +++ b/omaha/base/build.scons @@ -15,7 +15,7 @@ Import('env') -import functools +import fnmatch import glob import operator import os @@ -29,7 +29,6 @@ local_env = env.Clone() inputs = [ 'apply_tag.cc', 'app_util.cc', - 'atl_regexp.cc', 'browser_utils.cc', 'cgi.cc', 'clipboard.cc', @@ -53,7 +52,6 @@ inputs = [ 'file_ver.cc', 'firewall_product_detection.cc', 'highres_timer-win32.cc', - 'localization.cc', 'logging.cc', 'omaha_version.cc', 'path.cc', @@ -63,7 +61,6 @@ inputs = [ 'queue_timer.cc', 'reactor.cc', 'reg_key.cc', - 'regexp.cc', 'registry_monitor_manager.cc', 'safe_format.cc', 'service_utils.cc', @@ -101,159 +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', - '/wd4310', - '/wd4355', - '/wd4388', - '/wd4389', - '/wd4456', - '/wd4506', - '/wd4548', - '/wd4647', - '/wd4701', - '/wd4702', - '/wd4703', - '/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=[ - '$GOOGLE3', 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/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/printer.cc', - 'protobuf/io/strtod.cc', - 'protobuf/io/tokenizer.cc', - 'protobuf/io/strtod.cc', - 'protobuf/io/zero_copy_stream.cc', - 'protobuf/io/zero_copy_stream_impl.cc', - 'protobuf/io/zero_copy_stream_impl_lite.cc', - 'protobuf/arena.cc', - 'protobuf/map_field.cc', - 'protobuf/message.cc', - 'protobuf/message_lite.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') @@ -268,7 +198,6 @@ proto_sources = [ cc_files = local_env.CompileProtoBuf(proto_sources) local_env.Append( CPPPATH=[ - '$GOOGLE3', protobuf_src_dir, '$TARGET_ROOT/proto_files/', ], @@ -278,11 +207,12 @@ cc_files += [ '../third_party/chrome/files/src/components/crx_file/crx_verifier.cc', '../third_party/chrome/files/src/components/crx_file/id_util.cc', '../third_party/chrome/files/src/crypto/signature_verifier.cc', + '../third_party/chrome/files/src/crypto/signature_verifier_win.cc', '../third_party/chrome/files/src/crypto/secure_util.cc', ] """ Add zlib library files.""" -zlib_src_path = '$THIRD_PARTY/zlib/v1_2_11/' +zlib_src_path = '$GOOGLE3/third_party/zlib/' zlib_src_path_dir = local_env.Dir(zlib_src_path) local_env.Dir('zlib').addRepository(zlib_src_path_dir) cc_files += ['zlib/' + @@ -290,20 +220,21 @@ cc_files += ['zlib/' + glob.glob(zlib_src_path_dir.path + os.sep + '*.c')] """ Add libzip library files.""" - -libzip_src_path = '$THIRD_PARTY/libzip/lib/' +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) -libzip_gladman_src_path = '$THIRD_PARTY/libzip/lib/gladman-fcrypt' +libzip_gladman_src_path = '$GOOGLE3/third_party/libzip/lib/gladman-fcrypt' local_env.Append( CPPDEFINES=[ 'HAVE_CONFIG_H', + 'ZLIB_COMPAT', # we do not need zlib ng, classic zlib is good enough. ], CPPPATH=[ 'libzip/', libzip_gladman_src_path, libzip_src_path, + libzip_src_path + '..', zlib_src_path, ], CCFLAGS=[ @@ -313,6 +244,7 @@ local_env.Append( '/wd4047', '/wd4131', '/wd4132', + '/wd4232', '/wd4242', '/wd4244', '/wd4245', @@ -324,18 +256,29 @@ local_env.Append( '/wd4389', '/wd4647', '/wd4548', + '/FIstdint.h' # zlib/src/fallback_builtins.h does not include stdint.h, + # which is required for the definition of `uint32_t`. So + # we force-include here. ], ) cc_files_exclusions = [ '*unix*.c', + 'zip_algorithm_xz.c', + 'zip_algorithm_zstd.c', + 'zip_crypto_win.c', 'zip_crypto_[cgmo]*.c', + 'zip_mkstempm.c', 'zip_source_file.c', + 'zip_source_file_stdio.c', + 'zip_source_file_stdio_named.c', 'zip_random_uwp.c', 'zip_winzip_aes.c', '*bzip2.c', 'zip_source_winzip_aes_*.c' ] + +cc_files += ['libzip/zip_err_str.c'] cc_files += ['libzip/' + os.path.basename(x) for x in list( set(glob.glob(os.sep.join([libzip_src_path_dir.path, '*.c']))) - set(reduce(operator.concat, map (lambda x : glob.glob( diff --git a/omaha/base/const_addresses.h b/omaha/base/const_addresses.h index f9facec76..afa79c2cc 100644 --- a/omaha/base/const_addresses.h +++ b/omaha/base/const_addresses.h @@ -83,6 +83,10 @@ const TCHAR* const kUrlCodeRedCheck = const TCHAR* const kUrlUsageStatsReport = _T("https://clients5.") COMPANY_DOMAIN _T("/tbproxy/usagestats"); +// App logo. +const TCHAR* const kUrlAppLogo = + _T("https://dl.") COMPANY_DOMAIN _T("/update2/installers/icons/"); + #if defined(HAS_DEVICE_MANAGEMENT) // Device Management API url. diff --git a/omaha/base/const_code_signing.h b/omaha/base/const_code_signing.h index cff2aab04..d202386ce 100644 --- a/omaha/base/const_code_signing.h +++ b/omaha/base/const_code_signing.h @@ -29,65 +29,85 @@ namespace omaha { // The company and organization names expected in the code // signing certificates which are trusted. -const TCHAR* const kCertificateSubjectName = _T("Google Inc"); +const TCHAR* const kLegacyCertificateSubjectName = _T("Google Inc"); +const TCHAR* const kSha1CertificateSubjectName = _T("Google LLC"); const TCHAR* const kSha256CertificateSubjectName = _T("Google LLC"); // The Omaha certificate thumbprint. Used by unit tests. const TCHAR* const kCertificateThumbprint = - _T("1a6ac0549a4a44264deb6ff003391da2f285b19f"); + _T("a3958ae522f3c54b878b20d7b0f63711e08666b2"); const TCHAR* const kSha256CertificateThumbprint = - _T("cb7e84887f3c6015fe7edfb4f8f36df7dc10590e"); + _T("2673ea6cc23beffda49ac715b121544098a1284c"); // The SHA256 hash of the Omaha certificate RSA public key. const TCHAR* const kCertificatePublicKeyHash = - _T("d49de35a2e9fdbed09e2b9a6c1243df414d6aac13690ab221b0017a5cbe1351f"); + _T("6cb128676c6d0b49d3e8918bd835888694333da7540a0994261c0ec0b3516f9d"); const TCHAR* const kSha256CertificatePublicKeyHash = - _T("03e27c19d222043a8f0c64181c23c9339cc84a7ec4ebff8a19adb7caefb0c709"); + _T("3e9d92dfb3a046d49f53bab836f387177ac1ec075e8e3dd306b7c1764432f276"); // The hash of public keys that we pin the code signing certificates to. // For quick identification, the date and thumbprint of the certificates are // provide below. The hash is the SHA256 hash of the raw certificate RSA public // key bytes in DER format. const TCHAR* const kPublicKeyHashes[] = { -// Omaha certificate: (11/9/2011 to 11/9/2014). -// thumbprint=8aed552a1387870a53f5f8aee17a3761232a4609 -_T("64637c145ee0b7888af408ec24f714242fc4da6b8ad7a04803254bf93f7d295f"), - -// Chrome certificate: (11/13/2011 to 11/13/2014) revoked on 1/28/2014. -// thumbprint=06c92bec3bbf32068cb9208563d004169448ee21 -// serial=09E28B26DB593EC4E73286B66499C370 -// SHA1 Fingerprint=06:C9:2B:EC:3B:BF:32:06:8C:B9:20:85:63:D0:04:16:94:48:EE:21 -_T("c7b4d0bf956f7ebbbc7369786f111ee6caa225af173be135e1de9e5a1d11951a"), - -// Omaha and Chrome certificate: sha1 (01/28/2014 to 01/29/2016). -// thumbprint=fcac7e666cc54341ca213becf2eb463f2b62adb0 -// serial=2912C70C9A2B8A3EF6F6074662D68B8D -// SHA1 Fingerprint=FC:AC:7E:66:6C:C5:43:41:CA:21:3B:EC:F2:EB:46:3F:2B:62:AD:B0 -_T("4365c47f17727f2da65892b1f34c0cf418b0138b519b6864dd17300f21aa3144"), - -// Omaha and Chrome certificate: sha1 (12/13/2015 to 12/14/2016). -// thumbprint=264e38570f882e5a0272423757741233a661b553 -// serial=4c40dba5f988fae57a57d6457495f98b -// SHA1 Fingerprint=26:4E:38:57:0F:88:2E:5A:02:72:42:37:57:74:12:33:A6:61:B5:53 -_T("309bae1b466c4235e1daea9fe0e373b3415807ac667202f704d030ef33b519d6"), - -// Omaha and Chrome certificate: sha256 (12/15/2015 to 12/16/2018). -// thumbprint=5a9272ce76a9415a4a3a5002a2589a049312aa40 -// serial=2a9c21acaaa63a3c58a7b9322bee948d -// SHA1 Fingerprint=5A:92:72:CE:76:A9:41:5A:4A:3A:50:02:A2:58:9A:04:93:12:AA:40 -_T("cd623b2bf2c06940bd480b6bcf4a5c9e1cbe94626fbfa127d001bf19ae5ba9fe"), - -// Omaha and Chrome certificate: sha1 (11/28/2016 to 11/21/2019). -// thumbprint=1a6ac0549a4a44264deb6ff003391da2f285b19f -// serial=14F8FDD167F92402B1570B5DC495C815 -// SHA1 Fingerprint=1A:6A:C0:54:9A:4A:44:26:4D:EB:6F:F0:03:39:1D:A2:F2:85:B1:9F -kCertificatePublicKeyHash, - -// Omaha and Chrome certificate: sha256 (11/06/2018 to 11/17/2021). -// thumbprint=cb7e84887f3c6015fe7edfb4f8f36df7dc10590e -// serial=0c15be4a15bb0903c901b1d6c265302f -// SHA1 Fingerprint=CB:7E:84:88:7F:3C:60:15:FE:7E:DF:B4:F8:F3:6D:F7:DC:10:59:0E -kSha256CertificatePublicKeyHash, + // Omaha certificate: (11/9/2011 to 11/9/2014). + // thumbprint=8aed552a1387870a53f5f8aee17a3761232a4609 + _T("64637c145ee0b7888af408ec24f714242fc4da6b8ad7a04803254bf93f7d295f"), + + // Chrome certificate: (11/13/2011 to 11/13/2014) revoked on 1/28/2014. + // thumbprint=06c92bec3bbf32068cb9208563d004169448ee21 + // serial=09E28B26DB593EC4E73286B66499C370 + // SHA1 + // Fingerprint=06:C9:2B:EC:3B:BF:32:06:8C:B9:20:85:63:D0:04:16:94:48:EE:21 + _T("c7b4d0bf956f7ebbbc7369786f111ee6caa225af173be135e1de9e5a1d11951a"), + + // Omaha and Chrome certificate: sha1 (01/28/2014 to 01/29/2016). + // thumbprint=fcac7e666cc54341ca213becf2eb463f2b62adb0 + // serial=2912C70C9A2B8A3EF6F6074662D68B8D + // SHA1 + // Fingerprint=FC:AC:7E:66:6C:C5:43:41:CA:21:3B:EC:F2:EB:46:3F:2B:62:AD:B0 + _T("4365c47f17727f2da65892b1f34c0cf418b0138b519b6864dd17300f21aa3144"), + + // Omaha and Chrome certificate: sha1 (12/13/2015 to 12/14/2016). + // thumbprint=264e38570f882e5a0272423757741233a661b553 + // serial=4c40dba5f988fae57a57d6457495f98b + // SHA1 + // Fingerprint=26:4E:38:57:0F:88:2E:5A:02:72:42:37:57:74:12:33:A6:61:B5:53 + _T("309bae1b466c4235e1daea9fe0e373b3415807ac667202f704d030ef33b519d6"), + + // Omaha and Chrome certificate: sha256 (12/15/2015 to 12/16/2018). + // thumbprint=5a9272ce76a9415a4a3a5002a2589a049312aa40 + // serial=2a9c21acaaa63a3c58a7b9322bee948d + // SHA1 + // Fingerprint=5A:92:72:CE:76:A9:41:5A:4A:3A:50:02:A2:58:9A:04:93:12:AA:40 + _T("cd623b2bf2c06940bd480b6bcf4a5c9e1cbe94626fbfa127d001bf19ae5ba9fe"), + + // Omaha and Chrome certificate: sha1 (11/28/2016 to 11/21/2019). + // thumbprint=1a6ac0549a4a44264deb6ff003391da2f285b19f + // serial=14F8FDD167F92402B1570B5DC495C815 + // SHA1 + // Fingerprint=1A:6A:C0:54:9A:4A:44:26:4D:EB:6F:F0:03:39:1D:A2:F2:85:B1:9F + _T("d49de35a2e9fdbed09e2b9a6c1243df414d6aac13690ab221b0017a5cbe1351f"), + + // Omaha certificate: sha1 (11/07/2019 to 11/16/2022). + // thumbprint=a3958ae522f3c54b878b20d7b0f63711e08666b2 + // serial=06aea76bac46a9e8cfe6d29e45aaf033 + // SHA1 + // Fingerprint=A3:95:8A:E5:22:F3:C5:4B:87:8B:20:D7:B0:F6:37:11:E0:86:66:B2 + kCertificatePublicKeyHash, + + // Omaha and Chrome certificate: sha256 (11/06/2018 to 11/17/2021). + // thumbprint=cb7e84887f3c6015fe7edfb4f8f36df7dc10590e + // serial=0c15be4a15bb0903c901b1d6c265302f + // SHA1 + // Fingerprint=CB:7E:84:88:7F:3C:60:15:FE:7E:DF:B4:F8:F3:6D:F7:DC:10:59:0E + _T("03e27c19d222043a8f0c64181c23c9339cc84a7ec4ebff8a19adb7caefb0c709"), + + // Google LLC sha256 certificate valid from 07-01-2021 to 07-10-2024. + // Issued by DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1. + // thumbprint=2673ea6cc23beffda49ac715b121544098a1284c. + // serial=0e4418e2dede36dd2974c3443afb5ce5. + kSha256CertificatePublicKeyHash, }; } // namespace omaha diff --git a/omaha/base/const_config.h b/omaha/base/const_config.h index 13bb02f4f..8b214c58e 100644 --- a/omaha/base/const_config.h +++ b/omaha/base/const_config.h @@ -25,24 +25,6 @@ namespace omaha { #define kCiRegKeyShared GOOPDATE_MAIN_KEY kRegKeyShared #define kRegValueReportIds _T("report_ids") -// TODO(omaha): Move these plugin values someplace else. Since we're building -// constants, that should probably be the customization header. Move the Omaha 3 -// plugin equivalents from config.cc there as well. - -// NOTE: ONECLICK_PLUGIN_VERSION_ANSI is defined in main.scons -// For example: kOneClickProgId == "Google.OneClickCtrl.1" -const TCHAR* const kOneClickProgId = COMPANY_NAME_IDENTIFIER - _T(".OneClickCtrl.") - _T(ONECLICK_PLUGIN_VERSION_ANSI); -// The plug-in MIME type. -// For example: -// kOneClickPluginMimeTypeAnsi == "application/x-vnd.google.oneclickctrl.1" -// TODO(omaha): Deal with the "Google.OneClickCtrl.%d") in -// tools\goopdump\data_dumper_oneclick.cc after integrating goopdump. -#define kOneClickPluginMimeTypeAnsi \ - "application/x-vnd." COMPANY_DOMAIN_BASE_ANSI ".oneclickctrl." \ - ONECLICK_PLUGIN_VERSION_ANSI - } // namespace omaha #endif // OMAHA_BASE_CONST_CONFIG_H_ diff --git a/omaha/base/const_object_names.h b/omaha/base/const_object_names.h index 9ff1d92ca..e49ece44d 100644 --- a/omaha/base/const_object_names.h +++ b/omaha/base/const_object_names.h @@ -24,12 +24,12 @@ namespace omaha { // The prefix to use for global names in the win32 API's. -const TCHAR* const kGlobalPrefix = _T("Global\\Omaha"); +const TCHAR* const kGlobalPrefix = _T("Global\\") SHORT_COMPANY_NAME; const TCHAR* const kObjectName64Suffix = _T("-x64"); const TCHAR* const kCrashPipeNamePrefix = - _T("\\\\.\\pipe\\") SHORT_COMPANY_NAME _T("CrashServices"); + _T("\\\\.\\pipe\\") PATH_COMPANY_NAME _T("CrashServices"); // Ensures that only one instance of machine or user Omaha is trying to setup at // a time. diff --git a/omaha/base/constants.h b/omaha/base/constants.h index 577469e9f..bd86f34b2 100644 --- a/omaha/base/constants.h +++ b/omaha/base/constants.h @@ -46,14 +46,21 @@ namespace omaha { // Full company name. // FULL_COMPANY_NAME == "Google LLC" -const TCHAR* const kFullCompanyName = _T(FULL_COMPANY_NAME_ANSI); +#define FULL_COMPANY_NAME _T(FULL_COMPANY_NAME_ANSI) +const TCHAR* const kFullCompanyName = FULL_COMPANY_NAME; -// Short company name (for use in paths and messages and to combine with product -// name). Does not include "Inc." and similar formal parts of the company name. +// Short company name (for use in messages and to combine with product name). +// Does not include "Inc." and similar formal parts of the company name. +// If the company name can be abbreviated, it will be here. // SHORT_COMPANY_NAME == "Google" #define SHORT_COMPANY_NAME _T(SHORT_COMPANY_NAME_ANSI) const TCHAR* const kShortCompanyName = SHORT_COMPANY_NAME; +// The company name to use in file and registry paths. +// PATH_COMPANY_NAME == "Google" +#define PATH_COMPANY_NAME _T(PATH_COMPANY_NAME_ANSI) +const TCHAR* const kPathCompanyName = PATH_COMPANY_NAME; + // Product name. // PRODUCT_NAME == "Update" #define PRODUCT_NAME _T(PRODUCT_NAME_ANSI) @@ -113,8 +120,6 @@ const TCHAR* const kOmahaBrokerFileName = MAIN_EXE_BASE_NAME _T("Broker.exe"); const TCHAR* const kOmahaOnDemandFileName = MAIN_EXE_BASE_NAME _T("OnDemand.exe"); -const TCHAR* const kOmahaWebPluginFileName = - MAIN_EXE_BASE_NAME _T("WebPlugin.exe"); const TCHAR* const kCrashHandlerFileName = CRASH_HANDLER_NAME _T(".exe"); const TCHAR* const kCrashHandler64FileName = CRASH_HANDLER_NAME _T("64.exe"); const TCHAR* const kOmahaMetainstallerFileName = @@ -130,15 +135,8 @@ const TCHAR* const kPSFileNameUser64 = _T("psuser_64.dll"); // TODO(omaha): Replace the following literal in clickonce\build.scons. // '%s/GoogleUpdateSetup.exe' -// These must be in sync with the WiX files. -// TODO(omaha): Make these constants in main.scons and use them in the .wxs -// files, kMsiUninstallKey, and elsewhere this GUID appears. -const TCHAR* const kHelperInstallerName = MAIN_EXE_BASE_NAME _T("Helper.msi"); -const TCHAR* const kHelperInstallerProductGuid = +const TCHAR* const kLegacyHelperInstallerGuid = _T("{A92DAB39-4E2C-4304-9AB6-BC44E68B55E2}"); -const TCHAR* const kHelperPatchName = MAIN_EXE_BASE_NAME _T("HelperPatch.msp"); -const TCHAR* const kHelperPatchGuid = - _T("{E0D0D2C9-5836-4023-AB1D-54EC3B90AD03}"); // The value that is used in the run key. const TCHAR* const kRunValueName = kAppName; @@ -183,9 +181,10 @@ const TCHAR* const kChromeAppId = CHROME_APP_ID; #define INSTALL_WORKING_DIR_NAME _T("Install") // Directories relative to \Google -#define OMAHA_REL_COMPANY_DIR SHORT_COMPANY_NAME +#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 \ @@ -210,14 +209,14 @@ const TCHAR* const kChromeAppId = CHROME_APP_ID; #define USER_KEY_NAME _T("HKCU") #define USER_KEY USER_KEY_NAME _T("\\") #define USERS_KEY _T("HKU\\") -#define COMPANY_MAIN_KEY _T("Software\\") SHORT_COMPANY_NAME _T("\\") +#define COMPANY_MAIN_KEY _T("Software\\") PATH_COMPANY_NAME _T("\\") #define GOOPDATE_MAIN_KEY COMPANY_MAIN_KEY PRODUCT_NAME _T("\\") #define GOOPDATE_REG_RELATIVE_CLIENTS GOOPDATE_MAIN_KEY _T("Clients\\") #define GOOPDATE_REG_RELATIVE_CLIENT_STATE GOOPDATE_MAIN_KEY _T("ClientState\\") #define GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM \ GOOPDATE_MAIN_KEY _T("ClientStateMedium\\") #define COMPANY_POLICIES_MAIN_KEY \ - _T("Software\\Policies\\") SHORT_COMPANY_NAME _T("\\") + _T("Software\\Policies\\") PATH_COMPANY_NAME _T("\\") #define GOOPDATE_POLICIES_RELATIVE COMPANY_POLICIES_MAIN_KEY \ PRODUCT_NAME _T("\\") #define CLOUD_MANAGEMENT_POLICIES_RELATIVE COMPANY_POLICIES_MAIN_KEY \ @@ -248,15 +247,6 @@ const TCHAR* const kChromeAppId = CHROME_APP_ID; // Expands to HKEY_LOCAL_MACHINE\SOFTWARE\Google\UpdateDev #define MACHINE_REG_UPDATE_DEV MACHINE_KEY REG_UPDATE_DEV -// Regular expressions for the servers allowed to use the Omaha plugins. -const TCHAR* const kSiteLockPatternStrings[] = { - _T("^(gears)|(mail)|(tools)|(www)|(desktop)|(pack)|(chrome)|(drive)\\.google\\.com$"), // NOLINT - _T("^www\\.google\\.(ad)|(bg)|(ca)|(cn)|(cz)|(de)|(es)|(fi)|(fr)|(gr)|(hr)|(hu)|(it)|(ki)|(kr)|(lt)|(lv)|(nl)|(no)|(pl)|(pt)|(ro)|(ru)|(sk)|(sg)|(sl)|(sr)|(vn)$"), // NOLINT - _T("^www\\.google\\.co\\.(hu)|(id)|(il)|(it)|(jp)|(kr)|(th)|(uk)$"), - _T("^www\\.google\\.com\\.(ar)|(au)|(br)|(cn)|(et)|(gr)|(hr)|(ki)|(lv)|(om)|(pl)|(pt)|(ru)|(sg)|(sv)|(tr)|(vn)$"), // NOLINT - _T("^(www\\.)?chrome\\.com$"), -}; - // // Minimum compatible shell version. // Shell versions equal to or newer than the following version are compatible @@ -292,6 +282,7 @@ const TCHAR* const kRegValueNamePingUrl = _T("PingUrl"); const TCHAR* const kRegValueNameCrashReportUrl = _T("CrashReportUrl"); const TCHAR* const kRegValueNameGetMoreInfoUrl = _T("MoreInfoUrl"); const TCHAR* const kRegValueNameUsageStatsReportUrl = _T("UsageStatsReportUrl"); +const TCHAR* const kRegValueNameAppLogoUrl = _T("AppLogoUrl"); const TCHAR* const kRegValueTestSource = _T("TestSource"); const TCHAR* const kRegValueAuCheckPeriodMs = _T("AuCheckPeriodMs"); const TCHAR* const kRegValueCrCheckPeriodMs = _T("CrCheckPeriodMs"); @@ -300,6 +291,17 @@ const TCHAR* const kRegValueProxyHost = _T("ProxyHost"); const TCHAR* const kRegValueProxyPort = _T("ProxyPort"); const TCHAR* const kRegValueMID = _T("mid"); +const TCHAR* const kRegValueDisablePayloadAuthenticodeVerification = + _T("DisablePayloadAuthenticodeVerification"); + +// File extensions that can be verified with an Authenticode signature. +const TCHAR* const kAuthenticodeVerifiableExtensions[] = { + _T("exe"), _T("msi"), _T("dll"), _T("sys"), _T("cab"), _T("ocx"), + _T("xpi"), _T("xap"), _T("cat"), _T("jar"), _T("ps1"), _T("psm1"), + _T("psd1"), _T("ps1xml"), _T("psc1"), _T("acm "), _T("ax"), _T("cpl"), + _T("drv"), _T("efi"), _T("mui"), _T("scr"), _T("sys"), _T("tsp") +}; + #if defined(HAS_DEVICE_MANAGEMENT) const TCHAR* const kRegValueNameDeviceManagementUrl = _T("DeviceManagementUrl"); #endif @@ -307,6 +309,9 @@ const TCHAR* const kRegValueNameDeviceManagementUrl = _T("DeviceManagementUrl"); // The values below can be overriden in unofficial builds. const TCHAR* const kRegValueNameWindowsInstalling = _T("WindowsInstalling"); +// Allows Omaha to log to %ALLUSERSPROFILE%\Google\Update\Log\GoogleUpdate.log. +const TCHAR* const kRegValueIsEnabledLogToFile = _T("IsEnabledLogToFile"); + // Allows Omaha to log events in the Windows Event Log. This is // a DWORD value 0: Log nothing, 1: Log warnings and errors, 2: Log everything. const TCHAR* const kRegValueEventLogLevel = _T("LogEventLevel"); @@ -323,13 +328,6 @@ const TCHAR* const kRegValueLastCheckPeriodSec = _T("LastCheckPeriodSec"); // Uses the production or the test cup keys. const TCHAR* const kRegValueCupKeys = _T("TestKeys"); -// Allow a custom host pattern to be specified. For example, -// "^https?://some_test_server\.google\.com/". For other examples, see -// kSiteLockPatternStrings. The detailed regular expression syntax is documented -// in the MSDN documentation for the CAtlRegExp class: -// http://msdn.microsoft.com/en-us/library/k3zs4axe.aspx. -const TCHAR* const kRegValueOneClickHostPattern = _T("OneClickHostPattern"); - // Disables executable verification for application commands. const TCHAR* const kRegValueSkipCommandVerification = _T("NoAppCommandVerification"); @@ -415,13 +413,6 @@ const TCHAR* const kDefaultCountryCode = _T("us"); // the max length of the extra info we can store inside the install stubs. const int kExtraMaxLength = 64 * 1024; // 64 KB -#if defined(HAS_DEVICE_MANAGEMENT) - -// The maximum length of an enrollment token. -const int kEnrollmentTokenMaxLength = 1024; - -#endif // defined(HAS_DEVICE_MANAGEMENT) - // Default brand code value when one is not specified. // This has been specifically assigned to Omaha. const TCHAR* const kDefaultGoogleUpdateBrandCode = _T("GGLS"); diff --git a/omaha/base/debug.cc b/omaha/base/debug.cc index 861ac0fa1..f0eab1afc 100644 --- a/omaha/base/debug.cc +++ b/omaha/base/debug.cc @@ -267,22 +267,19 @@ TCHAR *ReportSummaryGenerator::GetReportSummary() { if (s) { s[0] = 0; if (g_total_reports) { - SafeStrCat(s, L"REPORT SUMMARY:\r\n\r\n", kMaxReportSummaryLen); - SafeStrCat(s, - SPRINTF(L"%d total reports\r\n\r\n", g_total_reports), - kMaxReportSummaryLen); - SafeStrCat(s, + wcscat_s(s, kMaxReportSummaryLen, L"REPORT SUMMARY:\r\n\r\n"); + wcscat_s(s, kMaxReportSummaryLen, + SPRINTF(L"%d total reports\r\n\r\n", g_total_reports)); + wcscat_s(s, kMaxReportSummaryLen, g_report_summary. - Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(), - kMaxReportSummaryLen); + Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString()); CString report_string = g_report_ids.DebugReportString(); ReplaceCString(report_string, L"&", L"\r\n"); - SafeStrCat(s, + wcscat_s(s, kMaxReportSummaryLen, report_string. - Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(), - kMaxReportSummaryLen); + Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString()); } else { - SafeStrCat(s, L"NO REPORTS!!\r\n", kMaxReportSummaryLen); + wcscat_s(s, kMaxReportSummaryLen, L"NO REPORTS!!\r\n"); } } @@ -587,7 +584,7 @@ bool DebugReport(unsigned int id, arraysize(clipboard_string)); stack_trace = stack_trace.Left( arraysize(clipboard_string) - lstrlen(clipboard_string) - 1); - SafeStrCat(clipboard_string, stack_trace, arraysize(clipboard_string)); + wcscat_s(clipboard_string, arraysize(clipboard_string), stack_trace); SetClipboard(clipboard_string); stack_trace = stack_trace.Left(kMaxStackTraceDialogLen); @@ -665,7 +662,7 @@ ReportIds::~ReportIds() { } } -const TCHAR* const GetRegKeyShared() { +const TCHAR* GetRegKeyShared() { return vista_util::IsUserAdmin() ? _T("HKLM\\") kCiRegKeyShared : _T("HKCU\\") kCiRegKeyShared; } @@ -927,9 +924,9 @@ TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) { va_end(argptr); // copy to fixed return buffers - SafeStrCat(sprintf_buf, - out.GetBufferSetLength(kSprintfMaxLen), - g_current_sprintf_buffer); + wcscat_s(sprintf_buf, + g_current_sprintf_buffer, + out.GetBufferSetLength(kSprintfMaxLen)); sprintf_buf[kSprintfMaxLen] = '\0'; return sprintf_buf; @@ -1000,10 +997,9 @@ bool ReleaseAssert(const char *expr, filename, linenumber, VER_TIMESTAMP_STR_FILE); - SafeStrCat(error_string, msg, arraysize(error_string)); - SafeStrCat(error_string, - L"\r\n\r\n*** This message has been copied to the clipboard. ***", - arraysize(error_string)); + wcscat_s(error_string, arraysize(error_string), msg); + wcscat_s(error_string, arraysize(error_string), + L"\r\n\r\n*** This message has been copied to the clipboard. ***"); SetClipboard(error_string); TCHAR title_string[1024]; @@ -1049,10 +1045,9 @@ void ReleaseAbort(const TCHAR *msg, filename, linenumber, omaha::GetVersionString()); - SafeStrCat(error_string, msg, arraysize(error_string)); - SafeStrCat(error_string, - L"\r\n\r\n*** This message has been copied to the clipboard. ***", - arraysize(error_string)); + wcscat_s(error_string, arraysize(error_string), msg); + wcscat_s(error_string, arraysize(error_string), + L"\r\n\r\n*** This message has been copied to the clipboard. ***"); SetClipboard(error_string); TCHAR title_string[1024]; @@ -1073,70 +1068,6 @@ void ReleaseAbort(const TCHAR *msg, #endif -#ifdef _DEBUG - -void DumpInterface(IUnknown* unknown) { - if (!unknown) - return; - - OutputDebugString(_T("------------------------------------------------\r\n")); - - // Open the HKCR\Interfaces key where the IIDs of marshalable interfaces - // are stored. - RegKey key; - if (SUCCEEDED(key.Open(HKEY_CLASSES_ROOT, _T("Interface"), KEY_READ))) { - TCHAR name[_MAX_PATH + 1] = {0}; - DWORD name_size = _MAX_PATH; - DWORD index = 0; - FILETIME last_written; - - // - // Enumerate through the IIDs and see if the object supports it - // by calling QueryInterface. - // - while (::RegEnumKeyEx(key.Key(), - index++, - name, - &name_size, - NULL, - NULL, - NULL, - &last_written) == ERROR_SUCCESS) { - // Convert the string to an IID - IID iid; - HRESULT hr = StringToGuidSafe(name, &iid); - - CComPtr test; - if (unknown->QueryInterface(iid, - reinterpret_cast(&test)) == S_OK) { - // - // The object supports this interface. - // See if we can get a human readable name for the interface - // If not, the name buffer already contains the string - // representation of the IID, which we'll use as a fallback. - // - RegKey sub_key; - if (sub_key.Open(key.Key(), name, KEY_READ) == S_OK) { - std::unique_ptr display; - // If this fails, we should still have the IID - if (sub_key.GetValue(NULL, &display) == S_OK) - lstrcpyn(name, display.get(), _MAX_PATH); - } - - CString fmt; - SafeCStringFormat(&fmt, _T(" %s\r\n"), name); - OutputDebugString(fmt); - } - - ZeroMemory(name, arraysize(name)); - name_size = _MAX_PATH; - } - } - - OutputDebugString(_T("------------------------------------------------\r\n")); -} -#endif - // TODO(omaha): the implementation below is using CStrings so it is not very // conservative in terms of memory allocations. int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *, diff --git a/omaha/base/debug.h b/omaha/base/debug.h index 3b5b5e3db..44e76387b 100644 --- a/omaha/base/debug.h +++ b/omaha/base/debug.h @@ -23,10 +23,13 @@ // // #define ASSERT_IN_RELEASE -#include "omaha/base/atlassert.h" #include "omaha/base/synchronized.h" #include "omaha/base/time.h" +#define CONCATENATE_DIRECT(s1, s2) s1##s2 +#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2) +#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__) + namespace omaha { // hash table for counts of the number of times REPORTs occur @@ -44,6 +47,20 @@ extern bool g_always_assert; const int kMaxStackTraceDialogLen = 512; // too long and dialog box fails +enum ReportType { + R_INFO = 1, // Not an error, used for accumulating statistics. + R_WARNING, // May or may not be an error. + R_ERROR, // Definitely an error. + R_FATAL // halt program == ASSERT for release mode. +}; + +enum DebugReportKind { + DEBUGREPORT_NONE = 0, + DEBUGREPORT_ASSERT = 1, + DEBUGREPORT_REPORT = 2, + DEBUGREPORT_ABORT = 3 +}; + // TODO(omaha): consider merging this into DebugObserver. // // For automated testing, we don't (always) want asserts to fire. So @@ -199,12 +216,13 @@ class ReportIds : public GLock { void TraceError(DWORD error); inline void TraceLastError() { TraceError(GetLastError()); } - /** - * Iterates through HKEY_CLASSES_ROOT\Interface and calls QI for - * all the interfaces there. Useful for finding out what type of - * object you're dealing with :-) - */ - void DumpInterface(IUnknown* unknown); + #define VERIFY_SUCCEEDED(hr) \ + do { \ + const auto ANONYMOUS_VARIABLE(__hr) = (hr); \ + if (FAILED(ANONYMOUS_VARIABLE(__hr))) { \ + VERIFY(false, (L"FAILED(hr): %#08x", ANONYMOUS_VARIABLE(__hr))); \ + } \ + } while (0) #else // #ifdef _DEBUG @@ -224,6 +242,8 @@ class ReportIds : public GLock { do { \ (expr); \ } while (0) + #define VERIFY_SUCCEEDED(hr) do { (hr); } while(0) + #define REPORT(expr, type, msg, id) \ ((expr) ? 0 : g_report_ids.ReleaseReport(id)) void ReleaseAbort(const TCHAR* msg, diff --git a/omaha/base/disk.cc b/omaha/base/disk.cc index 20a5c0e74..d1a11a251 100644 --- a/omaha/base/disk.cc +++ b/omaha/base/disk.cc @@ -23,7 +23,6 @@ #include "omaha/base/const_config.h" #include "omaha/base/error.h" #include "omaha/base/file.h" -#include "omaha/base/localization.h" #include "omaha/base/logging.h" #include "omaha/base/safe_format.h" #include "omaha/base/shell.h" diff --git a/omaha/base/error.h b/omaha/base/error.h index 71c7b783c..02fe334cd 100644 --- a/omaha/base/error.h +++ b/omaha/base/error.h @@ -36,6 +36,7 @@ namespace omaha { // #define EXCEPTION_IMPERSONATION_FAILED 0x1 #define EXCEPTION_REVERT_IMPERSONATION_FAILED 0x2 +#define EXCEPTION_FAILED_TO_GET_THREAD_TOKEN 0x3 // // HRESULT Functions. @@ -96,6 +97,11 @@ const ULONG kFacilityOmaha = 67; #define CI_E_BITS_DISABLED \ MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0030) +// CI_E_BITS_REQUEUED is returned by a BITS request if a job is moved back from +// transmitting state to queued state due to a user session switch or logoff. +#define CI_E_BITS_REQUEUED \ + MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0031) + // CI_E_HTTPS_CERT_FAILURE is returned when the https connection fails. // One cause of this is when the system clock is off by a significant // amount which makes the server certificate appear invalid. @@ -123,6 +129,8 @@ const ULONG kFacilityOmaha = 67; MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0203) #define SIGS_E_INVALID_SIGNATURE \ MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0204) +#define SIGS_E_FILE_SIZE_TOO_BIG \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0205) // Crypto error codes. // Obsolete: GOOPDATE_E_CRYPT_CANT_CREATE_KEY 0x210 @@ -192,6 +200,9 @@ const ULONG kFacilityOmaha = 67; #define GOOPDATEDOWNLOAD_E_CACHING_FAILED \ MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x50D) +#define GOOPDATEDOWNLOAD_E_AUTHENTICODE_VERIFICATION_FAILED \ + MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x50E) + #define GOOPDATEDOWNLOAD_E_FAILED_MOVE \ MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x5FF) @@ -449,11 +460,12 @@ const ULONG kFacilityOmaha = 67; #define GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER_MATCHED \ MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xC01) -// OneClick custom error codes -#define GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED \ - MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD01) -#define GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED \ - MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD02) +// OneClick custom error codes. +// Obsolete. +// #define GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED \ +// MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD01) +// #define GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED \ +// MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD02) // Usage stats / metrics error codes #define GOOPDATE_E_METRICS_LOCK_INIT_FAILED \ diff --git a/omaha/base/event_trace_provider_unittest.cc b/omaha/base/event_trace_provider_unittest.cc index a99cf21c2..a571bce32 100644 --- a/omaha/base/event_trace_provider_unittest.cc +++ b/omaha/base/event_trace_provider_unittest.cc @@ -48,10 +48,10 @@ TEST(EtwTraceProviderTest, ToleratesPreCreateInvocations) { provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo"); provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo"); - EtwMofEvent<1> dummy(kTestEventClass, 0, TRACE_LEVEL_FATAL); + EtwMofEvent<1> etw_mof_event(kTestEventClass, 0, TRACE_LEVEL_FATAL); DWORD data = 0; - dummy.SetField(0, sizeof(data), &data); - provider.Log(dummy.get()); + etw_mof_event.SetField(0, sizeof(data), &data); + provider.Log(etw_mof_event.get()); // Placement-new the provider into our buffer. new (buf) EtwTraceProvider(kTestProvider); // NOLINT @@ -73,7 +73,7 @@ TEST(EtwTraceProviderTest, ToleratesPreCreateInvocations) { // We expect these not to crash. provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo"); provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo"); - provider.Log(dummy.get()); + provider.Log(etw_mof_event.get()); } TEST(EtwTraceProviderTest, Initialize) { diff --git a/omaha/base/extractor.h b/omaha/base/extractor.h index cd6c699bb..dd31a081b 100644 --- a/omaha/base/extractor.h +++ b/omaha/base/extractor.h @@ -47,7 +47,7 @@ class TagExtractor { * * Logic: * - * - Sanity-check that we're a PEF image. + * - Check that we're a PEF image. * - Find the signature, which should be stored in the PE "Certificates * Directory" (dumpbin.exe /headers "Firefox Setup 1.0.7.exe") in a * WIN_CERTIFICATE structure. diff --git a/omaha/base/extractor_unittest.cc b/omaha/base/extractor_unittest.cc index 44b3e26c4..1da7141d1 100644 --- a/omaha/base/extractor_unittest.cc +++ b/omaha/base/extractor_unittest.cc @@ -32,7 +32,7 @@ namespace omaha { const TCHAR kFilePath[] = _T("."); -const TCHAR kFileName[] = _T("GoogleUpdateSetup_repair.exe"); +const TCHAR kFileName[] = MAIN_EXE_BASE_NAME _T("Setup_repair.exe"); const char kTagString[] = "1234567890abcdefg"; const char kAppendTagString[] = "..AppendedStr"; diff --git a/omaha/base/file.cc b/omaha/base/file.cc index eff3cee1e..a7d8e75b9 100644 --- a/omaha/base/file.cc +++ b/omaha/base/file.cc @@ -90,7 +90,7 @@ File::File() File::~File() { if (handle_ != INVALID_HANDLE_VALUE) { - VERIFY1(SUCCEEDED(Close())); + VERIFY_SUCCEEDED(Close()); } } @@ -153,34 +153,23 @@ HRESULT File::OpenShareMode(const TCHAR* file_name, return S_OK; } -// The path must not be enclosed in quotes. This is the Windows standard. -// ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths. -bool File::Exists(const TCHAR* file_name) { - ASSERT1(file_name && *file_name); - ASSERT1(lstrlen(file_name) > 0); - - // NOTE: This is the fastest implementation I found. The results were: - // CreateFile 1783739 avg ticks/call - // FindFirstFile 634148 avg ticks/call - // GetFileAttributes 428714 avg ticks/call - // GetFileAttributesEx 396324 avg ticks/call - WIN32_FILE_ATTRIBUTE_DATA attrs = {0}; - return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs); -} - -bool File::IsDirectory(const TCHAR* file_name) { +HRESULT File::IsReparsePoint(const TCHAR* file_name, bool* is_reparse_point) { ASSERT1(file_name && *file_name); + ASSERT1(is_reparse_point); - WIN32_FILE_ATTRIBUTE_DATA attrs; - SetZero(attrs); + WIN32_FILE_ATTRIBUTE_DATA attrs = {}; if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) { - UTIL_LOG(LEVEL_ERROR, - (_T("[File::IsDirectory - GetFileAttributesEx failed][%s][0x%x]"), - file_name, HRESULTFromLastError())); - return false; + HRESULT hr = HRESULTFromLastError(); + UTIL_LOG( + LE, + (_T("[File::IsReparsePoint][::GetFileAttributesEx failed][%s][%#x]"), + file_name, hr)); + return hr; } - return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + *is_reparse_point = + ((attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0); + return S_OK; } HRESULT File::GetWildcards(const TCHAR* dir, @@ -1050,7 +1039,7 @@ HRESULT File::SetLength(const uint32 n, bool zero_data) { HRESULT hr = S_OK; uint32 len = 0; - VERIFY1(SUCCEEDED(GetLength(&len))); + VERIFY_SUCCEEDED(GetLength(&len)); if (len == n) { return S_OK; diff --git a/omaha/base/file.h b/omaha/base/file.h index b9fa5e9aa..64ff38c53 100644 --- a/omaha/base/file.h +++ b/omaha/base/file.h @@ -19,8 +19,8 @@ #ifndef OMAHA_BASE_FILE_H_ #define OMAHA_BASE_FILE_H_ -#include #include +#include #include #include #include @@ -33,7 +33,6 @@ namespace omaha { class File { public: - File(); ~File(); @@ -45,8 +44,36 @@ class File { HRESULT Close(); - static bool Exists(const TCHAR* file_name); - static bool IsDirectory(const TCHAR *file_name); + static inline bool Exists(const TCHAR* file_name) { + // The path must not be enclosed in quotes. This is the Windows standard. + // ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths. + _ASSERTE(file_name && *file_name); + _ASSERTE(lstrlen(file_name) > 0); + + // NOTE: This is the fastest implementation I found. The results were: + // CreateFile 1783739 avg ticks/call + // FindFirstFile 634148 avg ticks/call + // GetFileAttributes 428714 avg ticks/call + // GetFileAttributesEx 396324 avg ticks/call + WIN32_FILE_ATTRIBUTE_DATA attrs = {0}; + return 0 != ::GetFileAttributesEx(file_name, + ::GetFileExInfoStandard, + &attrs); + } + + static inline bool IsDirectory(const TCHAR* file_name) { + _ASSERTE(file_name && *file_name); + + WIN32_FILE_ATTRIBUTE_DATA attrs = {}; + if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) { + return false; + } + + return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + + static HRESULT IsReparsePoint(const TCHAR* file_name, + bool* is_reparse_point); static HRESULT GetWildcards(const TCHAR* dir, const TCHAR* wildcard, std::vector* matching_paths); // returns S_OK on successful removal or if not existing diff --git a/omaha/base/libzip/zip_err_str.c b/omaha/base/libzip/zip_err_str.c new file mode 100644 index 000000000..c9b1504ec --- /dev/null +++ b/omaha/base/libzip/zip_err_str.c @@ -0,0 +1,77 @@ +/* + This file was generated automatically by CMake + from zip.h and zipint.h; make changes there. +*/ + +#include "zipint.h" + +#define L ZIP_ET_LIBZIP +#define N ZIP_ET_NONE +#define S ZIP_ET_SYS +#define Z ZIP_ET_ZLIB + +#define E ZIP_DETAIL_ET_ENTRY +#define G ZIP_DETAIL_ET_GLOBAL + +const struct _zip_err_info _zip_err_str[] = { + { N, "No error" }, + { N, "Multi-disk zip archives not supported" }, + { S, "Renaming temporary file failed" }, + { S, "Closing zip archive failed" }, + { S, "Seek error" }, + { S, "Read error" }, + { S, "Write error" }, + { N, "CRC error" }, + { N, "Containing zip archive was closed" }, + { N, "No such file" }, + { N, "File already exists" }, + { S, "Can't open file" }, + { S, "Failure to create temporary file" }, + { Z, "Zlib error" }, + { N, "Malloc failure" }, + { N, "Entry has been changed" }, + { N, "Compression method not supported" }, + { N, "Premature end of file" }, + { N, "Invalid argument" }, + { N, "Not a zip archive" }, + { N, "Internal error" }, + { L, "Zip archive inconsistent" }, + { S, "Can't remove file" }, + { N, "Entry has been deleted" }, + { N, "Encryption method not supported" }, + { N, "Read-only archive" }, + { N, "No password provided" }, + { N, "Wrong password provided" }, + { N, "Operation not supported" }, + { N, "Resource still in use" }, + { S, "Tell error" }, + { N, "Compressed data invalid" }, + { N, "Operation cancelled" }, +}; + +const int _zip_err_str_count = sizeof(_zip_err_str)/sizeof(_zip_err_str[0]); + +const struct _zip_err_info _zip_err_details[] = { + { G, "no detail" }, + { G, "central directory overlaps EOCD, or there is space between them" }, + { G, "archive comment length incorrect" }, + { G, "central directory length invalid" }, + { E, "central header invalid" }, + { G, "central directory count of entries is incorrect" }, + { E, "local and central headers do not match" }, + { G, "wrong EOCD length" }, + { G, "EOCD64 overlaps EOCD, or there is space between them" }, + { G, "EOCD64 magic incorrect" }, + { G, "EOCD64 and EOCD do not match" }, + { G, "invalid value in central directory" }, + { E, "variable size fields overflow header" }, + { E, "invalid UTF-8 in filename" }, + { E, "invalid UTF-8 in comment" }, + { E, "invalid Zip64 extra field" }, + { E, "invalid WinZip AES extra field" }, + { E, "garbage at end of extra fields" }, + { E, "extra field length is invalid" }, + { E, "file length in header doesn't match actual file length" }, +}; + +const int _zip_err_details_count = sizeof(_zip_err_details)/sizeof(_zip_err_details[0]); diff --git a/omaha/base/localization.cc b/omaha/base/localization.cc deleted file mode 100644 index 5c1ffedc9..000000000 --- a/omaha/base/localization.cc +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2004-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// localization.cpp -// -// Localization functions for date-time, strings, locales, and numbers - -#include "omaha/base/localization.h" - -#include // SetThreadLocale -#include -#include "omaha/base/debug.h" -#include "omaha/base/logging.h" -#include "omaha/base/string.h" -#include "omaha/base/time.h" -#include "omaha/base/utils.h" - -namespace omaha { - -// The maximum length for a date -#define kDateLengthMax 200 - -// The maximum length for chracter seperators -#define kDecimalSeparatorMaxLen 5 - -// Allow the unittest to override. -static LCID lcid_override = MAKELCID(-1, -1); - -void SetLcidOverride(const LCID & lcid_new) { - lcid_override = lcid_new; -} - -// We should call this, it allows for our override -LCID GetLiveLcid() { - if (lcid_override != MAKELCID(-1, -1)) - return lcid_override; - - return GetUserDefaultLCID(); -} - - -CString ShowDateInternal(const time64 & t, const LCID & lcid, - // Either type needs to be 0 or format needs to be - // NULL; both cannot be set simultaneously: - const DWORD type, const TCHAR * format ) { - SYSTEMTIME time = Time64ToLocalTime(t); - TCHAR buf[kDateLengthMax] = {_T('\0')}; - int num = ::GetDateFormat(lcid, type, &time, format, buf, kDateLengthMax); - ASSERT(num > 0, (_T("[localization::ShowDateInternal] - GetDateFormat ") - _T("failed"))); - - return CString(buf); -} - -CString ShowDateForLocale(const time64 & t, const LCID & lcid) { - return ShowDateInternal(t, lcid, DATE_SHORTDATE, NULL); -} - -CString ShowFormattedDateForLocale(const time64 & t, const LCID & lcid, - const TCHAR * format) { - return ShowDateInternal(t, lcid, 0, format); -} - - -CString ShowTimeInternal(const time64 & t, const LCID & lcid, - // Either type needs to be 0 or format needs to be - // NULL; both cannot be set simultaneously: - const DWORD type, const TCHAR * format) { - ASSERT(IsValidTime(t), (_T("[localization::ShowTimeInternal - Invalid ") - _T("time %llu"), t)); - - SYSTEMTIME time = Time64ToLocalTime(t); - TCHAR buf[kDateLengthMax] = {_T('\0')}; - int num = ::GetTimeFormat(lcid, type, &time, format, buf, kDateLengthMax); - ASSERT(num > 0, (_T("[localization::ShowTimeInternal - GetTimeFormat ") - _T("failed"))); - - return CString(buf); -} - -CString ShowTimeForLocale(const time64 & t, const LCID & lcid) { - return ShowTimeInternal(t, lcid, TIME_NOSECONDS, NULL); -} - -CString ShowFormattedTimeForLocale(const time64 & t, const LCID & lcid, - const TCHAR * format) { - return ShowTimeInternal(t, lcid, 0, format); -} - -// Show the long date and time [ie - Tuesday, March 20, 2004 5:15pm] -CString ShowDateTimeForLocale(const time64 & t, const LCID & lcid) { - return ShowDateForLocale(t, lcid) + _T(" ") + ShowTimeForLocale(t, lcid); -} - -// Get the long data and time in a (US English) format for logging -CString ShowDateTimeForLogging(const time64 & t) { - if (t == 0) { - return CString(); - } - const LCID lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); - return ShowDateTimeForLocale(t, lcid); -} - -// Convert a number in string format to formatted string -static CString Show(const CString & in, const int decimal_places) { - NUMBERFMT nf = {0}; - TCHAR decimal_seperator[8] = {_T('\0')}; - TCHAR thousands_seperator[8] = {_T('\0')}; - GetNumberFormatForLCID(GetLiveLcid(), &nf, - decimal_seperator, arraysize(decimal_seperator), - thousands_seperator, arraysize(thousands_seperator)); - nf.NumDigits = decimal_places; - - TCHAR buf[kDateLengthMax] = {_T('\0')}; - int num = GetNumberFormat(GetLiveLcid(), // locale (current user locale) - 0, // options - in, // input string (see MSDN for chars) - &nf, // formatting information - buf, // formatted string buffer - kDateLengthMax); // size of buffer - - ASSERT(num > 0, (_T("GetNumberFormat failed: %s?"), in.GetString())); - - return CString(buf); -} - -// If we have a formatted number containing a decimal, and we want it to be -// an int -CString TrimDecimal(const CString & in) { - // Get the decimal seperator -- cache it as this is very slow - static LCID last_user_default_lcid = MAKELCID(-1, -1); - static TCHAR buf[kDecimalSeparatorMaxLen] = {_T('\0')}; - - LCID current_lcid = GetLiveLcid(); - if (last_user_default_lcid != current_lcid) { - int num = GetLocaleInfo(GetLiveLcid(), - LOCALE_SDECIMAL, - buf, - kDecimalSeparatorMaxLen); - ASSERT(num > 0, (L"GetLocaleInfo(.., LOCALE_SDECIMAL, ..) failed?")); - last_user_default_lcid = current_lcid; - } - - CString sep(buf); - - // Trim it if necessary - int pos = String_FindString(in, sep); - if (pos != -1) - return in.Left(pos); - - return in; -} - -// Number Functions -// Changes the number into a user viewable format for the current locale - -// TODO(omaha): Rename these functions into ShowNumberForLocale. -CString Show(const int i) { - return TrimDecimal(Show(itostr(i), 0)); -} - -CString Show(const uint32 u) { - return TrimDecimal(Show(itostr(u), 0)); -} - -CString Show(const double & d, const int decimal_places) { - return Show(String_DoubleToString(d, decimal_places), decimal_places); -} - -HRESULT SetLocaleToRfc1766(const TCHAR * rfc1766_locale) { - ASSERT1(rfc1766_locale != NULL); - - // Convert the RFC 1766 locale (eg, "fr-CA" for Canadian French to a - // Windows LCID (eg, 0x0c0c for Canadian French) - CComPtr pIM; - RET_IF_FAILED(pIM.CoCreateInstance(__uuidof(CMultiLanguage))); - - LCID lcid = 0; - CComBSTR rfc1766_locale_bstr(rfc1766_locale); - RET_IF_FAILED(pIM->GetLcidFromRfc1766(&lcid, (BSTR)rfc1766_locale_bstr)); - - return SetLocaleToLCID(lcid); -} - -HRESULT SetLocaleToLCID(const LCID & lcid) { - // Initialize the locales - // (in an attempt to cut down on our memory footprint, don't call - // the libc version of setlocale) - if (!::SetThreadLocale(lcid)) { - UTIL_LOG(LEVEL_ERROR, (_T("Unable to SetThreadLocale to lcid 0x%x"), - lcid)); - return E_FAIL; - } - - return S_OK; -} - -HRESULT GetLocaleAsLCID(LCID * lcid) { - ASSERT1(lcid != NULL); - - *lcid = GetThreadLocale(); - return S_OK; -} - -HRESULT GetLocaleAsRfc1766(CString * rfc1766_locale) { - ASSERT1(rfc1766_locale != NULL); - - LCID lcid = 0; - HRESULT hr = GetLocaleAsLCID(&lcid); - if (FAILED(hr)) { - return hr; - } - - CComPtr pIM; - RET_IF_FAILED(pIM.CoCreateInstance(__uuidof(CMultiLanguage))); - - CComBSTR bstr; - RET_IF_FAILED(pIM->GetRfc1766FromLcid(lcid, &bstr)); - - *rfc1766_locale = bstr; - return hr; -} - -HRESULT GetNumberFormatForLCID(const LCID & lcid, NUMBERFMT * fmt, - TCHAR * fmt_decimal_buf, - size_t decimal_buf_len, // including null char - TCHAR * fmt_thousand_buf, - size_t thousand_buf_len) { // including null - ASSERT1(fmt); - - if (decimal_buf_len > INT_MAX || thousand_buf_len > INT_MAX) { - return E_INVALIDARG; - } - - TCHAR buf[64] = {_T('\0')}; - const int buf_len = arraysize(buf); - - HRESULT hr = S_OK; - int retval = GetLocaleInfo(lcid, LOCALE_IDIGITS, buf, buf_len); - - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[localization::GetNumberFormatForLCID - ") - _T("Failed to load LOCALE_IDIGITS]"))); - hr = E_FAIL; - } else { - fmt->NumDigits = String_StringToInt(buf); - } - - retval = GetLocaleInfo(lcid, LOCALE_ILZERO, buf, buf_len); - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ") - _T("Failed to load LOCALE_ILZERO]"))); - hr = E_FAIL; - } else { - fmt->LeadingZero = String_StringToInt(buf); - } - - retval = GetLocaleInfo(lcid, LOCALE_INEGNUMBER, buf, buf_len); - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ") - _T("Failed to load LOCALE_INEGNUMBER]"))); - hr = E_FAIL; - } else { - fmt->NegativeOrder = String_StringToInt(buf); - } - - retval = GetLocaleInfo(lcid, LOCALE_SGROUPING, buf, buf_len); - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ") - _T("Failed to load LOCALE_SGROUPING]"))); - hr = E_FAIL; - } else { - // A string terminated in ';0' is equivalent to the substring without - // the ';0', so just truncate the ';0' from the string - int semicolon_idx = String_ReverseFindChar(buf, _T(';')); - if (retval > semicolon_idx && buf[semicolon_idx + 1] == _T('0')) { - buf[semicolon_idx] = _T('\0'); - } - - if (String_FindChar(buf, _T(';')) != -1) { - // NUMBERFMT only allows values 0-9 or 32 for number grouping. If - // this locale has variable-length grouping rules (as indicated by - // the presence of ';[1-9]'), pass in the only variable-length - // grouping rule NUMBERFMT understands: 32. Note that '3;0' is - // considered a fixed-length grouping rule and handled above. - // This is a HACK. - fmt->Grouping = 32; - } else { - fmt->Grouping = String_StringToInt(buf); - } - } - - // GetLocaleInfo doesn't write more than 4 chars for this field (per MSDN) - retval = GetLocaleInfo(lcid, LOCALE_SDECIMAL, fmt_decimal_buf, - static_cast(decimal_buf_len)); - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ") - _T("Failed to load LOCALE_SDECIMAL]"))); - hr = E_FAIL; - } else { - fmt->lpDecimalSep = fmt_decimal_buf; - } - - // GetLocaleInfo doesn't write more than 4 chars for this field (per MSDN) - retval = GetLocaleInfo(lcid, LOCALE_STHOUSAND, fmt_thousand_buf, - static_cast(thousand_buf_len)); - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ") - _T("Failed to load LOCALE_STHOUSAND]"))); - hr = E_FAIL; - } else { - fmt->lpThousandSep = fmt_thousand_buf; - } - - retval = GetLocaleInfo(lcid, LOCALE_INEGNUMBER, buf, buf_len); - if (!retval) { - CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ") - _T("Failed to load LOCALE_INEGNUMBER]"))); - hr = E_FAIL; - } else { - fmt->NegativeOrder = String_StringToInt(buf); - } - - return hr; -} - -} // namespace omaha - diff --git a/omaha/base/localization.h b/omaha/base/localization.h deleted file mode 100644 index 87c8b147e..000000000 --- a/omaha/base/localization.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2004-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// -// localization.h -// -// Localization functions for date-time, strings, locales, and numbers - -#ifndef OMAHA_BASE_LOCALIZATION_H_ -#define OMAHA_BASE_LOCALIZATION_H_ - -#include -#include "base/basictypes.h" -#include "omaha/base/time.h" - -namespace omaha { - -// Allows us to override LCIDs for unittests -void SetLcidOverride(const LCID & lcid_new); - - -// -// Date-time Functions -// - -// Show the time in the specified locale's default format. -// If you want to use the user's default format, use LOCALE_USER_DEFAULT -// for the locale [eg - "5:15:34 pm" is the US default] -CString ShowDateForLocale(const time64 & t, const LCID & lcid); - -// Show the time in the specified format for the specified locale. -// If you want to use the user's default format, use LOCALE_USER_DEFAULT -// for the locale [eg - "5:15:34 pm" is the US default] -CString ShowFormattedDateForLocale(const time64 & t, const LCID & lcid, - const TCHAR * format); - - -// Show the time in the specified locale's default format. -// If you want to use the user's default format, use LOCALE_USER_DEFAULT -// for the locale [eg - "5:15:34 pm" is the US default] -CString ShowTimeForLocale(const time64 & t, const LCID & lcid); - -// Show the time in the specified format for the specified locale. -// If you want to use the user's default format, use LOCALE_USER_DEFAULT -// for the locale [eg - "5:15:34 pm" is the US default] -CString ShowFormattedTimeForLocale(const time64 & t, const LCID & lcid, - const TCHAR * format); - -// Show the long date and time [ie - Tuesday, March 20, 2004 5:15pm] -CString ShowDateTimeForLocale(const time64 & t, const LCID & lcid); - -// Get the long data and time in a (US English) format for logging -CString ShowDateTimeForLogging(const time64 & t); - -// -// Number Functions -// - -// Changes the number into a user viewable format for the current locale -CString Show(const int i); -CString Show(const uint32 u); -CString Show(const double & d, const int decimal_places); - - -// -// Locale Name / LCID / RFC 1766 conversions -// -HRESULT SetLocaleToRfc1766(const TCHAR * rfc1766_locale); -HRESULT SetLocaleToLCID(const LCID & lcid); - -HRESULT GetLocaleAsLCID(LCID * lcid); -HRESULT GetLocaleAsRfc1766(CString * rfc1766_locale); - -HRESULT GetNumberFormatForLCID(const LCID & lcid, NUMBERFMT * fmt, - TCHAR * fmt_decimal_buf, - size_t decimal_buf_len, - TCHAR * fmt_thousand_buf, - size_t thousand_buf_len); - -} // namespace omaha - -#endif // OMAHA_BASE_LOCALIZATION_H_ diff --git a/omaha/base/localization_unittest.cc b/omaha/base/localization_unittest.cc deleted file mode 100644 index 221c23632..000000000 --- a/omaha/base/localization_unittest.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2004-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// localization_unittest.cpp -// -// Unit test functions for Localization - -#include -#include -#include "omaha/base/localization.h" -#include "omaha/base/string.h" -#include "omaha/base/time.h" -#include "omaha/testing/unit_test.h" - -using testing::Message; - -namespace omaha { - -// Test out the time display functions -void LocalizationTimeTest() { - CString time_str; - - // Lets process this a bit to give ourselves a known time. - SYSTEMTIME temp_time; - temp_time.wYear = 2004; - temp_time.wMonth = 4; - temp_time.wDayOfWeek = 1; - temp_time.wDay = 19; - temp_time.wHour = 19; - temp_time.wMinute = 18; - temp_time.wSecond = 17; - temp_time.wMilliseconds = 16; - - time64 override_time = SystemTimeToTime64(&temp_time); - - // Useful when debugging to confirm that this worked - SYSTEMTIME confirm = Time64ToSystemTime(override_time); - - // the need to check two different times below is because: - - // FileTimeToLocalFileTime uses the current settings for the time - // zone and daylight saving time. Therefore, if it is daylight - // saving time, this function will take daylight saving time into - // account, even if the time you are converting is in standard - // time. - // TODO(omaha): we may want to fix this. - - // Show just the time [ie -12:19pm] - time_str = ShowTimeForLocale(override_time, 1033 /* US english */); - ASSERT_TRUE(time_str == _T("12:18 PM") || time_str == _T("11:18 AM")) - << _T("Returned time string was ") << time_str.GetString(); - - // Show just the time [ie - 12:19:18pm] - time_str = ShowFormattedTimeForLocale(override_time, 1033, - _T("hh:mm:ss tt")); - ASSERT_TRUE(time_str == _T("12:18:17 PM") || time_str == _T("11:18:17 AM")) - << _T("Returned time string was ") << time_str.GetString(); - - // Try it out with a some different values to test out single digit - // minutes and such - temp_time.wHour = 15; - temp_time.wMinute = 4; - temp_time.wSecond = 3; - temp_time.wMilliseconds = 2; - override_time = SystemTimeToTime64(&temp_time); - - time_str = ShowTimeForLocale(override_time, 1033); - ASSERT_TRUE(time_str == _T("8:04 AM") || time_str == _T("7:04 AM")) - << _T("Returned time string was ") << time_str.GetString(); - - time_str = ShowFormattedTimeForLocale(override_time, 1033, - _T("hh:mm:ss tt")); - ASSERT_TRUE(time_str == _T("08:04:03 AM") || time_str == _T("07:04:03 AM")) - << _T("Returned time string was ") << time_str.GetString(); - - - // - // Check the date functionality - // - - temp_time.wYear = 2004; - temp_time.wMonth = 4; - temp_time.wDayOfWeek = 1; - temp_time.wDay = 19; - - // Show the short date - time_str = ShowDateForLocale(override_time, 1033); -// CHKM(time_str == _T("Monday, April 19, 2004"), - ASSERT_STREQ(time_str, _T("4/19/2004")); - - // Show the customized date - time_str = ShowFormattedDateForLocale(override_time, 1033, - _T("MMM d, yyyy")); - ASSERT_STREQ(time_str, _T("Apr 19, 2004")); - - // Try it out with a some different values to test out single dates and such - temp_time.wDay = 1; - override_time = SystemTimeToTime64(&temp_time); - - time_str = ShowFormattedDateForLocale(override_time, 1033, - _T("ddd, MMM dd")); - ASSERT_STREQ(time_str, _T("Thu, Apr 01")); - - time_str = ShowFormattedDateForLocale(override_time, 1033, _T("MM/dd/yyyy")); - ASSERT_STREQ(time_str, _T("04/01/2004")); -} - -// Test out the numbers and display functions -void LocalizationNumberTest() { - // Make sure we are using the normal american version - SetLcidOverride(1033); // the codepage for american english - - // Try some basics - ASSERT_STREQ(Show(1), _T("1")); - ASSERT_STREQ(Show(2), _T("2")); - - // Try some extremes - ASSERT_STREQ(Show(0), _T("0")); - ASSERT_STREQ(Show(std::numeric_limits::max()), _T("2,147,483,647")); - ASSERT_STREQ(Show(-std::numeric_limits::max()), - _T("-2,147,483,647")); - ASSERT_STREQ(Show(std::numeric_limits::max()), _T("4,294,967,295")); - - // Try some doubles - ASSERT_STREQ(Show(0.3, 0), _T("0")); - ASSERT_STREQ(Show(0.3, 1), _T("0.3")); - ASSERT_STREQ(Show(0.3, 2), _T("0.30")); - ASSERT_STREQ(Show(0.3, 5), _T("0.30000")); - - // Try some with interesting rounding - ASSERT_STREQ(Show(0.159, 0), _T("0")); - ASSERT_STREQ(Show(0.159, 1), _T("0.1")); - ASSERT_STREQ(Show(0.159, 2), _T("0.15")); - ASSERT_STREQ(Show(0.159, 5), _T("0.15900")); - - // Try a nice whole number - ASSERT_STREQ(Show(12.0, 0), _T("12")); - ASSERT_STREQ(Show(12.0, 1), _T("12.0")); - ASSERT_STREQ(Show(12.0, 2), _T("12.00")); - ASSERT_STREQ(Show(12.0, 5), _T("12.00000")); -} - -TEST(LocalizationTest, Localization) { - LocalizationTimeTest(); - LocalizationNumberTest(); -} - -} // namespace omaha - diff --git a/omaha/base/logging.cc b/omaha/base/logging.cc index a0d34208c..94a6abfc2 100644 --- a/omaha/base/logging.cc +++ b/omaha/base/logging.cc @@ -69,6 +69,52 @@ namespace omaha { +namespace { + +// Checks an open file handle to see if it is a reparse point. +bool IsReparsePoint(HANDLE file) { + if (!file) { + return true; + } + + BY_HANDLE_FILE_INFORMATION file_info = {}; + if (!::GetFileInformationByHandle(file, &file_info)) { + ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: ERROR - " + L"[::GetFileInformationByHandle failed][%d]", + ::GetLastError())); + return true; + } + + return (file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; +} + +bool IsEnabledLogToFile() { + HKEY key = NULL; + int res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, + REG_UPDATE_DEV, + 0, + KEY_READ, + &key); + if (res != ERROR_SUCCESS) { + return false; + } + + DWORD is_enabled_log_to_file = 0; + DWORD bytes = sizeof(is_enabled_log_to_file); + DWORD type = REG_DWORD; + res = ::RegQueryValueEx(key, + kRegValueIsEnabledLogToFile, + 0, + &type, + reinterpret_cast(&is_enabled_log_to_file), + &bytes); + ::RegCloseKey(key); + + return res == ERROR_SUCCESS && type == REG_DWORD && is_enabled_log_to_file; +} + +} // namespace + // enforce ban on ASSERT/REPORT #undef ASSERT #undef REPORT @@ -101,7 +147,6 @@ struct { LC_ENTRY(LC_SHELL), LC_ENTRY(LC_CORE), LC_ENTRY(LC_JS), - LC_ENTRY(LC_PLUGIN), LC_ENTRY(LC_SERVICE), LC_ENTRY(LC_OPT), LC_ENTRY(LC_NET), @@ -182,8 +227,7 @@ Logging::Logging() logging_enabled_(true), force_show_time_(false), show_time_(true), - log_to_file_(true), - log_file_name_(kDefaultLogFileName), + log_to_file_(false), log_to_debug_out_(true), append_to_file_(true), logging_shutdown_(false), @@ -256,6 +300,8 @@ void Logging::UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat) { } void Logging::ReadLoggingSettings() { + log_to_file_ = IsEnabledLogToFile(); + CString config_file = GetCurrentConfigurationFilePath(); if (!config_file.IsEmpty()) { logging_enabled_ = ::GetPrivateProfileInt( @@ -270,12 +316,6 @@ void Logging::ReadLoggingSettings() { kDefaultShowTime, config_file) == 0 ? false : true; - log_to_file_ = ::GetPrivateProfileInt( - kConfigSectionLoggingSettings, - kConfigAttrLogToFile, - kDefaultLogToFile, - config_file) == 0 ? false : true; - log_to_debug_out_ = ::GetPrivateProfileInt( kConfigSectionLoggingSettings, kConfigAttrLogToOutputDebug, @@ -287,20 +327,11 @@ void Logging::ReadLoggingSettings() { kConfigAttrAppendToFile, kDefaultAppendToFile, config_file) == 0 ? false : true; - - ::GetPrivateProfileString(kConfigSectionLoggingSettings, - kConfigAttrLogFilePath, - kDefaultLogFileName, - CStrBuf(log_file_name_, MAX_PATH), - MAX_PATH, - config_file); } else { logging_enabled_ = kDefaultLoggingEnabled; show_time_ = kDefaultShowTime; - log_to_file_ = kDefaultLogToFile; log_to_debug_out_ = kDefaultLogToOutputDebug; append_to_file_ = kDefaultAppendToFile; - log_file_name_ = kDefaultLogFileName; } if (force_show_time_) { @@ -338,20 +369,12 @@ CString Logging::GetDefaultLogDirectory() const { } CString Logging::GetLogFilePath() const { - if (log_file_name_.IsEmpty()) { - return CString(); - } - - if (!ATLPath::IsRelative(log_file_name_)) { - return log_file_name_; - } - CString path = GetDefaultLogDirectory(); if (path.IsEmpty()) { return CString(); } - if (!::PathAppend(CStrBuf(path, MAX_PATH), log_file_name_)) { + if (!::PathAppend(CStrBuf(path, MAX_PATH), kDefaultLogFileName)) { return CString(); } @@ -387,8 +410,7 @@ void Logging::ConfigureFileLogWriter() { return; } - // Extract the final target directory which will not be what - // GetDefaultLogDirectory() returns if log_file_name_ is an absolute path. + // Extract the final target directory. CString log_file_dir = GetDirectoryFromPath(path); if (!File::Exists(log_file_dir)) { if (FAILED(CreateDir(log_file_dir, NULL))) { @@ -943,7 +965,7 @@ bool LogWriter::Register() { bool LogWriter::Unregister() { Logging* logger = GetLogging(); if (logger) { - return logger->RegisterWriter(this); + return logger->UnregisterWriter(this); } else { return false; } @@ -1085,6 +1107,30 @@ bool FileLogWriter::CreateLoggingFile() { return false; } + // As a defense in depth measure, we check to make sure the parent directory + // has not been redirected. i.e., the %LocalAppData%\Google\Update directory. + // We do not check %LocalAppData%\Google and above for reparse points, since + // an attacker would need to reuse an existing directory structure which has + // "\Update", which narrows the attack surface considerably, and in addition, + // we only write to a "GoogleUpdate.log" file within, which is unlikely to + // affect most applications (such as GoogleUpdate, which has that directory + // structure under %ProgramFiles (x86)%). + const CString log_file_dir = GetDirectoryFromPath(file_name_); + bool is_log_file_dir_reparse_point = true; + File::IsReparsePoint(log_file_dir, &is_log_file_dir_reparse_point); + + // Check whether the file or the parent directory are reparse points after + // opening the file. The checks are made after opening the file, so that the + // attacker does not get a chance to substitute a reparse point. + if (is_log_file_dir_reparse_point || IsReparsePoint(log_file_)) { + ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - " + L"Log path %s has a reparse point", + proc_name_, file_name_)); + ::CloseHandle(log_file_); + log_file_ = NULL; + return false; + } + // Allow users to read, write, and delete the log file. ACCESS_MASK mask = GENERIC_READ | GENERIC_WRITE | DELETE; CDacl dacl; diff --git a/omaha/base/logging.h b/omaha/base/logging.h index 2a6d714af..ad1f4d8c1 100644 --- a/omaha/base/logging.h +++ b/omaha/base/logging.h @@ -58,7 +58,7 @@ namespace omaha { #ifdef _DEBUG #define kDefaultMaxLogFileSize 0xFFFFFFFF // 4GB -#define kDefaultLogToFile 1 +#define kDefaultLogToFile 0 #define kDefaultLogToOutputDebug 1 #define kDefaultLogLevel L3 #else @@ -80,7 +80,6 @@ namespace omaha { #define kConfigAttrEnableLogging L"EnableLogging" #define kConfigAttrShowTime L"ShowTime" #define kConfigAttrLogToFile L"LogToFile" -#define kConfigAttrLogFilePath L"LogFilePath" #define kConfigAttrLogFileWide L"LogFileWide" #define kConfigAttrLogToOutputDebug L"LogToOutputDebug" #define kConfigAttrAppendToFile L"AppendToFile" @@ -129,7 +128,6 @@ namespace omaha { // Shortcuts for different logging categories - no need to specify the category. #define CORE_LOG(x, y) LC_LOG_DEBUG(omaha::LC_CORE, x, y) #define NET_LOG(x, y) LC_LOG_DEBUG(omaha::LC_NET, x, y) -#define PLUGIN_LOG(x, y) LC_LOG_DEBUG(omaha::LC_PLUGIN, x, y) #define SERVICE_LOG(x, y) LC_LOG_DEBUG(omaha::LC_SERVICE, x, y) #define SETUP_LOG(x, y) LC_LOG_DEBUG(omaha::LC_SETUP, x, y) #define SHELL_LOG(x, y) LC_LOG_DEBUG(omaha::LC_SHELL, x, y) @@ -154,7 +152,6 @@ enum LogCategory { LC_SHELL, LC_CORE, LC_JS, - LC_PLUGIN, LC_SERVICE, LC_OPT, LC_NET, @@ -216,6 +213,7 @@ class LogWriter { protected: LogWriter(); virtual void Cleanup(); + public: virtual ~LogWriter(); @@ -370,6 +368,7 @@ class Logging { const CString& proc_name() const { return proc_name_; } bool IsCategoryEnabledForBuffering(LogCategory cat); + private: bool InternalInitialize(); void InternalLogMessageMaskedVA(DWORD writer_mask, @@ -412,7 +411,6 @@ class Logging { CString GetAltConfigurationFilePath() const; public: - // Passes the messages along to other OutputMessage() void OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level, const wchar_t* msg1, const wchar_t* msg2); @@ -424,7 +422,6 @@ class Logging { void OutputMessage(DWORD writer_mask, const OutputInfo* output_info); private: - CategoryInfo category_list_[LC_MAX_CAT]; // Checks if logging is initialized. @@ -444,7 +441,6 @@ class Logging { bool force_show_time_; bool show_time_; bool log_to_file_; - CString log_file_name_; bool log_to_debug_out_; bool append_to_file_; diff --git a/omaha/base/logging/logging.cc b/omaha/base/logging/logging.cc index 330a2702a..b68042837 100644 --- a/omaha/base/logging/logging.cc +++ b/omaha/base/logging/logging.cc @@ -175,8 +175,6 @@ void DisplayDebugMessage(const std::string& str) { wcsncat(prog_name, L"DebugMessage.exe", num); prog_name[MAX_PATH - 1] = L'\0'; - // stupid CreateProcess requires a non-const command line and may modify it. - // We also want to use the wide string int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0); if (!charcount) return; diff --git a/omaha/base/logging/logging.h b/omaha/base/logging/logging.h index f02918fde..8c63ea5a0 100644 --- a/omaha/base/logging/logging.h +++ b/omaha/base/logging/logging.h @@ -32,7 +32,7 @@ // loop, which causes application messages to be processed and potentially // dispatched to existing application windows. Since the application is in a // bad state when this assertion dialog is displayed, these messages may not -// get processed and hang the dialog, or the application might go crazy. +// get processed and hang the dialog, or the application might crash. // // Therefore, it can be beneficial to display the error dialog in a separate // process from the main application. When the logging system needs to display diff --git a/omaha/base/path.cc b/omaha/base/path.cc index 5d14caf26..43a90e920 100644 --- a/omaha/base/path.cc +++ b/omaha/base/path.cc @@ -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&); diff --git a/omaha/base/path_unittest.cc b/omaha/base/path_unittest.cc index 12267e668..67f559d85 100644 --- a/omaha/base/path_unittest.cc +++ b/omaha/base/path_unittest.cc @@ -138,17 +138,17 @@ TEST(PathTest, EnclosePathIfExe) { new_path = EnclosePathIfExe(original_path); EXPECT_STREQ(_T("\"c:\\Windows\\notepad.exe\""), new_path); - original_path = _T("c:\\Program Files\\Google\\Update"); + original_path = _T("c:\\Program Files\\") PATH_COMPANY_NAME _T("\\Update"); new_path = EnclosePathIfExe(original_path); EXPECT_STREQ(original_path, new_path); - original_path = _T("c:\\Progra Files\\Google\\Update\\1.1.1.1\\goopdate.dll"); + original_path = _T("c:\\Progra Files\\") PATH_COMPANY_NAME _T("\\Update\\1.1.1.1\\goopdate.dll"); new_path = EnclosePathIfExe(original_path); EXPECT_STREQ(original_path, new_path); - original_path = _T("c:\\Prog F\\Googl\\Update\\GoogleUpdate.exe"); + original_path = _T("c:\\Prog F\\Googl\\Update\\") MAIN_EXE_BASE_NAME _T(".exe"); new_path = EnclosePathIfExe(original_path); - EXPECT_STREQ(_T("\"c:\\Prog F\\Googl\\Update\\GoogleUpdate.exe\""), new_path); + EXPECT_STREQ(_T("\"c:\\Prog F\\Googl\\Update\\") MAIN_EXE_BASE_NAME _T(".exe\""), new_path); } TEST(PathTest, ConcatenatePath) { @@ -211,6 +211,11 @@ TEST(PathTest, ShortPathToLongPath) { CString expected_path("C:\\Program Files"); CString short_path("C:\\Progra~1"); + // The short path may not exist in some environments, such as windows sandbox. + if (!File::Exists(short_path)) { + return; + } + CString long_path; ASSERT_SUCCEEDED(ShortPathToLongPath(short_path, &long_path)); ASSERT_STREQ(expected_path, long_path); diff --git a/omaha/base/process.cc b/omaha/base/process.cc index 05a6b343b..7001bd7d4 100644 --- a/omaha/base/process.cc +++ b/omaha/base/process.cc @@ -159,7 +159,7 @@ bool Process::Running() const { HANDLE Process::AssignToJob() { // Make sure that the process handle is valid if (!get(process_)) { - return false; + return nullptr; } // Create a job @@ -168,7 +168,7 @@ HANDLE Process::AssignToJob() { UTIL_LOG(LEVEL_ERROR, (_T("[Process::AssignToJob - CreateJobObject failed][0x%x]"), HRESULTFromLastError())); - return false; + return nullptr; } // Assign the process to the job @@ -176,7 +176,7 @@ HANDLE Process::AssignToJob() { UTIL_LOG(LEVEL_ERROR, (_T("[Process::AssignToJob-AssignProcessToJobObject fail][0x%x]"), HRESULTFromLastError())); - return false; + return nullptr; } return release(job); diff --git a/omaha/base/process_unittest.cc b/omaha/base/process_unittest.cc index fdc2b5cb4..931f92a72 100644 --- a/omaha/base/process_unittest.cc +++ b/omaha/base/process_unittest.cc @@ -56,7 +56,7 @@ TEST(ProcessTest, StartOneProcess) { EXPECT_EQ(kExpectedExitCode, exit_code); } -// Dummy process to spin off and then find. The numeric argument will make +// Test process to spin off and then find. The numeric argument will make // ping.exe run until it's killed by the ScopedProcess destructor. const TCHAR kTestExecutable[] = _T("ping.exe"); const TCHAR kTestArguments[] = _T("-w 10000 2.2.2.2"); diff --git a/omaha/base/reg_key.cc b/omaha/base/reg_key.cc index 4a7b0c5c1..475718ba5 100644 --- a/omaha/base/reg_key.cc +++ b/omaha/base/reg_key.cc @@ -17,6 +17,7 @@ #include #include +#include #include "omaha/base/logging.h" #include "omaha/base/static_assert.h" @@ -29,6 +30,14 @@ // handler is compiled in a 64-bit form, and it needs to access the 32-bit // registry view. KEY_WOW64_32KEY is used by default for all registry access. +namespace { + +// Declare `::NtDeleteKey` exported from ntdll.lib here, since no official +// header has it. +extern "C" NTSTATUS WINAPI NtDeleteKey(IN HANDLE KeyHandle); + +} // namespace + namespace omaha { namespace { @@ -158,7 +167,8 @@ HRESULT RegKey::CreateKey(const TCHAR* full_key_name, HRESULT RegKey::Open(HKEY hKeyParent, const TCHAR * key_name, - REGSAM sam_desired) { + REGSAM sam_desired, + DWORD options) { ASSERT1(key_name); ASSERT1(hKeyParent != NULL); ASSERT1((sam_desired & (KEY_WOW64_64KEY | KEY_WOW64_32KEY)) != @@ -169,7 +179,7 @@ HRESULT RegKey::Open(HKEY hKeyParent, wow_override = k64BitView; else sam_desired |= KEY_WOW64_32KEY; - LONG res = ::RegOpenKeyEx(hKeyParent, key_name, 0, + LONG res = ::RegOpenKeyEx(hKeyParent, key_name, options, sam_desired, &hKey); HRESULT hr = HRESULT_FROM_WIN32(res); @@ -591,7 +601,7 @@ HRESULT RegKey::GetValue(const TCHAR * value_name, TCHAR * * value) const { if (byte_count != 0) { // make the call again res = ::SHQueryValueEx(h_key_, value_name, NULL, &type, - reinterpret_cast(*value), &byte_count); + reinterpret_cast(*value), &byte_count); hr = HRESULT_FROM_WIN32(res); } else { (*value)[0] = _T('\0'); @@ -633,7 +643,7 @@ HRESULT RegKey::GetValue(const TCHAR* value_name, OUT CString* value) const { hr = E_OUTOFMEMORY; } else { res = ::SHQueryValueEx(h_key_, value_name, NULL, &type, - reinterpret_cast(buffer), &byte_count); + reinterpret_cast(buffer), &byte_count); hr = HRESULT_FROM_WIN32(res); } value->ReleaseBuffer(); @@ -865,7 +875,7 @@ HRESULT RegKey::RenameValue(const TCHAR* old_value_name, return hr; } - VERIFY1(SUCCEEDED(DeleteValue(old_value_name))); + VERIFY_SUCCEEDED(DeleteValue(old_value_name)); return S_OK; } @@ -1017,10 +1027,39 @@ HRESULT RegKey::DeleteValue(const TCHAR * full_key_name, return hr; } +std::tuple RegKey::DeleteLink(const TCHAR* key_name) { + ASSERT1(key_name); + ASSERT1(h_key_); + + RegKey maybe_link; + HRESULT hr = maybe_link.Open(h_key_, + key_name, + KEY_QUERY_VALUE | DELETE, + REG_OPTION_OPEN_LINK); + if (FAILED(hr)) { + return {false, hr}; + } + + DWORD value_type = 0; + hr = maybe_link.GetValueType(L"SymbolicLinkValue", &value_type); + if (FAILED(hr) || value_type != REG_LINK) { + return {false, hr}; + } + + // `::NtDeleteKey` can delete symbolic links opened with + // `REG_OPTION_OPEN_LINK`. + return {true, HRESULT_FROM_NT(NtDeleteKey(maybe_link.h_key_))}; +} + HRESULT RegKey::RecurseDeleteSubKey(const TCHAR * key_name) { ASSERT1(key_name); ASSERT1(h_key_); + const std::tuple delete_result = DeleteLink(key_name); + if (std::get<0>(delete_result)) { + return std::get<1>(delete_result); + } + RegKey key; HRESULT hr = key.Open(h_key_, key_name, ApplyWoWOverride(KEY_ALL_ACCESS, wow_override_)); @@ -1342,7 +1381,7 @@ HRESULT RegKeyWatcher::EnsureEventSetup() { if (allow_creation_ && !RegKey::HasKey(reg_key_string_)) { RegKey key; - VERIFY1(SUCCEEDED(key.Create(reg_key_string_))); + VERIFY_SUCCEEDED(key.Create(reg_key_string_)); } HRESULT hr = local_reg_key->Open(reg_key_string_, KEY_NOTIFY); diff --git a/omaha/base/reg_key.h b/omaha/base/reg_key.h index da639240c..b232e2c7f 100644 --- a/omaha/base/reg_key.h +++ b/omaha/base/reg_key.h @@ -28,6 +28,7 @@ #include #include +#include #include #include "base/basictypes.h" @@ -96,7 +97,8 @@ class RegKey { // open an existing reg key HRESULT Open(HKEY hKeyParent, const TCHAR * key_name, - REGSAM sam_desired = KEY_ALL_ACCESS); + REGSAM sam_desired = KEY_ALL_ACCESS, + DWORD options = 0); // open an existing reg key, given the full key name, including the HKEY root // (say for example, "HKLM\\Software") @@ -357,6 +359,11 @@ class RegKey { // delete a subkey of the current key (with no subkeys) HRESULT DeleteSubKey(const TCHAR * key_name); + // Returns `true, hr` if `key_name` is a symbolic link, where `true` indicates + // that `key_name` is a symbolic link, and `hr` is the HRESULT success/error + // code for the delete operation. Returns `false, hr` otherwise. + std::tuple DeleteLink(const TCHAR* key_name); + // recursively delete a sub key of the current key (and all its subkeys) HRESULT RecurseDeleteSubKey(const TCHAR * key_name); @@ -820,6 +827,11 @@ inline HRESULT RegKey::DeleteSubKey(const TCHAR* key_name) { ASSERT1(key_name); ASSERT1(h_key_); + const std::tuple delete_result = DeleteLink(key_name); + if (std::get<0>(delete_result)) { + return std::get<1>(delete_result); + } + LONG res = ::RegDeleteKey(h_key_, key_name); HRESULT hr = HRESULT_FROM_WIN32(res); if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || diff --git a/omaha/base/reg_key_unittest.cc b/omaha/base/reg_key_unittest.cc index 9e25964a5..2b22e2f2c 100644 --- a/omaha/base/reg_key_unittest.cc +++ b/omaha/base/reg_key_unittest.cc @@ -16,13 +16,14 @@ #include "omaha/base/reg_key.h" #include "omaha/base/debug.h" -#include "omaha/base/utils.h" #include "omaha/base/dynamic_link_kernel32.h" +#include "omaha/base/user_info.h" +#include "omaha/base/utils.h" #include "omaha/testing/unit_test.h" namespace omaha { -#define kStTestRkeyRelativeBase _T("Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UnitTest") +#define kStTestRkeyRelativeBase _T("Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UnitTest") #define kStTestRkeyBase _T("HKCU\\") kStTestRkeyRelativeBase #define kStRkey1Name _T("TEST") #define kStRkey1 kStTestRkeyBase _T("\\") kStRkey1Name @@ -76,7 +77,7 @@ namespace omaha { // Test the private member functions of RegKey class RegKeyTestClass : public testing::Test { protected: - static const HKEY GetHKey(const RegKey& reg) { + static HKEY GetHKey(const RegKey& reg) { return reg.h_key_; } @@ -1020,6 +1021,64 @@ TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_ParentKeyDoesNotExist_Recursively) { EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey)); } +TEST_F(RegKeyCleanupTestKeyTest, DeleteLinkNotTarget) { + // Create a target key with string and DWORD values. + const CString target_path = kStRkey1 _T("\\LinkTarget"); + ASSERT_SUCCEEDED( + RegKey::SetValue(target_path, _T("TargetString"), _T("Hello"))); + ASSERT_SUCCEEDED( + RegKey::SetValue(target_path, _T("TargetDWORD"), static_cast(1))); + + // Create a source registry link to the above target key. + const CString source_path = kStRkey1 _T("\\LinkSource"); + { + RegKey link; + EXPECT_SUCCEEDED(link.Create(source_path, + nullptr, + REG_OPTION_CREATE_LINK | + REG_OPTION_NON_VOLATILE, + KEY_WRITE)); + CString user_sid; + ASSERT_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid)); + const CString value = _T("\\Registry\\User\\") + + user_sid + + _T("\\") kRkey1 _T("\\LinkTarget"); + ASSERT_SUCCEEDED( + link.SetValue(_T("SymbolicLinkValue"), + reinterpret_cast(value.GetString()), + value.GetLength() * sizeof(TCHAR), + REG_LINK)); + } + + // Verify that reading from the source registry link actually reads the target + // values. + { + CString string_value; + ASSERT_SUCCEEDED( + RegKey::GetValue(source_path, _T("TargetString"), &string_value)); + ASSERT_EQ(string_value, _T("Hello")); + DWORD value = 0; + ASSERT_SUCCEEDED(RegKey::GetValue(source_path, _T("TargetDWORD"), &value)); + ASSERT_EQ(value, 1U); + } + + // Now delete the link and ensure that the source registry link is no longer + // working, but the target key and values are still present. + { + ASSERT_SUCCEEDED(RegKey::DeleteKey(source_path)); + CString string_value; + ASSERT_FAILED( + RegKey::GetValue(source_path, _T("TargetString"), &string_value)); + ASSERT_SUCCEEDED( + RegKey::GetValue(target_path, _T("TargetString"), &string_value)); + ASSERT_EQ(string_value, _T("Hello")); + DWORD value = 0; + ASSERT_FAILED(RegKey::GetValue(source_path, _T("TargetDWORD"), &value)); + ASSERT_SUCCEEDED(RegKey::GetValue(target_path, _T("TargetDWORD"), &value)); + ASSERT_EQ(value, 1U); + } +} + TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_ParentKeyDoesNotExist_NotRecursively) { EXPECT_FALSE(RegKey::HasKey(kStRkey1)); diff --git a/omaha/base/regexp.cc b/omaha/base/regexp.cc deleted file mode 100644 index ba071ca06..000000000 --- a/omaha/base/regexp.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2005-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/base/regexp.h" -#include "omaha/base/debug.h" - -namespace omaha { - -#define kMaxArgs 16 - -bool RE::PartialMatch(const TCHAR* text, const RE& re, // 3..16 args - CString * a0, - CString * a1, - CString * a2, - CString * a3, - CString * a4, - CString * a5, - CString * a6, - CString * a7, - CString * a8, - CString * a9, - CString * a10, - CString * a11, - CString * a12, - CString * a13, - CString * a14, - CString * a15) -{ - ASSERT(text, (L"")); - // a0 may be NULL - // a1 may be NULL - // a2 may be NULL - // a3 may be NULL - // a4 may be NULL - // a5 may be NULL - // a6 may be NULL - // a7 may be NULL - // a8 may be NULL - // a9 may be NULL - // a10 may be NULL - // a11 may be NULL - // a12 may be NULL - // a13 may be NULL - // a14 may be NULL - // a15 may be NULL - - CString * args[kMaxArgs]; - int n = 0; - if (a0 == NULL) goto done; args[n++] = a0; - if (a1 == NULL) goto done; args[n++] = a1; - if (a2 == NULL) goto done; args[n++] = a2; - if (a3 == NULL) goto done; args[n++] = a3; - if (a4 == NULL) goto done; args[n++] = a4; - if (a5 == NULL) goto done; args[n++] = a5; - if (a6 == NULL) goto done; args[n++] = a6; - if (a7 == NULL) goto done; args[n++] = a7; - if (a8 == NULL) goto done; args[n++] = a8; - if (a9 == NULL) goto done; args[n++] = a9; - if (a10 == NULL) goto done; args[n++] = a10; - if (a11 == NULL) goto done; args[n++] = a11; - if (a12 == NULL) goto done; args[n++] = a12; - if (a13 == NULL) goto done; args[n++] = a13; - if (a14 == NULL) goto done; args[n++] = a14; - if (a15 == NULL) goto done; args[n++] = a15; - -done: - return re.DoMatchImpl(text,args,n,NULL); -} - -// Like PartialMatch(), except the "input" is advanced past the matched -// text. Note: "input" is modified iff this routine returns true. -// For example, "FindAndConsume(s, "(\\w+)", &word)" finds the next -// word in "s" and stores it in "word". -bool RE::FindAndConsume(const TCHAR **input, const RE& re, - CString * a0, - CString * a1, - CString * a2, - CString * a3, - CString * a4, - CString * a5, - CString * a6, - CString * a7, - CString * a8, - CString * a9, - CString * a10, - CString * a11, - CString * a12, - CString * a13, - CString * a14, - CString * a15) -{ - ASSERT(input, (L"")); - // a0 may be NULL - // a1 may be NULL - // a2 may be NULL - // a3 may be NULL - // a4 may be NULL - // a5 may be NULL - // a6 may be NULL - // a7 may be NULL - // a8 may be NULL - // a9 may be NULL - // a10 may be NULL - // a11 may be NULL - // a12 may be NULL - // a13 may be NULL - // a14 may be NULL - // a15 may be NULL - - CString * args[kMaxArgs]; - int n = 0; - if (a0 == NULL) goto done; args[n++] = a0; - if (a1 == NULL) goto done; args[n++] = a1; - if (a2 == NULL) goto done; args[n++] = a2; - if (a3 == NULL) goto done; args[n++] = a3; - if (a4 == NULL) goto done; args[n++] = a4; - if (a5 == NULL) goto done; args[n++] = a5; - if (a6 == NULL) goto done; args[n++] = a6; - if (a7 == NULL) goto done; args[n++] = a7; - if (a8 == NULL) goto done; args[n++] = a8; - if (a9 == NULL) goto done; args[n++] = a9; - if (a10 == NULL) goto done; args[n++] = a10; - if (a11 == NULL) goto done; args[n++] = a11; - if (a12 == NULL) goto done; args[n++] = a12; - if (a13 == NULL) goto done; args[n++] = a13; - if (a14 == NULL) goto done; args[n++] = a14; - if (a15 == NULL) goto done; args[n++] = a15; - -done: - return re.DoMatchImpl(*input,args,n,input); -} - -} // namespace omaha - diff --git a/omaha/base/regexp.h b/omaha/base/regexp.h deleted file mode 100644 index 25781c1f0..000000000 --- a/omaha/base/regexp.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2005-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Implementors: There is only one function to implement -- DoMatchImpl -// See below - -#ifndef OMAHA_COMMON_REGEXP_H__ -#define OMAHA_COMMON_REGEXP_H__ - -#include - -namespace omaha { - -// Interface for regular expression matching. Also corresponds to a -// pre-compiled regular expression. An "RE" object is safe for -// concurrent use by multiple threads. -class RE { - public: - - // Matches "text" against "pattern". If pointer arguments are - // supplied, copies matched sub-patterns into them. Use braces - // "{", "}" within the regexp to indicate a pattern to be copied. - // - // Returns true iff all of the following conditions are satisfied: - // a. some substring of "text" matches "pattern" - // b. The number of matched sub-patterns is >= number of supplied pointers - static bool PartialMatch(const TCHAR* text, const RE& re, // 3..16 args - CString * a0 = NULL, - CString * a1 = NULL, - CString * a2 = NULL, - CString * a3 = NULL, - CString * a4 = NULL, - CString * a5 = NULL, - CString * a6 = NULL, - CString * a7 = NULL, - CString * a8 = NULL, - CString * a9 = NULL, - CString * a10 = NULL, - CString * a11 = NULL, - CString * a12 = NULL, - CString * a13 = NULL, - CString * a14 = NULL, - CString * a15 = NULL); - - // Like PartialMatch(), except the "input" is advanced past the matched - // text. Note: "input" is modified iff this routine returns true. - // For example, "FindAndConsume(s, "{\\w+}", &word)" finds the next - // word in "s" and stores it in "word". - static bool FindAndConsume(const TCHAR** input, const RE& re, - CString * a0 = NULL, - CString * a1 = NULL, - CString * a2 = NULL, - CString * a3 = NULL, - CString * a4 = NULL, - CString * a5 = NULL, - CString * a6 = NULL, - CString * a7 = NULL, - CString * a8 = NULL, - CString * a9 = NULL, - CString * a10 = NULL, - CString * a11 = NULL, - CString * a12 = NULL, - CString * a13 = NULL, - CString * a14 = NULL, - CString * a15 = NULL); - - protected: - - // The behavior of this function is subject to how it's used - // in PartialMatch() and FindAndConsume() above. See the header - // description of those functions to understand how an implementation - // should behave. - // text is the text we're looking in - // args is where matches should be outputted - // n is the number of CStrings in args - // match_end is a pointer to the position in text that - // we ended matching on - // returns true if data was found, false otherwise - // Example:Suppose text = "google 1\nYahoo! 2\n ..." and the regexp - // is something like "{\w+} \d". If args has two CStrings (n=2), - // then args[0] = "google", arg[1] = "1" and match_end will point to the \n - // before "Yahoo!" - virtual bool DoMatchImpl(const TCHAR *text, - CString * args[], - int n, - const TCHAR ** match_end) const = 0; -}; - -} // namespace omaha - -#endif // OMAHA_COMMON_REGEXP_H__ diff --git a/omaha/base/registry_monitor_manager.cc b/omaha/base/registry_monitor_manager.cc index 7edec09d1..aacf82028 100644 --- a/omaha/base/registry_monitor_manager.cc +++ b/omaha/base/registry_monitor_manager.cc @@ -395,7 +395,7 @@ void KeyWatcher::HandleEvent(HANDLE handle) { } } - VERIFY1(SUCCEEDED(StartWatching())); + VERIFY_SUCCEEDED(StartWatching()); } HRESULT KeyWatcher::EnsureOpen() { @@ -403,7 +403,7 @@ HRESULT KeyWatcher::EnsureOpen() { // deleted and recreated back. if (!IsKeyValid()) { UTIL_LOG(L3, (_T("[key '%s' is not valid]"), key_id_.key_name())); - VERIFY1(SUCCEEDED(key_.Close())); + VERIFY_SUCCEEDED(key_.Close()); } // Open the key if not already open or create the key if needed. @@ -530,7 +530,7 @@ void RegistryMonitorImpl::Run() { std::unique_ptr handles(new HANDLE[kNumHandles]); for (size_t i = 0; i != watchers_.size(); ++i) { handles[i] = watchers_[i].second->notification_event(); - VERIFY1(SUCCEEDED(watchers_[i].second->StartWatching())); + VERIFY_SUCCEEDED(watchers_[i].second->StartWatching()); } handles[kStopMonitoringHandleIndex] = get(stop_monitoring_); diff --git a/omaha/base/safe_format.cc b/omaha/base/safe_format.cc index b169724d6..74306c372 100644 --- a/omaha/base/safe_format.cc +++ b/omaha/base/safe_format.cc @@ -105,7 +105,7 @@ void SafeCStringWFormatV(CStringW* dest_str, va_list arg_list) { ASSERT1(dest_str); ASSERT1(format_str); - VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list))); + VERIFY_SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)); } void SafeCStringAFormatV(CStringA* dest_str, @@ -113,7 +113,7 @@ void SafeCStringAFormatV(CStringA* dest_str, va_list arg_list) { ASSERT1(dest_str); ASSERT1(format_str); - VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list))); + VERIFY_SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)); } void SafeCStringWFormat(CStringW* dest_str, LPCWSTR format_str, ...) { @@ -122,7 +122,7 @@ void SafeCStringWFormat(CStringW* dest_str, LPCWSTR format_str, ...) { va_list arg_list; va_start(arg_list, format_str); - VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list))); + VERIFY_SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)); va_end(arg_list); } @@ -132,7 +132,7 @@ void SafeCStringAFormat(CStringA* dest_str, LPCSTR format_str, ...) { va_list arg_list; va_start(arg_list, format_str); - VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list))); + VERIFY_SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)); va_end(arg_list); } @@ -144,7 +144,7 @@ void SafeCStringWAppendFormat(CStringW* dest_str, LPCWSTR format_str, ...) { va_start(arg_list, format_str); CStringW append_str; - VERIFY1(SUCCEEDED(InternalCStringVPrintf(append_str, format_str, arg_list))); + VERIFY_SUCCEEDED(InternalCStringVPrintf(append_str, format_str, arg_list)); dest_str->Append(append_str); va_end(arg_list); @@ -158,7 +158,7 @@ void SafeCStringAAppendFormat(CStringA* dest_str, LPCSTR format_str, ...) { va_start(arg_list, format_str); CStringA append_str; - VERIFY1(SUCCEEDED(InternalCStringVPrintf(append_str, format_str, arg_list))); + VERIFY_SUCCEEDED(InternalCStringVPrintf(append_str, format_str, arg_list)); dest_str->Append(append_str); va_end(arg_list); diff --git a/omaha/base/scope_guard.h b/omaha/base/scope_guard.h index d0227c9a6..cf8b299f1 100644 --- a/omaha/base/scope_guard.h +++ b/omaha/base/scope_guard.h @@ -51,8 +51,10 @@ // TODO(omaha): provide support to run with or without exceptions enabled. // For now it assumes that the code is not throwing exceptions. -#ifndef SCOPEGUARD_H_ -#define SCOPEGUARD_H_ +#ifndef OMAHA_BASE_SCOPE_GUARD_H_ +#define OMAHA_BASE_SCOPE_GUARD_H_ + +#include "omaha/base/debug.h" namespace omaha { @@ -357,13 +359,9 @@ inline ObjScopeGuardImpl3 MakeObjGuard(Obj& obj, MemFun return ObjScopeGuardImpl3::MakeObjGuard(obj, memFun, p1, p2, p3); } -#define CONCATENATE_DIRECT(s1, s2) s1##s2 -#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2) -#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__) - #define ON_SCOPE_EXIT ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeGuard #define ON_SCOPE_EXIT_OBJ ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeObjGuard } // namespace omaha -#endif //SCOPEGUARD_H_ +#endif //OMAHA_BASE_SCOPE_GUARD_H_ diff --git a/omaha/base/scoped_impersonation.h b/omaha/base/scoped_impersonation.h index 6a78061c6..f62a41089 100644 --- a/omaha/base/scoped_impersonation.h +++ b/omaha/base/scoped_impersonation.h @@ -94,7 +94,16 @@ struct scoped_impersonation { class scoped_revert_to_self { public: scoped_revert_to_self() { - token_.GetThreadToken(TOKEN_ALL_ACCESS); + if (!token_.GetThreadToken(TOKEN_ALL_ACCESS)) { + if (::GetLastError() != ERROR_NO_TOKEN) { + ::RaiseException(EXCEPTION_FAILED_TO_GET_THREAD_TOKEN, + EXCEPTION_NONCONTINUABLE, + 0, + NULL); + } + return; + } + if (token_.GetHandle()) { RevertToSelfOrDie(); } diff --git a/omaha/base/security/README b/omaha/base/security/README index 181325a7f..a6df69db7 100644 --- a/omaha/base/security/README +++ b/omaha/base/security/README @@ -1,6 +1,6 @@ This directory contains a couple of stand-alone crypto implementations. -These implemenations have no Google3, stl or OpenSSL dependencies and +These implementations have no Google3, stl or OpenSSL dependencies and are endian-neutral. The Android code base has code derived from this code, notably a diff --git a/omaha/base/security/build.scons b/omaha/base/security/build.scons index 8eaeaeb37..50df69290 100644 --- a/omaha/base/security/build.scons +++ b/omaha/base/security/build.scons @@ -36,7 +36,6 @@ security_inputs = [ 'p256_ec.c', 'p256_ecdsa.c', 'p256_prng.c', - 'sha.c', 'sha256.c', 'util.c', ] diff --git a/omaha/base/security/hmac.c b/omaha/base/security/hmac.c index 667076d6b..0eeb17a73 100644 --- a/omaha/base/security/hmac.c +++ b/omaha/base/security/hmac.c @@ -19,7 +19,6 @@ #include "util.h" #include -#include "sha.h" #include "sha256.h" static void HMAC_init(LITE_HMAC_CTX* ctx, const void* key, unsigned int len) { @@ -46,11 +45,6 @@ static void HMAC_init(LITE_HMAC_CTX* ctx, const void* key, unsigned int len) { } } -void HMAC_SHA_init(LITE_HMAC_CTX* ctx, const void* key, unsigned int len) { - SHA_init(&ctx->hash); - HMAC_init(ctx, key, len); -} - void HMAC_SHA256_init(LITE_HMAC_CTX* ctx, const void* key, unsigned int len) { SHA256_init(&ctx->hash); HMAC_init(ctx, key, len); diff --git a/omaha/base/security/hmac.h b/omaha/base/security/hmac.h index baf545ff1..1ec70f971 100644 --- a/omaha/base/security/hmac.h +++ b/omaha/base/security/hmac.h @@ -28,7 +28,6 @@ typedef struct LITE_HMAC_CTX { uint8_t opad[64]; } LITE_HMAC_CTX; -void HMAC_SHA_init(LITE_HMAC_CTX* ctx, const void* key, unsigned int len); void HMAC_SHA256_init(LITE_HMAC_CTX* ctx, const void* key, unsigned int len); const uint8_t* HMAC_final(LITE_HMAC_CTX* ctx); diff --git a/omaha/base/security/hmac_unittest.cc b/omaha/base/security/hmac_unittest.cc index 339926a7e..a7adff700 100644 --- a/omaha/base/security/hmac_unittest.cc +++ b/omaha/base/security/hmac_unittest.cc @@ -32,32 +32,26 @@ static const struct KAT { int md5_keylength; // md5 test keys are sometimes shorter. const char* data; const char* md5; - const char* sha1; const char* sha256; } KATS[] = { {"x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", 16, "Hi There", "9294727a3638bb1c13f48ef8158bfc9d", - "b617318655057264e28bc0b6fb378c8ef146be00", "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"}, {"Jefe", 4, "what do ya want for nothing?", "750c783e6ab0b503eaa86e310a5db738", - "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"}, {"xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 16, "xdddddddddddddddddddddddddddddddddddddddddddddddddd" "dddddddddddddddddddddddddddddddddddddddddddddddddd", "56be34521d144c88dbb8c733f0e8b3f6", - "125d7342b9ac11cd91a39af48aa17b4f63f175d3", "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"}, {"x0102030405060708090a0b0c0d0e0f10111213141516171819", 25, "xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "697eaf0aca3a3aea3a75164746ffaa79", - "4c9007f4026250c6bc8414f9bf50c86c2d7235da", "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"}, {"x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", 16, "Test With Truncation", "56461ef2342edc00f9bab995690efd4c", - "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", "a3b6167473100ee06e0c796c2955552bfa6f7c0a6a8aef8b93f860aab0cd20c5"}, {"xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -65,7 +59,7 @@ static const struct KAT { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 80, "Test Using Larger Than Block-Size Key - Hash Key First", "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd", - "aa4ae5e15272d00e95705637ce8a3b55ed402112", nullptr}, + nullptr}, {"xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -76,7 +70,7 @@ static const struct KAT { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", 8 * 16 + 3, "Test Using Larger Than Block-Size Key - Hash Key First", - nullptr, nullptr, + nullptr, "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"}, {"xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -86,7 +80,7 @@ static const struct KAT { "Test Using Larger Than Block-Size Key and Larger Than One Block-Size " "Data", "6f630fad67cda0ee1fb1f562db3aa53e", - "e8e99d0f45237d786d6bbaa7965c7808bbff1a91", nullptr}, + nullptr}, {"xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -100,7 +94,7 @@ static const struct KAT { "This is a test using a larger than block-size key and a larger t" "han block-size data. The key needs to be hashed before being use" "d by the HMAC algorithm.", - nullptr, nullptr, + nullptr, "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"}, {nullptr}}; @@ -114,16 +108,6 @@ TEST_F(HmacTest, RFC2202andRFC4131) { string data = katp->data[0] == 'x' ? omaha::a2b_hex(katp->data + 1) : katp->data; - if (katp->sha1) { - HMAC_SHA_init(&hmac, key.data(), key.size()); - HMAC_update(&hmac, data.data(), data.size()); - - EXPECT_EQ(omaha::b2a_hex( - reinterpret_cast(HMAC_final(&hmac)), - HMAC_size(&hmac)), - katp->sha1); - } - if (katp->sha256) { HMAC_SHA256_init(&hmac, key.data(), key.size()); HMAC_update(&hmac, data.data(), data.size()); diff --git a/omaha/base/security/sha.c b/omaha/base/security/sha.c deleted file mode 100644 index 3b0a79585..000000000 --- a/omaha/base/security/sha.c +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Optimized for minimal code size. - -#include "sha.h" - -#include -#include - -#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static void SHA1_Transform(SHA_CTX* ctx) { - uint32_t W[80]; - uint32_t A, B, C, D, E; - uint8_t* p = ctx->buf; - int t; - - for(t = 0; t < 16; ++t) { - uint32_t tmp = (uint32_t)(*p++) << 24; - tmp |= (uint32_t)(*p++) << 16; - tmp |= (uint32_t)(*p++) << 8; - tmp |= (uint32_t)(*p++); - W[t] = tmp; - } - - for(; t < 80; t++) { - W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); - } - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - E = ctx->state[4]; - - for(t = 0; t < 80; t++) { - uint32_t tmp = rol(5,A) + E + W[t]; - - if (t < 20) - tmp += (D^(B&(C^D))) + 0x5A827999; - else if ( t < 40) - tmp += (B^C^D) + 0x6ED9EBA1; - else if ( t < 60) - tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; - else - tmp += (B^C^D) + 0xCA62C1D6; - - E = D; - D = C; - C = rol(30,B); - B = A; - A = tmp; - } - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; - ctx->state[4] += E; -} - -static const HASH_VTAB SHA_VTAB = { - SHA_init, - SHA_update, - SHA_final, - SHA_hash, - SHA_DIGEST_SIZE -}; - -void SHA_init(SHA_CTX* ctx) { - ctx->f = &SHA_VTAB; - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; - ctx->state[4] = 0xC3D2E1F0; - ctx->count = 0; -} - - -void SHA_update(SHA_CTX* ctx, const void* data, size_t len) { - unsigned int i = (unsigned int)(ctx->count & 63); - const uint8_t* p = (const uint8_t*)data; - - ctx->count += len; - - while (len--) { - ctx->buf[i++] = *p++; - if (i == 64) { - SHA1_Transform(ctx); - i = 0; - } - } -} - - -const uint8_t* SHA_final(SHA_CTX* ctx) { - uint8_t *p = ctx->buf; - uint64_t cnt = LITE_LShiftU64(ctx->count, 3); - int i; - - SHA_update(ctx, (uint8_t*)"\x80", 1); - while ((ctx->count & 63) != 56) { - SHA_update(ctx, (uint8_t*)"\0", 1); - } - for (i = 0; i < 8; ++i) { - uint8_t tmp = (uint8_t) LITE_RShiftU64(cnt, 56); - cnt = LITE_LShiftU64(cnt, 8); - SHA_update(ctx, &tmp, 1); - } - - for (i = 0; i < 5; i++) { - uint32_t tmp = ctx->state[i]; - *p++ = (uint8_t)(tmp >> 24); - *p++ = (uint8_t)(tmp >> 16); - *p++ = (uint8_t)(tmp >> 8); - *p++ = (uint8_t)(tmp >> 0); - } - - return ctx->buf; -} - -/* Convenience function */ -const uint8_t* SHA_hash(const void* data, size_t len, uint8_t* digest) { - SHA_CTX ctx; - SHA_init(&ctx); - SHA_update(&ctx, data, len); - memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE); - return digest; -} diff --git a/omaha/base/security/sha.h b/omaha/base/security/sha.h deleted file mode 100644 index 43867e2be..000000000 --- a/omaha/base/security/sha.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_BASE_SECURITY_SHA_H_ -#define OMAHA_BASE_SECURITY_SHA_H_ - -#include -#include "hash-internal.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -typedef HASH_CTX SHA_CTX; - -void SHA_init(SHA_CTX* ctx); -void SHA_update(SHA_CTX* ctx, const void* data, size_t len); -const uint8_t* SHA_final(SHA_CTX* ctx); - -// Convenience method. Returns digest address. -// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes. -const uint8_t* SHA_hash(const void* data, size_t len, uint8_t* digest); - -#define SHA_DIGEST_SIZE 20 - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // OMAHA_BASE_SECURITY_SHA_H_ diff --git a/omaha/base/shell.cc b/omaha/base/shell.cc index 95e8600ec..95ed81e43 100644 --- a/omaha/base/shell.cc +++ b/omaha/base/shell.cc @@ -46,7 +46,7 @@ HRESULT Shell::Execute(const TCHAR* file) { SEE_MASK_NOZONECHECKS | // Do not perform a zone check. SEE_MASK_NOASYNC; // Wait to complete before returning. // Pass NULL for hwnd. This will have ShellExecuteExEnsureParent() - // create a dummy parent window for us. + // create a default parent window for us. // sei.hwnd = NULL; sei.lpVerb = _T("open"); sei.lpFile = file; diff --git a/omaha/base/shutdown_handler.cc b/omaha/base/shutdown_handler.cc index 8c72af182..1dd492cfd 100644 --- a/omaha/base/shutdown_handler.cc +++ b/omaha/base/shutdown_handler.cc @@ -32,7 +32,7 @@ ShutdownHandler::ShutdownHandler() ShutdownHandler::~ShutdownHandler() { if (get(shutdown_event_)) { - VERIFY1(SUCCEEDED(reactor_->UnregisterHandle(get(shutdown_event_)))); + VERIFY_SUCCEEDED(reactor_->UnregisterHandle(get(shutdown_event_))); } } diff --git a/omaha/base/signatures.cc b/omaha/base/signatures.cc index 6c09ae96a..11b147a17 100644 --- a/omaha/base/signatures.cc +++ b/omaha/base/signatures.cc @@ -19,12 +19,7 @@ // signatures of buffers. #include "omaha/base/signatures.h" -#include #include -#pragma warning(disable : 4245) -// C4245 : conversion from 'type1' to 'type2', signed/unsigned mismatch -#include -#pragma warning(default : 4245) #include #include @@ -32,94 +27,19 @@ #include "omaha/base/debug.h" #include "omaha/base/error.h" #include "omaha/base/logging.h" -#include "omaha/base/security/sha256.h" -#include "omaha/base/security/sha.h" #include "omaha/base/string.h" #include "omaha/base/utils.h" namespace omaha { -constexpr ALG_ID kHashAlgorithm = CALG_SHA1; -constexpr DWORD kEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; -constexpr DWORD kCertificateNameType = CERT_NAME_SIMPLE_DISPLAY_TYPE; -constexpr DWORD kKeyPairType = AT_SIGNATURE; - // Maximum file size allowed for performing authentication. -constexpr size_t kMaxFileSizeForAuthentication = 512 * 1024 * 1024; // 512MB +constexpr size_t kMaxFileSizeForAuthentication = 1024 * 1024 * 1024; // 1GB. // Buffer size used to read files from disk. -constexpr size_t kFileReadBufferSize = 128 * 1024; +constexpr size_t kFileReadBufferSize = 1024 * 1024; // 1MB. namespace CryptDetails { -void crypt_release_context(HCRYPTPROV provider) { - UTIL_LOG(L3, (L"Releasing HCRYPTPROV 0x%08lx", provider)); - BOOL b = ::CryptReleaseContext(provider, 0 /*flags*/); - ASSERT(b, (L"")); -} - -void crypt_close_store(HCERTSTORE store) { - UTIL_LOG(L3, (L"Releasing HCERTSTORE 0x%08lx", store)); - BOOL b = ::CertCloseStore(store, 0 /*flags*/); - ASSERT(b, (L"")); - ASSERT(::GetLastError() != CRYPT_E_PENDING_CLOSE, (L"")); -} - -void crypt_free_certificate(PCCERT_CONTEXT certificate) { - UTIL_LOG(L3, (L"Releasing PCCERT_CONTEXT 0x%08lx", certificate)); - BOOL b = ::CertFreeCertificateContext(certificate); - ASSERT(b, (L"")); -} - -void crypt_destroy_key(HCRYPTKEY key) { - UTIL_LOG(L3, (L"Releasing HCRYPTKEY 0x%08lx", key)); - BOOL b = ::CryptDestroyKey(key); - ASSERT(b, (L"")); -} - -void crypt_destroy_hash(HCRYPTHASH hash) { - UTIL_LOG(L3, (L"Releasing HCRYPTHASH 0x%08lx", hash)); - BOOL b = ::CryptDestroyHash(hash); - ASSERT(b, (L"")); -} - -typedef close_fun smart_destroy_hash; -typedef scoped_any scoped_crypt_hash; - -// Providers implementing SHA256 can be instantiated using different names. -// On Vista and up, both the default provider and the enhanced RSA/AES -// provider support SHA256. On Windows XP, the named provider has a different -// name, therefore, the code falls back to a specific named provider in case -// of errors. -HRESULT CryptAcquireContextWithFallback(DWORD provider_type, - HCRYPTPROV* provider) { - const TCHAR* kHashCryptoProvider[] = { - NULL, // The default provider. - MS_ENH_RSA_AES_PROV, // The named provider for Vista and up. - MS_ENH_RSA_AES_PROV_XP // The named provider for XP SP3 and up. - }; - - // Try different providers until one of them succeeds. - HRESULT hr = S_OK; - CryptDetails::scoped_crypt_context scoped_csp_handle; - for (size_t i = 0; i != arraysize(kHashCryptoProvider); ++i) { - if (::CryptAcquireContext(address(scoped_csp_handle), - NULL, - kHashCryptoProvider[i], - provider_type, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - UTIL_LOG(L6, (_T("[CryptAcquireContext succeeded]"))); - *provider = release(scoped_csp_handle); - return S_OK; - } else { - hr = HRESULTFromLastError(); - UTIL_LOG(LE, (_T("[CryptAcquireContext failed][0x%08lX]"), hr)); - } - } - return hr; -} - class SHA256Hash : public HashInterface { public: SHA256Hash() { @@ -145,184 +65,12 @@ class SHA256Hash : public HashInterface { DISALLOW_COPY_AND_ASSIGN(SHA256Hash); }; -class SHA1Hash : public HashInterface { - public: - SHA1Hash() { - SHA_init(&ctx_); - } - virtual ~SHA1Hash() {} - - virtual void update(const void* data, unsigned int len) { - SHA_update(&ctx_, data, len); - } - - virtual const uint8_t* final() { - return SHA_final(&ctx_); - } - - virtual size_t hash_size() const { - return SHA_DIGEST_SIZE; - } - - private: - SHA_CTX ctx_; - - DISALLOW_COPY_AND_ASSIGN(SHA1Hash); -}; - -CryptDetails::HashInterface* CreateHasher(bool use_sha256) { - if (use_sha256) { - return new CryptDetails::SHA256Hash; - } else { - return new CryptDetails::SHA1Hash; - } +CryptDetails::HashInterface* CreateHasher() { + return new CryptDetails::SHA256Hash; } } // namespace CryptDetails -// Base64 encode/decode functions are part of ATL Server -HRESULT Base64::Encode(const std::vector& buffer_in, - std::vector* encoded, - bool break_into_lines) { - ASSERT(encoded, (L"")); - - if (buffer_in.empty()) { - encoded->clear(); - return S_OK; - } - - if (buffer_in.size() > INT_MAX) { - return E_INVALIDARG; - } - - int encoded_len = - Base64EncodeGetRequiredLength( - static_cast(buffer_in.size()), - break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF); - ASSERT(encoded_len > 0, (L"")); - - encoded->resize(encoded_len); - int str_out_len = encoded_len; - - BOOL result = Base64Encode( - &buffer_in.front(), - static_cast(buffer_in.size()), - reinterpret_cast(&encoded->front()), - &str_out_len, - break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF); - if (!result) - return E_FAIL; - ASSERT(str_out_len <= encoded_len, (L"")); - if (str_out_len < encoded_len) - encoded->resize(str_out_len); - - return S_OK; -} - -HRESULT Base64::Encode(const std::vector& buffer_in, - CStringA* encoded, - bool break_into_lines) { - ASSERT(encoded, (L"")); - - if (buffer_in.empty()) { - return S_OK; - } - - std::vector buffer_out; - RET_IF_FAILED(Encode(buffer_in, &buffer_out, break_into_lines)); - - if (buffer_out.size() > INT_MAX) { - return E_FAIL; - } - encoded->Append(reinterpret_cast(&buffer_out.front()), - static_cast(buffer_out.size())); - - return S_OK; -} - -HRESULT Base64::Encode(const std::vector& buffer_in, - CString* encoded, - bool break_into_lines) { - ASSERT(encoded, (L"")); - - CStringA string_out; - RET_IF_FAILED(Encode(buffer_in, &string_out, break_into_lines)); - *encoded = string_out; - - return S_OK; -} - -HRESULT Base64::Decode(const std::vector& encoded, - std::vector* buffer_out) { - ASSERT(buffer_out, (L"")); - - const size_t encoded_len = encoded.size(); - if (encoded_len > INT_MAX) { - return E_INVALIDARG; - } - - const int required_len = Base64DecodeGetRequiredLength( - static_cast(encoded_len)); - - buffer_out->resize(required_len); - - if (required_len == 0) { - return S_OK; - } - - int bytes_written = required_len; - BOOL result = Base64Decode(reinterpret_cast(&encoded.front()), - static_cast(encoded_len), - &buffer_out->front(), - &bytes_written); - if (!result) - return E_FAIL; - ASSERT(bytes_written <= required_len, (L"")); - if (bytes_written < required_len) { - buffer_out->resize(bytes_written); - } - - return S_OK; -} - -HRESULT Base64::Decode(const CStringA& encoded, std::vector* buffer_out) { - ASSERT(buffer_out, (L"")); - - size_t encoded_len = encoded.GetLength(); - std::vector buffer_in(encoded_len); - if (encoded_len != 0) { - memcpy(&buffer_in.front(), encoded.GetString(), encoded_len); - } - - return Decode(buffer_in, buffer_out); -} - -// Base64 in a CString -> binary -HRESULT Base64::Decode(const CString& encoded, std::vector* buffer_out) { - ASSERT(buffer_out, (L"")); - - CW2A encoded_a(encoded.GetString()); - - size_t encoded_len = ::strlen(encoded_a); - std::vector buffer_in(encoded_len); - if (encoded_len != 0) { - memcpy(&buffer_in.front(), encoded_a, encoded_len); - } - - return Decode(buffer_in, buffer_out); -} - -const size_t CryptoHash::kSha1HashSize = 20; -const size_t CryptoHash::kSha256HashSize = 32; - -CryptoHash::CryptoHash(HashAlgorithm hash_algorithm) - : use_sha256_(hash_algorithm == kSha256) { - ASSERT1(hash_algorithm == kSha1 || hash_algorithm == kSha256); -} - -CryptoHash::~CryptoHash() { -} - HRESULT CryptoHash::Compute(const TCHAR* filepath, uint64 max_len, std::vector* hash_out) { @@ -390,7 +138,7 @@ HRESULT CryptoHash::ComputeOrValidate(const std::vector& filepaths, static_assert(kFileReadBufferSize <= INT_MAX); std::unique_ptr hasher( - CryptDetails::CreateHasher(use_sha256_)); + CryptDetails::CreateHasher()); for (size_t i = 0; i < filepaths.size(); ++i) { scoped_hfile file_handle(::CreateFile(filepaths[i], @@ -413,7 +161,7 @@ HRESULT CryptoHash::ComputeOrValidate(const std::vector& filepaths, if (curr_len > max_len) { UTIL_LOG(LE, (_T("[exceed max len][curr_len=%lu][max_len=%lu]"), curr_len, max_len)); - return E_FAIL; + return SIGS_E_FILE_SIZE_TOO_BIG; } } @@ -434,7 +182,7 @@ HRESULT CryptoHash::ComputeOrValidate(const std::vector& filepaths, } DWORD digest_size = static_cast(hash_size()); - std::vector digest_data(digest_size); + std::vector digest_data(digest_size); memcpy(&digest_data.front(), hasher->final(), digest_size); @@ -444,13 +192,9 @@ HRESULT CryptoHash::ComputeOrValidate(const std::vector& filepaths, return S_OK; } - std::vector calculated_hash(digest_size); - memcpy(&calculated_hash.front(), &digest_data.front(), digest_size); CStringA base64_encoded_hash; - Base64::Encode(calculated_hash, &base64_encoded_hash, false); - CString hash = AnsiToWideString(base64_encoded_hash, - base64_encoded_hash.GetLength()); - REPORT_LOG(L1, (_T("[actual hash=%s]"), hash)); + Base64Escape(digest_data.data(), digest_size, &base64_encoded_hash, true); + REPORT_LOG(L1, (_T("[actual hash=%S]"), base64_encoded_hash)); return SIGS_E_INVALID_SIGNATURE; } else { hash_out->resize(digest_size); @@ -470,7 +214,7 @@ HRESULT CryptoHash::ComputeOrValidate(const std::vector& buffer_in, } std::unique_ptr hasher( - CryptDetails::CreateHasher(use_sha256_)); + CryptDetails::CreateHasher()); const size_t datalen = buffer_in.size(); const uint8* data = datalen ? &buffer_in.front() : NULL; @@ -492,683 +236,6 @@ HRESULT CryptoHash::ComputeOrValidate(const std::vector& buffer_in, } } -// To sign data you need a CSP with the proper private key installed. -// To get a signing certificate you start with a PFX file. This file -// encodes a "certificate store" which can hold more than one -// certificate. (In general it can hold a certificate chain, but we -// only use the signing certificate.) There are special APIs to verify -// the format of a PFX file and read it into a new certificate store. A -// password must be specified to read the PFX file as it is encrypted. -// The password was set when the PFX file was exported or otherwise -// created. Then you search for the proper certificate in the store -// (using the subject_name which tells who the certificate was issued -// to). Finally, to get a CSP with the certificate's private key -// available there is a special API, CryptAcquireCertificatePrivateKey, -// that takes a CSP and a certificate and makes the private key of the -// certificate the private key of the CSP. - -CryptoSigningCertificate::CryptoSigningCertificate() : key_spec_(0) { -} - -CryptoSigningCertificate::~CryptoSigningCertificate() { -} - -HRESULT CryptoSigningCertificate::ImportCertificate( - const TCHAR * filepath, - const TCHAR * password, - const TCHAR * subject_name) { - ASSERT(filepath, (L"")); - ASSERT(password, (L"")); - - std::vector buffer; - HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer); - if (FAILED(hr)) { - UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]" - L"['%s' not read, hr 0x%08lx]", filepath, hr)); - return hr; - } - return ImportCertificate(buffer, password, subject_name); -} - -HRESULT CryptoSigningCertificate::ImportCertificate( - const std::vector& certificate_in, - const TCHAR * password, - const TCHAR * subject_name) { - ASSERT(password, (L"")); - ASSERT1(!certificate_in.empty()); - - UTIL_LOG(L2, (L"[CryptoSigningCertificate::ImportCertificate]" - L"[%d bytes, subject_name '%s']", - certificate_in.size(), subject_name ? subject_name : L"")); - - if (certificate_in.size() > INT_MAX) { - return E_INVALIDARG; - } - - // CryptoAPI treats the certificate as a "blob" - CRYPT_DATA_BLOB blob; - blob.cbData = static_cast(certificate_in.size()); - blob.pbData = const_cast(&certificate_in.front()); - - // Ensure that it is PFX formatted - BOOL b = ::PFXIsPFXBlob(&blob); - if (!b) { - ASSERT(0, (L"Invalid PFX certificate, err 0x%08lx", ::GetLastError())); - return SIGS_E_INVALID_PFX_CERTIFICATE; - } - - // Make sure the password checks out - b = ::PFXVerifyPassword(&blob, password, 0 /* flags */); - if (!b) { - UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]" - L"[invalid password, err 0x%08lx]", ::GetLastError())); - return SIGS_E_INVALID_PASSWORD; - } - - // Do the import from the certificate to a new certificate store - // TODO(omaha): Check that this is in fact a new certificate store, not an - // existing one. If it is an existing one we'll need to delete the - // certificate later. - // The last parameter to ::PFXImportCertStore() is 0, indicating that we want - // the CSP to be "silent"; i.e., not prompt. - reset(store_, ::PFXImportCertStore(&blob, password, 0)); - if (!store_) { - DWORD err = ::GetLastError(); - ASSERT(0, (L"Failed to import PFX certificate into a certificate store, " - L"err 0x%08lx", err)); - return HRESULT_FROM_WIN32(err); - } - UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" - L"[new store 0x%08lx]", get(store_))); - - // Now that we have a store, look for the correct certificate. (There may - // have been more than one in the PFX file, e.g., a certificate chain.) - PCCERT_CONTEXT certificate_context = NULL; - while ((certificate_context = - ::CertEnumCertificatesInStore(get(store_), - certificate_context)) != NULL) { - // Have a certificate, does it look like the right one? Check the name - DWORD name_len = ::CertGetNameString(certificate_context, - kCertificateNameType, - 0 /*flags*/, - NULL, - NULL, - 0); - if (name_len <= 1) { - // Name attribute not found - should never happen - ASSERT(0, (L"CryptoSigningCertificate::ImportCertificate failed to get " - L"certificate name length, err 0x%08lx", ::GetLastError())); - continue; - } - // name_len includes the terminating null - - std::vector name; - name.resize(name_len); - ASSERT1(!name.empty()); - DWORD name_len2 = ::CertGetNameString(certificate_context, - kCertificateNameType, - 0, - NULL, - &name.front(), - name_len); - ASSERT(name_len2 == name_len, (L"")); - - UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" - L"[found '%s' in store]", &name.front())); - - // Check the name if the user so desires. (If subject_name == NULL then - // the first certificate found is used.) - if (subject_name && (0 != String_StrNCmp(&name.front(), - subject_name, - ::lstrlen(subject_name), - false))) { - // name mismatch - UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" - L"[not the right certificate, we're looking for '%s']", - subject_name)); - continue; - } - - // This is the right certificate - subject_name_ = &name.front(); - reset(certificate_, certificate_context); - UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]" - L"[new certificate 0x%08lx]", get(certificate_))); - break; - } - - return S_OK; -} - -HRESULT CryptoSigningCertificate::GetCSPContext(HCRYPTPROV* csp_context) { - ASSERT(csp_context, (L"")); - ASSERT(get(certificate_), (L"")); - - // CSP may have already been used - reset it - reset(csp_); - - // Create a CSP context using the private key of the certificate we imported - // earlier. - HCRYPTPROV csp = NULL; - BOOL must_free_csp = FALSE; - BOOL b = ::CryptAcquireCertificatePrivateKey(get(certificate_), - 0 /*flags*/, - 0 /*reserved*/, - &csp, - &key_spec_, - &must_free_csp); - if (!b) { - DWORD err = ::GetLastError(); - ASSERT(0, (L"CryptoSigningCertificate::GetCSPContext " - L"CryptAcquireCertificatePrivateKey failed, err 0x%08lx", err)); - return HRESULT_FROM_WIN32(err); - } - - // (Funky API returns a boolean which tells you whether it is your - // responsibility to delete the CSP context or not.) - if (must_free_csp) { - reset(csp_, csp); - } - if (get(csp_)) { - UTIL_LOG(L3, (L"[CryptoSigningCertificate::GetCSPContext new CSP 0x%08lx]", - get(csp_))); - } - - ASSERT(key_spec_ == AT_SIGNATURE || key_spec_ == AT_KEYEXCHANGE, (L"")); - if (key_spec_ != kKeyPairType) { - UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" - L"[requires a AT_SIGNATURE type key]")); - return SIGS_E_INVALID_KEY_TYPE; - } - -#ifdef _DEBUG - // Which CSP did we get? - char csp_name[256] = {0}; - DWORD csp_name_len = arraysize(csp_name); - b = ::CryptGetProvParam(csp, - PP_NAME, - reinterpret_cast(&csp_name[0]), - &csp_name_len, - 0 /*flags*/); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" - L"[error getting CSP name, err 0x%08lx]", err)); - } - DWORD csp_prov_type; - DWORD csp_prov_type_len = sizeof(csp_prov_type); - b = ::CryptGetProvParam(csp, - PP_PROVTYPE, - reinterpret_cast(&csp_prov_type), - &csp_prov_type_len, - 0 /*flags*/); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" - L"[error getting CSP provtype, err 0x%08lx]", err)); - } - char csp_container[256] = {0}; - DWORD csp_container_len = arraysize(csp_container); - b = ::CryptGetProvParam(csp, - PP_CONTAINER, - reinterpret_cast(&csp_container[0]), - &csp_container_len, - 0 /*flags*/); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]" - L"[error getting CSP current container name, err 0x%08lx]", - err)); - } - UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]" - L"[have CSP '%S' (provtype %d) key container '%S']", - csp_name, csp_prov_type, csp_container)); - // End of which CSP did we get -#endif - - *csp_context = csp; - - UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]" - L"[getting CSP with private key from certificate]" - L"[HCRYPTPROV 0x%08lx]", csp)); - - return S_OK; -} - -// To sign some data using CryptoAPI you first hash it into a hash -// object, then sign it using the CSP. The CSP needs to have the -// private key, of type AT_SIGNATURE, in it already, as it isn't a -// parameter of the CryptSignHash API. The CryptoSigningCertificate -// can provide such a CSP. - -CryptoComputeSignature::CryptoComputeSignature( - CryptoSigningCertificate* certificate) - : certificate_(certificate) { -} - -CryptoComputeSignature::~CryptoComputeSignature() { -} - -HRESULT CryptoComputeSignature::Sign(TCHAR const * const filepath, - uint32 max_len, - std::vector* signature_out) { - ASSERT(filepath, (L"")); - std::vector buffer; - HRESULT hr = ReadEntireFile(filepath, max_len, &buffer); - if (FAILED(hr)) { - UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" - L"['%s not read, hr 0x%08lx]", filepath, hr)); - return hr; - } - return Sign(buffer, signature_out); -} - -HRESULT CryptoComputeSignature::Sign(const std::vector& buffer_in, - std::vector* signature_out) { - ASSERT(signature_out, (L"")); - ASSERT1(!buffer_in.empty()); - - UTIL_LOG(L2, (L"[CryptoComputeSignature::Sign]" - L"[buffer of %d bytes]", buffer_in.size())); - - if (buffer_in.size() > INT_MAX) { - return E_INVALIDARG; - } - - // Get the proper CSP with the private key (certificate retains ownership) - HCRYPTPROV csp = NULL; - HRESULT hr = certificate_->GetCSPContext(&csp); - ASSERT(SUCCEEDED(hr) && csp, (L"")); - - // Hash the data - CryptDetails::scoped_crypt_hash hash; - BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash)); - if (!b) { - // hash is now invalid, but might not be NULL, so stomp on it - DWORD err = ::GetLastError(); - ASSERT(!hash, (L"")); - UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" - L"[could not create hash, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - UTIL_LOG(L3, (L"CryptoComputeSignature::Sign new hash 0x%08lx", get(hash))); - - b = ::CryptHashData(get(hash), - &buffer_in.front(), - static_cast(buffer_in.size()), - 0); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" - L"[could not hash data, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - - // Sign the hash (first get length, then allocate buffer and do real signing) - DWORD signature_len = 0; - b = ::CryptSignHash(get(hash), - kKeyPairType, - NULL, - 0 /*flags*/, - NULL, - &signature_len); - if (!b && ::GetLastError() != ERROR_MORE_DATA) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" - L"[could not compute size of signature, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - signature_out->resize(signature_len); - b = ::CryptSignHash(get(hash), - kKeyPairType, - NULL, - 0, - &signature_out->front(), - &signature_len); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]" - L"[could not compute signature, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - ASSERT(signature_len == signature_out->size(), (L"")); - - UTIL_LOG(L3, (L"[CryptoComputeSignature::Sign]" - L"[have %d byte signature]", signature_out->size())); - - return S_OK; -} - -// To verify signed data you need a CSP, and you also need the public -// key extracted from a certificate. The CSP can be any RSA CSP on the -// machine, the default one is fine. To get the public key you start -// by importing a certificate in standard "DER encoded" format. That -// returns a giant data structure, one field of which is the public key -// in a format that CryptoAPI understands. You import this public key -// into the CSP with the CryptImportPublicKey() API, and then create a -// key object from it suitable for use with the verification API. - -CryptoSignatureVerificationCertificate::CryptoSignatureVerificationCertificate() { // NOLINT -} - -CryptoSignatureVerificationCertificate::~CryptoSignatureVerificationCertificate() { // NOLINT -} - -HRESULT CryptoSignatureVerificationCertificate::ImportCertificate( - const TCHAR * filepath, - const TCHAR * subject_name) { - ASSERT(filepath, (L"")); - std::vector buffer; - HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer); - if (FAILED(hr)) { - UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" - L"['%s' not read, hr 0x%08lx]", filepath, hr)); - return hr; - } - return ImportCertificate(buffer, subject_name); -} - -HRESULT CryptoSignatureVerificationCertificate::ImportCertificate( - const std::vector& certificate_in, - const TCHAR * subject_name) { - // Import the certificate - ASSERT1(!certificate_in.empty()); - - if (certificate_in.size() > INT_MAX) { - return E_INVALIDARG; - } - - reset(certificate_, - ::CertCreateCertificateContext( - kEncodingType, - &certificate_in.front(), - static_cast(certificate_in.size()))); - if (!certificate_) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" - L"[could not import certificate, err 0x%08lx]", err)); - return SIGS_E_INVALID_DER_CERTIFICATE; - } - UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" - L"[new certificate 0x%08lx]", get(certificate_))); - - // Get certificate's subject name - DWORD name_len = ::CertGetNameString(get(certificate_), - kCertificateNameType, - 0 /*flags*/, - NULL, - NULL, - 0); - if (name_len <= 1) { - // Name attribute not found - should never happen - ASSERT(0, (L"CryptoSignatureVerificationCertificate failed to get " - L"certificate name length, err 0x%08lx", ::GetLastError())); - return E_FAIL; - } - // name_len includes the terminating NULL - - std::vector name; - name.resize(name_len); - ASSERT1(!name.empty()); - DWORD name_len2 = ::CertGetNameString(get(certificate_), - kCertificateNameType, - 0, - NULL, - &name.front(), - name_len); - ASSERT(name_len2 == name_len, (L"")); - - UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" - L"['%s' is subject of certificate]", &name.front())); - - subject_name_ = &name.front(); - - // Check the name if the user so desires. - if (subject_name && (0 != String_StrNCmp(&name.front(), - subject_name, - ::lstrlen(subject_name), false))) { - // name mismatch - UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]" - L"[not the right certificate, we're looking for '%s']", - subject_name)); - return E_FAIL; - } - - return S_OK; -} - -HRESULT CryptoSignatureVerificationCertificate::GetCSPContextAndKey( - HCRYPTPROV* csp_context, - HCRYPTKEY* public_key) { - ASSERT(csp_context, (L"")); - ASSERT(public_key, (L"")); - ASSERT(get(certificate_), (L"")); - - // Get the public key out of the certificate - PCERT_INFO cert_info = get(certificate_)->pCertInfo; - ASSERT(cert_info, (L"")); - PCERT_PUBLIC_KEY_INFO public_key_info = &cert_info->SubjectPublicKeyInfo; - ASSERT(public_key_info, (L"")); - - // Reset the CSP and key in case it has been used already - reset(key_); - reset(csp_); - - // Get the default CSP. With CRYPT_VERIFYCONTEXT don't need to worry - // about creating/destroying a key container. - // TODO(omaha): Why wasn't PROV_RSA_SIG available? Maybe looking for the - // default isn't a good idea? - const DWORD provider_type = PROV_RSA_FULL; - BOOL b = ::CryptAcquireContext(address(csp_), - NULL, - NULL, - provider_type, - CRYPT_VERIFYCONTEXT|CRYPT_SILENT); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[GetCSPContextAndKey]" - L"[failed to acquire CSP, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]" - L"[new CSP 0x%08lx]", get(csp_))); - - // Convert the public key in encoded form into a CryptoAPI HCRYPTKEY - b = ::CryptImportPublicKeyInfo(get(csp_), - kEncodingType, - public_key_info, - address(key_)); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[GetCSPContextAndKey]" - L"[failed to import public key, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]" - L"[new key 0x%08lx]", get(key_))); - - *csp_context = get(csp_); - *public_key = get(key_); - - return S_OK; -} - -// To verify the signature of some data using CryptoAPI you first hash -// it into a hash object, then verify it using the CSP and a public key. -// In this case the CryptVerifySignature takes the key (of type -// AT_SIGNATURE) as a separate parameter. The -// CryptoSignatureVerificationCertificate can provide the proper CSP and -// the public key from the certificate. - -CryptoVerifySignature::CryptoVerifySignature( - CryptoSignatureVerificationCertificate& certificate) // NOLINT - : certificate_(&certificate) { -} - -CryptoVerifySignature::~CryptoVerifySignature() { -} - -HRESULT CryptoVerifySignature::Validate(const TCHAR* filepath, - uint32 max_len, - const std::vector& signature_in) { - ASSERT(filepath, (L"")); - std::vector buffer; - HRESULT hr = ReadEntireFile(filepath, max_len, &buffer); - if (FAILED(hr)) { - UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]" - L"['%s' not read, hr 0x%08lx]", filepath, hr)); - return hr; - } - return Validate(buffer, signature_in); -} - -HRESULT CryptoVerifySignature::Validate(const std::vector& buffer_in, - const std::vector& signature_in) { - ASSERT(certificate_, (L"")); - ASSERT1(!buffer_in.empty()); - ASSERT1(!signature_in.empty()); - - UTIL_LOG(L2, (L"[CryptoVerifySignature::Validate]" - L"[buffer of %d bytes, signature of %d bytes]", - buffer_in.size(), signature_in.size())); - - if (buffer_in.size() > INT_MAX || signature_in.size() > INT_MAX) { - return E_INVALIDARG; - } - - // Get the CSP context and the public key from the certificate - HCRYPTPROV csp = NULL; - HCRYPTKEY key = NULL; - HRESULT hr = certificate_->GetCSPContextAndKey(&csp, &key); - ASSERT(SUCCEEDED(hr) && csp && key, (L"")); - - // Hash the data - CryptDetails::scoped_crypt_hash hash; - BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash)); - if (!b) { - // hash is now invalid, but might not be NULL, so stomp on it - DWORD err = ::GetLastError(); - ASSERT(!hash, (L"")); - UTIL_LOG(LE, (L"[CrypoVerifySignature::Validate]" - L"[could not create hash], err 0x%08lx", err)); - return HRESULT_FROM_WIN32(err); - } - - b = ::CryptHashData(get(hash), - &buffer_in.front(), - static_cast(buffer_in.size()), - 0 /*flags*/); - if (!b) { - DWORD err = ::GetLastError(); - UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]" - L"[could not hash data, err 0x%08lx]", err)); - return HRESULT_FROM_WIN32(err); - } - - // Verify the hash - b = ::CryptVerifySignature(get(hash), - &signature_in.front(), - static_cast(signature_in.size()), - key, - NULL, - 0 /*flags*/); - if (!b) { - DWORD err = ::GetLastError(); -#ifdef LOGGING - CString encoded_signature; - Base64::Encode(signature_in, &encoded_signature, false); - - UTIL_LOG(LE, (_T("CryptoVerifySignature::Validate could not ") - _T("verify signature, err 0x%08lx with sig \"%s\""), - err, encoded_signature)); -#endif - if (err == NTE_BAD_SIGNATURE) - return SIGS_E_INVALID_SIGNATURE; - else - return HRESULT_FROM_WIN32(err); - } - - return S_OK; -} - -HRESULT SignData(const TCHAR* certificate_path, - const TCHAR* certificate_password, - const TCHAR* certificate_subject_name, - const std::vector& data, - CString* signature_base64) { - ASSERT(certificate_path, (L"")); - ASSERT(certificate_password, (L"")); - // certificate_subject_name can be NULL - ASSERT(signature_base64, (L"")); - - CryptoSigningCertificate certificate; - RET_IF_FAILED(certificate.ImportCertificate(certificate_path, - certificate_password, - certificate_subject_name)); - - CryptoComputeSignature signer(&certificate); - std::vector signature; - RET_IF_FAILED(signer.Sign(data, &signature)); - RET_IF_FAILED(Base64::Encode(signature, signature_base64, false)); - - return S_OK; -} - -HRESULT VerifyData(const TCHAR* certificate_path, - const TCHAR* certificate_subject_name, - const std::vector& data, - const TCHAR* signature_base64) { - ASSERT(certificate_path, (L"")); - // certificate_subject_name can be NULL - ASSERT(signature_base64, (L"")); - - std::vector signature; - RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature)); - - CryptoSignatureVerificationCertificate certificate; - RET_IF_FAILED(certificate.ImportCertificate(certificate_path, - certificate_subject_name)); - - CryptoVerifySignature verifier(certificate); - RET_IF_FAILED(verifier.Validate(data, signature)); - - return S_OK; -} - -HRESULT VerifyData(const std::vector& certificate_buffer, - const TCHAR* certificate_subject_name, - const std::vector& data, - const TCHAR* signature_base64) { - // certificate_subject_name can be NULL - ASSERT(signature_base64, (L"")); - - std::vector signature; - RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature)); - - CryptoSignatureVerificationCertificate certificate; - RET_IF_FAILED(certificate.ImportCertificate(certificate_buffer, - certificate_subject_name)); - - CryptoVerifySignature verifier(certificate); - RET_IF_FAILED(verifier.Validate(data, signature)); - - return S_OK; -} - -HRESULT VerifyFileHash(const std::vector& files, - const CString& expected_hash) { - ASSERT1(!files.empty()); - - std::vector hash_vector; - RET_IF_FAILED(Base64::Decode(expected_hash, &hash_vector)); - - CryptoHash crypto(CryptoHash::kSha1); - if (!crypto.IsValidSize(hash_vector.size())) { - return E_INVALIDARG; - } - return crypto.Validate(files, kMaxFileSizeForAuthentication, hash_vector); -} - HRESULT VerifyFileHashSha256(const std::vector& files, const CString& expected_hash) { ASSERT1(!files.empty()); @@ -1178,7 +245,7 @@ HRESULT VerifyFileHashSha256(const std::vector& files, return E_INVALIDARG; } - CryptoHash crypto(CryptoHash::kSha256); + CryptoHash crypto; if (!crypto.IsValidSize(hash_vector.size())) { return E_INVALIDARG; } diff --git a/omaha/base/signatures.h b/omaha/base/signatures.h index 8a5b3f42a..f1c7dc3c1 100644 --- a/omaha/base/signatures.h +++ b/omaha/base/signatures.h @@ -27,34 +27,15 @@ #include #include #include "base/basictypes.h" +#include "omaha/base/security/sha256.h" #include "omaha/third_party/smartany/scoped_any.h" namespace omaha { class CryptoHash; -class CryptoComputeSignature; -class CryptoVerifySignature; -class CryptoSigningCertificate; -class CryptoSignatureVerificationCertificate; -// Useful scoped pointers for working with CryptoAPI objects namespace CryptDetails { -void crypt_close_store(HCERTSTORE); -void crypt_release_context(HCRYPTPROV); -void crypt_free_certificate(PCCERT_CONTEXT); -void crypt_destroy_key(HCRYPTKEY); - -typedef close_fun smart_close_store; // NOLINT -typedef close_fun smart_release_context; // NOLINT -typedef close_fun smart_free_certificate; // NOLINT -typedef close_fun smart_destroy_key; // NOLINT - -typedef scoped_any scoped_crypt_store; // NOLINT -typedef scoped_any scoped_crypt_context; // NOLINT -typedef scoped_any scoped_crypt_cert; // NOLINT -typedef scoped_any scoped_crypt_key; // NOLINT - class HashInterface { public: virtual ~HashInterface() {} @@ -64,52 +45,15 @@ class HashInterface { virtual size_t hash_size() const = 0; }; -HashInterface* CreateHasher(bool use_sha256); +HashInterface* CreateHasher(); } // namespace CryptDetails - -// A namespace for encoding binary into base64 (portable ASCII) representation -// and for decoding it again. -namespace Base64 { - -// Binary -> base64 in a buffer -HRESULT Encode(const std::vector& buffer_in, - std::vector* encoded, - bool break_into_lines = true); - -// Binary -> base64 in a string -HRESULT Encode(const std::vector& buffer_in, - CStringA* encoded, - bool break_into_lines = true); - -// Binary -> base64 in a wide string -HRESULT Encode(const std::vector& buffer_in, - CString* encoded, - bool break_into_lines = true); - -// Base64 in a buffer -> binary -HRESULT Decode(const std::vector& encoded, - std::vector* buffer_out); - -// Base64 in a CStringA -> binary -HRESULT Decode(const CStringA& encoded, std::vector* buffer_out); - -// Base64 in a CString -> binary -HRESULT Decode(const CString& encoded, std::vector* buffer_out); - -} // namespace Base64 - -// Compute and validate SHA-1 or SHA256 hashes of data. +// Compute and validate SHA256 hashes of data. class CryptoHash { public: - enum HashAlgorithm { - kSha1 = 0, - kSha256 - }; - - explicit CryptoHash(HashAlgorithm hash_algorithm); - ~CryptoHash(); + CryptoHash() = default; + ~CryptoHash() = default; // Hash a file HRESULT Compute(const TCHAR * filepath, @@ -144,7 +88,7 @@ class CryptoHash { } size_t hash_size() const { - return use_sha256_ ? kSha256HashSize : kSha1HashSize; + return SHA256_DIGEST_SIZE; } private: @@ -159,167 +103,9 @@ class CryptoHash { const std::vector* hash_in, std::vector* hash_out); - static const size_t kSha1HashSize; - static const size_t kSha256HashSize; - - const bool use_sha256_; - DISALLOW_COPY_AND_ASSIGN(CryptoHash); }; - -// Import and use a certificate for signing data (has a private key) -class CryptoSigningCertificate { - public: - CryptoSigningCertificate(); - ~CryptoSigningCertificate(); - - // Import certificate - with both public key and private key. - // Must be in PFX format. Password must unlock the PFX file. - // subject_name is the certificate's subject name (who it was - // issued to) - if not NULL then it is checked for an exact - // match against the certificate. - - // User can get the certificate in PFX format by following the procedure at - // http://support.globalsign.net/en/objectsign/transform.cfm - - HRESULT ImportCertificate(const TCHAR * filepath, - const TCHAR * password, - const TCHAR * subject_name); - - HRESULT ImportCertificate(const std::vector& certificate_in, - const TCHAR * password, - const TCHAR * subject_name); - - CString subject_name() { return subject_name_; } - - private: - static const int kMaxCertificateSize = 100000; - - CryptDetails::scoped_crypt_store store_; - CryptDetails::scoped_crypt_cert certificate_; - CryptDetails::scoped_crypt_context csp_; - CString subject_name_; - DWORD key_spec_; - - friend class CryptoComputeSignature; - // Get the CSP with the private key - // (CryptoSigningCertificate retains ownership of csp_context.) - HRESULT GetCSPContext(HCRYPTPROV* csp_context); - - DISALLOW_COPY_AND_ASSIGN(CryptoSigningCertificate); -}; - - -// Compute digital signatures -class CryptoComputeSignature { - public: - explicit CryptoComputeSignature(CryptoSigningCertificate* certificate); - ~CryptoComputeSignature(); - - // Sign a file, returning a separate signature - HRESULT Sign(const TCHAR * filepath, - uint32 max_len, - std::vector* signature_out); - - // Sign a chunk of memory, returning a separate signature - HRESULT Sign(const std::vector& buffer_in, - std::vector* signature_out); - - private: - // Does not take ownership of the certificate - CryptoSigningCertificate* const certificate_; - - DISALLOW_COPY_AND_ASSIGN(CryptoComputeSignature); -}; - - -// Import and use a certificate for verifying signatures (has public key only) -class CryptoSignatureVerificationCertificate { - public: - CryptoSignatureVerificationCertificate(); - ~CryptoSignatureVerificationCertificate(); - - // Import certificate - with only public key. Must be in DER (.cer) format. - // subject_name is the certificate's subject name (who it was - // issued to) - if not NULL then it is checked for an exact - // match against the certificate. - - // User can get certificate in DER format (.cer) by exporting from - // certmgr.exe, using openssl, etc.) - - HRESULT ImportCertificate(const TCHAR * filepath, - const TCHAR * subject_name); - HRESULT ImportCertificate(const std::vector& certificate_in, - const TCHAR * subject_name); - - CString subject_name() { return subject_name_; } - - private: - static const int kMaxCertificateSize = 100000; - - CryptDetails::scoped_crypt_cert certificate_; - CryptDetails::scoped_crypt_context csp_; - CryptDetails::scoped_crypt_key key_; - CString subject_name_; - - friend class CryptoVerifySignature; - // Get the CSP and the public key - // (CryptoSignatureVerificationCertificate retains ownership of csp_context - // and public_key.) - HRESULT GetCSPContextAndKey(HCRYPTPROV* csp_context, HCRYPTKEY* public_key); - - DISALLOW_COPY_AND_ASSIGN(CryptoSignatureVerificationCertificate); -}; - - -// Verify digital signatures -class CryptoVerifySignature { - public: - explicit CryptoVerifySignature( - CryptoSignatureVerificationCertificate& certificate); // NOLINT - ~CryptoVerifySignature(); - - // Validate signature of a file, signature given separately - HRESULT Validate(const TCHAR * filepath, - uint32 max_len, - const std::vector& signature_in); - - // Validate signature of a buffer of data, signature given separately - HRESULT Validate(const std::vector& buffer_in, - const std::vector& signature_in); - - private: - // Does not take ownership of the certificate - CryptoSignatureVerificationCertificate* const certificate_; - - DISALLOW_COPY_AND_ASSIGN(CryptoVerifySignature); -}; - -// All-in-one routine to sign a chunk of data and return the signature -// (encoded in base64) -HRESULT SignData(const TCHAR* certificate_path, - const TCHAR* certificate_password, - const TCHAR* certificate_subject_name, - const std::vector& data, - CString* signature_base64); - -// All-in-one routine to verify the signature of a chunk of data -HRESULT VerifyData(const TCHAR* certificate_path, - const TCHAR* certificate_subject_name, - const std::vector& data, - const TCHAR* signature_base64); - -HRESULT VerifyData(const std::vector& certificate_buffer, - const TCHAR* certificate_subject_name, - const std::vector& data, - const TCHAR* signature_base64); - -// Verifies that the files' SHA1 hash is the expected_hash. The hash is -// base64 encoded. -HRESULT VerifyFileHash(const std::vector& files, - const CString& expected_hash); - // Verifies that the files' SHA256 hash is the expected_hash. The hash is // hex-digit encoded. HRESULT VerifyFileHashSha256(const std::vector& files, diff --git a/omaha/base/signatures_unittest.cc b/omaha/base/signatures_unittest.cc index 6031046ea..562ab5402 100644 --- a/omaha/base/signatures_unittest.cc +++ b/omaha/base/signatures_unittest.cc @@ -35,30 +35,6 @@ namespace omaha { namespace { -struct { - const char* binary; - const char* base64; -} test_data[] = { - "", "", - "what", "d2hhdA==", - "what will print out", "d2hhdCB3aWxsIHByaW50IG91dA==", - "foobar", "Zm9vYmFy", - "a man, a plan, a canal: panama!", "YSBtYW4sIGEgcGxhbiwgYSBjYW5hbDogcGFuYW1hIQ==", // NOLINT -}; - -// This test data from http://en.wikipedia.org/wiki/SHA-1: -const struct { - const char* binary; - byte hash[20]; -} test_hash[] = { - "The quick brown fox jumps over the lazy dog", - 0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, - 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12, - "The quick brown fox jumps over the lazy cog", - 0xde, 0x9f, 0x2c, 0x7f, 0xd2, 0x5e, 0x1b, 0x3a, 0xfa, 0xd3, - 0xe8, 0x5a, 0x0b, 0xd1, 0x7d, 0x9b, 0x10, 0x0d, 0xb4, 0xb3, -}; - // This test data from http://en.wikipedia.org/wiki/SHA-2: struct { const char* binary; @@ -84,46 +60,8 @@ struct { } // namespace -TEST(SignaturesTest, Base64) { - for (size_t i = 0; i != arraysize(test_data); i++) { - std::vector buffer(strlen(test_data[i].binary)); - if (strlen(test_data[i].binary) != 0) { - memcpy(&buffer.front(), test_data[i].binary, strlen(test_data[i].binary)); - } - CStringA test_e; - ASSERT_SUCCEEDED(Base64::Encode(buffer, &test_e)); - ASSERT_STREQ(test_e, test_data[i].base64); - std::vector test_d; - uint32 test_d_written = 0; - ASSERT_SUCCEEDED(Base64::Decode(test_e, &test_d)); - ASSERT_EQ(test_d.size(), strlen(test_data[i].binary)); - if (strlen(test_data[i].binary) != 0) { - ASSERT_EQ(0, memcmp(&test_d.front(), - test_data[i].binary, - strlen(test_data[i].binary))); - } - } -} - -TEST(SignaturesTest, CryptoHash) { - CryptoHash chash(CryptoHash::kSha1); - for (size_t i = 0; i != arraysize(test_hash); i++) { - std::vector buffer(strlen(test_hash[i].binary)); - if (!buffer.empty()) { - memcpy(&buffer.front(), test_hash[i].binary, strlen(test_hash[i].binary)); - } - std::vector hash; - ASSERT_SUCCEEDED(chash.Compute(buffer, &hash)); - ASSERT_EQ(hash.size(), chash.hash_size()); - ASSERT_EQ(0, memcmp(&hash.front(), - test_hash[i].hash, - hash.size())); - ASSERT_SUCCEEDED(chash.Validate(buffer, hash)); - } -} - TEST(SignaturesTest, CryptoHashSha256) { - CryptoHash chash(CryptoHash::kSha256); + CryptoHash chash; for (size_t i = 0; i != arraysize(test_hash256); i++) { std::vector buffer(strlen(test_hash256[i].binary)); @@ -142,118 +80,6 @@ TEST(SignaturesTest, CryptoHashSha256) { } } -TEST(SignaturesTest, CreationVerification) { - CString module_directory = app_util::GetModuleDirectory(NULL); - ASSERT_FALSE(module_directory.IsEmpty()); - CString directory; - directory.Format(_T("%s\\unittest_support"), module_directory); - - CString encoded_cert_with_private_key_path; - encoded_cert_with_private_key_path.AppendFormat( - _T("%s\\certificate-with-private-key.pfx"), directory); - CString encoded_cert_without_private_key_path; - encoded_cert_without_private_key_path.AppendFormat( - _T("%s\\certificate-without-private-key.cer"), directory); - CString raw_test_data_path; - raw_test_data_path.AppendFormat(_T("%s\\declaration.txt"), directory); - - // Get cert with private key and cert without private key. - std::vector encoded_cert_with_private_key; - std::vector encoded_cert_without_private_key; - ASSERT_SUCCEEDED(ReadEntireFile(encoded_cert_with_private_key_path, - 0, - &encoded_cert_with_private_key)); - ASSERT_SUCCEEDED(ReadEntireFile(encoded_cert_without_private_key_path, - 0, - &encoded_cert_without_private_key)); - CString cert_password = _T("f00bar"); - CString cert_subject_name = _T("Unofficial Google Test"); - - // Get testdata. - std::vector raw_testdata; - ASSERT_SUCCEEDED(ReadEntireFile(raw_test_data_path, 0, &raw_testdata)); - - // Create a signing certificate. - CryptoSigningCertificate signing_certificate; - ASSERT_SUCCEEDED(signing_certificate.ImportCertificate( - encoded_cert_with_private_key, cert_password, cert_subject_name)); - - // Create a signature object and sign the test data. - std::vector signature; - CryptoComputeSignature signer(&signing_certificate); - ASSERT_SUCCEEDED(signer.Sign(raw_testdata, &signature)); - - // Create a validating certificate. - CryptoSignatureVerificationCertificate verification_certificate; - ASSERT_SUCCEEDED(verification_certificate.ImportCertificate( - encoded_cert_without_private_key, cert_subject_name)); - - // Create a signature object and verify the test data's signature. - CryptoVerifySignature verifier(verification_certificate); - ASSERT_SUCCEEDED(verifier.Validate(raw_testdata, signature)); - - // Mess up the signature and show it doesn't verify. - size_t mid = signature.size() / 2; - byte mid_byte = signature[mid]; - signature[mid] = ~mid_byte; - ASSERT_FAILED(verifier.Validate(raw_testdata, signature)); - - // Restore the signature, mess up the test data, and show it doesn't verify. - signature[mid] = mid_byte; - mid = raw_testdata.size() / 2; - mid_byte = raw_testdata[mid]; - raw_testdata[mid] = ~mid_byte; - ASSERT_FAILED(verifier.Validate(raw_testdata, signature)); -} - -TEST(SignaturesTest, VerifyFileHash) { - const CString executable_path(app_util::GetCurrentModuleDirectory()); - - const CString source_file1 = ConcatenatePath( - executable_path, - _T("unittest_support\\download_cache_test\\") - _T("{89640431-FE64-4da8-9860-1A1085A60E13}\\gears-win32-opt.msi")); - - const CString hash_file1 = _T("ImV9skETZqGFMjs32vbZTvzAYJU="); - - const CString source_file2 = ConcatenatePath( - executable_path, - _T("unittest_support\\download_cache_test\\") - _T("{7101D597-3481-4971-AD23-455542964072}\\livelysetup.exe")); - - const CString hash_file2 = _T("Igq6bYaeXFJCjH770knXyJ6V53s="); - - const CString hash_files = _T("e2uzy96jlusKbADl87zie6F5iwE="); - - const CString bad_hash = _T("sFzmoHgCbowEnioqVb8WanTYbhIabcde="); - - std::vector files; - - // Authenticate one file. - files.push_back(source_file1); - EXPECT_HRESULT_SUCCEEDED(VerifyFileHash(files, hash_file1)); - - // Incorrect hash. - EXPECT_EQ(SIGS_E_INVALID_SIGNATURE, VerifyFileHash(files, hash_file2)); - - // Bad hash. - EXPECT_EQ(E_INVALIDARG, VerifyFileHash(files, bad_hash)); - EXPECT_EQ(E_INVALIDARG, VerifyFileHash(files, _T(""))); - - // Authenticate two files. - files.push_back(source_file2); - EXPECT_HRESULT_SUCCEEDED(VerifyFileHash(files, hash_files)); - - // Round trip through CryptoHash::Compute to verify the hash of two files. - CryptoHash crypto(CryptoHash::kSha1); - std::vector hash_out; - EXPECT_HRESULT_SUCCEEDED(crypto.Compute(files, 0, &hash_out)); - - CStringA actual_hash_files; - EXPECT_HRESULT_SUCCEEDED(Base64::Encode(hash_out, &actual_hash_files)); - EXPECT_STREQ(hash_files, CString(actual_hash_files)); -} - TEST(SignaturesTest, VerifyFileHashSha256) { const CString executable_path(app_util::GetCurrentModuleDirectory()); @@ -293,7 +119,7 @@ TEST(SignaturesTest, VerifyFileHashSha256) { EXPECT_HRESULT_SUCCEEDED(VerifyFileHashSha256(files, hash_files)); // Round trip through CryptoHash::Compute to verify the hash of two files. - CryptoHash crypto(CryptoHash::kSha256); + CryptoHash crypto; std::vector hash_out; EXPECT_HRESULT_SUCCEEDED(crypto.Compute(files, 0, &hash_out)); diff --git a/omaha/base/signaturevalidator.cc b/omaha/base/signaturevalidator.cc index 62e800b1b..8f7965cbe 100644 --- a/omaha/base/signaturevalidator.cc +++ b/omaha/base/signaturevalidator.cc @@ -37,7 +37,6 @@ namespace omaha { namespace { -const LPCTSTR kEmptyStr = _T(""); const DWORD kCertificateEncoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; // Gets a handle to the certificate store and optionally the cryptographic @@ -350,7 +349,6 @@ void CertList::FindFirstCert(const CertInfo** result_cert_info, const std::vector& company_name_to_match, const CString &orgn_unit_to_match, const CString &trust_authority_to_match, - bool allow_test_variant, bool check_cert_is_valid_now) const { if (!result_cert_info) { return; @@ -362,17 +360,12 @@ void CertList::FindFirstCert(const CertInfo** result_cert_info, cert_iter != cert_list_.end(); ++cert_iter) { // Find a match between the name of the certificate and one of the - // company names provided as a paramer.. + // company names provided as a parameter. bool names_match = false; for (size_t i = 0; i != company_name_to_match.size(); ++i) { const TCHAR* certificate_company_name = (*cert_iter)->issuing_company_name_; names_match = company_name_to_match[i] == certificate_company_name; - if (!names_match && allow_test_variant) { - CString test_variant = company_name_to_match[i]; - test_variant += _T(" (TEST)"); - names_match = test_variant == certificate_company_name; - } if (names_match) { break; } @@ -400,6 +393,7 @@ void CertList::FindFirstCert(const CertInfo** result_cert_info, } void ExtractAllCertificatesFromSignature(const wchar_t* signed_file, + const wchar_t* subject_name, CertList* cert_list) { if ((!signed_file) || (!cert_list)) return; @@ -422,9 +416,10 @@ void ExtractAllCertificatesFromSignature(const wchar_t* signed_file, if (succeeded && (cert_store != NULL)) { PCCERT_CONTEXT cert_context_ptr = NULL; - while ((cert_context_ptr = - CertEnumCertificatesInStore(cert_store, cert_context_ptr)) - != NULL) { + while ((cert_context_ptr = ::CertFindCertificateInStore( + cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + !subject_name ? CERT_FIND_ANY : CERT_FIND_SUBJECT_STR, + subject_name, cert_context_ptr)) != NULL) { CertInfo* cert_info = new CertInfo(cert_context_ptr); cert_list->AddCertificate(cert_info); } @@ -440,11 +435,10 @@ void ExtractAllCertificatesFromSignature(const wchar_t* signed_file, // VerifyAuthenticodeSignature that adds WTD_LIFETIME_SIGNING_FLAG. HRESULT VerifyCertificate(const wchar_t* signed_file, const std::vector& subject, - bool allow_test_variant, bool check_cert_is_valid_now, const std::vector* expected_hashes) { CertList cert_list; - ExtractAllCertificatesFromSignature(signed_file, &cert_list); + ExtractAllCertificatesFromSignature(signed_file, NULL, &cert_list); if (cert_list.size() == 0) { return GOOPDATE_E_SIGNATURE_NOT_SIGNED; } @@ -454,7 +448,6 @@ HRESULT VerifyCertificate(const wchar_t* signed_file, subject, CString(), CString(), - allow_test_variant, check_cert_is_valid_now); if (required_cert == NULL) { return GOOPDATE_E_SIGNATURE_NOT_TRUSTED_SUBJECT; diff --git a/omaha/base/signaturevalidator.h b/omaha/base/signaturevalidator.h index b850c8d19..d691af61d 100644 --- a/omaha/base/signaturevalidator.h +++ b/omaha/base/signaturevalidator.h @@ -166,13 +166,11 @@ class CertList { } // FindFirstCert() finds the first certificate that exactly matches the given - // criteria. If allow_test_variant is true, the company name will also be - // deemed valid if it equals company_name_to_match + " (TEST)". + // criteria. void FindFirstCert(const CertInfo** result_cert_info, const std::vector& company_name_to_match, const CString &orgn_unit_to_match, const CString &trust_authority_to_match, - bool allow_test_variant, bool check_cert_is_valid_now) const; typedef std::vector CertInfoList; @@ -181,16 +179,16 @@ class CertList { CertInfoList cert_list_; }; - // ExtractAllCertificatesFromSignature() takes in a signed file, extracts all // the certificates related to its signature and returns them in a CertList -// object. +// object. `subject_name` can be used to narrow the list of certificates to only +// those that match the given subject string. void ExtractAllCertificatesFromSignature(const wchar_t* signed_file, + const wchar_t* subject_name, CertList* cert_list); // Returns true if the subject of the certificate exactly matches the first CN -// name. If the 'allow_test_variant' parameter is true, the function tries to -// match the "subject (TEST)" string. +// name. // // The function enforces an additional check against the public key of the // certificate. Pinning to specific public keys mitigates the risk of accepting @@ -201,7 +199,6 @@ void ExtractAllCertificatesFromSignature(const wchar_t* signed_file, // call. HRESULT VerifyCertificate(const wchar_t* signed_file, const std::vector& subject, - bool allow_test_variant, bool check_cert_is_valid_now, const std::vector* expected_hashes); diff --git a/omaha/base/signaturevalidator_unittest.cc b/omaha/base/signaturevalidator_unittest.cc index a3e363ea2..f5c0aed4e 100644 --- a/omaha/base/signaturevalidator_unittest.cc +++ b/omaha/base/signaturevalidator_unittest.cc @@ -27,98 +27,83 @@ namespace omaha { namespace { -bool VerifySigneeIsGoogle(const wchar_t* signed_file) { +const TCHAR* const kTestCertificateSubjectName = _T("Google Inc (TEST)"); + +bool VerifySigneeIs(const wchar_t* subject_name, + const wchar_t* signed_file) { std::vector subject; - subject.push_back(kSha256CertificateSubjectName); - subject.push_back(kCertificateSubjectName); + subject.push_back(subject_name); return SUCCEEDED( VerifyCertificate(signed_file, subject, - true, // Allow test variant. false, // Check certificate is valid now. NULL)); } } // namespace -// Checks Omaha Thawte certificate sha1 (11/28/2016 to 11/21/2019). -TEST(CertInfoTest, CertInfo) { - const TCHAR kRelativePath[] = - _T("unittest_support\\sha1_14F8FDD167F92402B1570B5DC495C815.sys"); - - CString executable_full_path(app_util::GetCurrentModuleDirectory()); - ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), - kRelativePath)); - ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); - - CertList cert_list; - ExtractAllCertificatesFromSignature(executable_full_path, &cert_list); - - // ExtractAllCertificatesFromSignature() gets the certificate chain for the - // first signature and the certificate chain for the corresponding timestamp, - // excluding the root certificates. - // The following certificates are enumerated from SaveArguments.exe signed - // Thursday, April 14, 2016 3:57:37 PM: - // * "Google Inc" hash 1a6ac0549a4a44264deb6ff003391da2f285b19f. - // * "Thawte Code Signing CA - G2" hash - // 808d62642b7d1c4a9a83fd667f7a2a9d243fb1c7. - // * "COMODO SHA-1 Time Stamping Signer" hash - // 03a5b14663eb12023091b84a6d6a68bc871de66b. - EXPECT_EQ(3, cert_list.size()); - - const CertInfo* cert_info = NULL; - std::vector subject; - subject.push_back(kCertificateSubjectName); - cert_list.FindFirstCert(&cert_info, - subject, - CString(), - CString(), - false, // Do not allow test variant. - true); // Check if the certificate is valid now. - ASSERT_TRUE(cert_info); - - EXPECT_STREQ(kCertificateSubjectName, cert_info->issuing_company_name_); - EXPECT_STREQ(kCertificateThumbprint, cert_info->thumbprint_); - EXPECT_STREQ(kCertificatePublicKeyHash, cert_info->public_key_hash_); -} - -// Checks Chrome certificate sha256 (11/06/2018 to 11/17/2021). -TEST(CertInfoTest, CertInfo_Sha256) { - const TCHAR kRelativePath[] = - _T("unittest_support\\chrome_setup.exe"); - - CString executable_full_path(app_util::GetCurrentModuleDirectory()); - ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), - kRelativePath)); - ASSERT_TRUE(File::Exists(executable_full_path)); +struct PathSubjectThumbprintHash { + PathSubjectThumbprintHash() = default; + PathSubjectThumbprintHash(const CString& path, const CString& subject, + const CString& thumbprint, const CString& hash) + : relative_path(path), + subject_name(subject), + certificate_thumbprint(thumbprint), + public_key_hash(hash) {} + PathSubjectThumbprintHash(const PathSubjectThumbprintHash&) = default; + PathSubjectThumbprintHash& operator=(const PathSubjectThumbprintHash&) = + default; + + const CString relative_path; + const CString subject_name; + const CString certificate_thumbprint; + const CString public_key_hash; +}; + +class CertInfoTest + : public ::testing::TestWithParam {}; + +INSTANTIATE_TEST_CASE_P( + PathSubjectThumbprintHash, CertInfoTest, + ::testing::Values( + // Omaha certificate sha1 (11/07/2019 to 11/16/2022). + PathSubjectThumbprintHash( + _T("unittest_support\\sha1_06aea76bac46a9e8cfe6d29e45aaf033.sys"), + kSha1CertificateSubjectName, kCertificateThumbprint, + kCertificatePublicKeyHash), + // Google LLC sha256 certificate valid from 07-01-2021 to 07-10-2024. + PathSubjectThumbprintHash( + _T("unittest_support\\sha2_0e4418e2dede36dd2974c3443afb5ce5.msi"), + kSha256CertificateSubjectName, + kSha256CertificateThumbprint, + kSha256CertificatePublicKeyHash))); + +TEST_P(CertInfoTest, CertInfo) { + CString binary_full_path(app_util::GetCurrentModuleDirectory()); + ASSERT_TRUE(::PathAppend(CStrBuf(binary_full_path, MAX_PATH), + GetParam().relative_path)); + ASSERT_TRUE(File::Exists(binary_full_path)); + EXPECT_TRUE(VerifySigneeIs(GetParam().subject_name, binary_full_path)); CertList cert_list; - ExtractAllCertificatesFromSignature(executable_full_path, &cert_list); + ExtractAllCertificatesFromSignature(binary_full_path, GetParam().subject_name, + &cert_list); - // For some reason, extracting all certificates from the file only yields - // the code signing certificates but skips the time stamping certificates. - // In the case of chrome_setup.exe the following certs are picked up: - // * Dummy certificate. - // * DigiCert SHA2 Assured ID Code Signing CA - // hash 92c1588e85af2201ce7915e8538b492f605b80c6 - // * Google LLC hash cb7e84887f3c6015fe7edfb4f8f36df7dc10590e - EXPECT_EQ(3, cert_list.size()); + EXPECT_EQ(1, cert_list.size()); const CertInfo* cert_info = NULL; std::vector subject; - subject.push_back(kSha256CertificateSubjectName); + subject.push_back(GetParam().subject_name); cert_list.FindFirstCert(&cert_info, subject, CString(), CString(), - false, // Do not allow test variant. - true); // Check if the certificate is valid now. + /*check_cert_is_valid_now*/ false); ASSERT_TRUE(cert_info); - EXPECT_STREQ(kSha256CertificateSubjectName, cert_info->issuing_company_name_); - EXPECT_STREQ(kSha256CertificateThumbprint, cert_info->thumbprint_); - EXPECT_STREQ(kSha256CertificatePublicKeyHash, cert_info->public_key_hash_); + EXPECT_STREQ(GetParam().subject_name, cert_info->issuing_company_name_); + EXPECT_STREQ(GetParam().certificate_thumbprint, cert_info->thumbprint_); + EXPECT_STREQ(GetParam().public_key_hash, cert_info->public_key_hash_); } TEST(SignatureValidatorTest, VerifySigneeIsGoogle_OfficiallySigned) { @@ -128,7 +113,8 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_OfficiallySigned) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kSha1CertificateSubjectName, + executable_full_path)); } // Tests a certificate subject containing multiple CNs such as: @@ -142,7 +128,8 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_MultipleCN) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kTestCertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, @@ -154,7 +141,8 @@ TEST(SignatureValidatorTest, ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kTestCertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, VerifySigneeIsGoogle_OmahaTestSigned) { @@ -165,7 +153,8 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_OmahaTestSigned) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kTestCertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, VerifySigneeIsGoogle_Sha256) { @@ -176,13 +165,15 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_Sha256) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kLegacyCertificateSubjectName, + executable_full_path)); executable_full_path = app_util::GetCurrentModuleDirectory(); ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), _T("unittest_support\\chrome_setup.exe"))); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kSha256CertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, VerifySigneeIsGoogle_DualSigned_Sha1AndSha256) { @@ -192,20 +183,22 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_DualSigned_Sha1AndSha256) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kLegacyCertificateSubjectName, + executable_full_path)); } // The certificate was valid when it was used to sign the executable, but it has // since expired. TEST(SignatureValidatorTest, VerifySigneeIsGoogle_SignedWithNowExpiredCert) { const TCHAR kRelativePath[] = - _T("unittest_support\\GoogleUpdate_now_expired_cert.exe"); + _T("unittest_support\\") MAIN_EXE_BASE_NAME _T("_now_expired_cert.exe"); CString executable_full_path(app_util::GetCurrentModuleDirectory()); ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_TRUE(VerifySigneeIs(kLegacyCertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_NoCN) { @@ -216,7 +209,8 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_NoCN) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_FALSE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_FALSE(VerifySigneeIs(kLegacyCertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_WrongCN) { @@ -227,13 +221,14 @@ TEST(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_WrongCN) { ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH), kRelativePath)); ASSERT_TRUE(File::Exists(executable_full_path)); - EXPECT_FALSE(VerifySigneeIsGoogle(executable_full_path)); + EXPECT_FALSE(VerifySigneeIs(kLegacyCertificateSubjectName, + executable_full_path)); } TEST(SignatureValidatorTest, VerifyAuthenticodeSignature) { const TCHAR* kFileNamesToVerify[] = { - _T("GoogleUpdate_now_expired_cert.exe"), - _T("GoogleUpdate_old_signature.exe"), + MAIN_EXE_BASE_NAME _T("_now_expired_cert.exe"), + MAIN_EXE_BASE_NAME _T("_old_signature.exe"), _T("SaveArguments.exe"), _T("chrome_setup.exe"), _T("sha2_0c15be4a15bb0903c901b1d6c265302f.msi"), diff --git a/omaha/base/string.cc b/omaha/base/string.cc index 42d2908bd..03fdfe553 100644 --- a/omaha/base/string.cc +++ b/omaha/base/string.cc @@ -25,7 +25,6 @@ #include "omaha/base/commontypes.h" #include "omaha/base/debug.h" -#include "omaha/base/localization.h" #include "omaha/base/logging.h" #include "omaha/base/safe_format.h" @@ -86,12 +85,6 @@ bool IsSpaceA(char c) { return spaces[static_cast(c)] == 1; } -int TrimCString(CString &s) { - int len = Trim(s.GetBuffer()); - s.ReleaseBufferSetLength(len); - return len; -} - void MakeLowerCString(CString & s) { int len = s.GetLength(); String_FastToLower(s.GetBuffer()); @@ -134,39 +127,6 @@ void TrimString(CString& s, const TCHAR* delimiters) { s = s.Trim(delimiters); } -// Strip the first token from the front of argument s. A token is a -// series of consecutive non-blank characters - unless the first -// character is a double-quote ("), in that case the token is the full -// quoted string -CString StripFirstQuotedToken(const CString& s) { - const int npos = -1; - - // Make a writeable copy - CString str(s); - - // Trim any surrounding blanks (and tabs, for the heck of it) - TrimString(str, L" \t"); - - // Too short to have a second token - if (str.GetLength() <= 1) - return L""; - - // What kind of token are we stripping? - if (str[0] == L'\"') { - // Remove leading quoting string - int i = str.Find(L"\"", 1); - if (i != npos) - i++; - return str.Mid(i); - } else { - // Remove leading token - int i = str.FindOneOf(L" \t"); - if (i != npos) - i++; - return str.Mid(i); - } -} - // A block of text to separate lines, and back void TextToLines(const CString& text, const TCHAR* delimiter, std::vector* lines) { ASSERT(delimiter, (L"")); @@ -216,53 +176,6 @@ void LinesToText(const std::vector& lines, const TCHAR* delimiter, CStr } } -int CleanupWhitespaceCString(CString &s) { - int len = CleanupWhitespace(s.GetBuffer()); - s.ReleaseBufferSetLength(len); - return len; -} - -int CleanupWhitespace(TCHAR *str) { - ASSERT(str, (L"")); - - TCHAR *src = str; - TCHAR *dest = str; - int num_spaces = 0; - bool at_start = true; - while (true) { - // At end of string? - TCHAR c = *src; - if (0 == c) - break; - - // Look for whitespace; copy it over if not whitespace - if (IsSpace(c)) { - ++num_spaces; - } - else { - *dest++ = c; - at_start = false; - num_spaces = 0; - } - - // Write only first consecutive space (but skip space at start) - if (1 == num_spaces && !at_start) - *dest++ = ' '; - - ++src; - } - - // Remove trailing space, if any - if (dest > str && *(dest - 1) == L' ') - --dest; - - // 0-terminate - *dest = 0; - - // TODO(portability): cast is unsafe. - return static_cast(dest - str); -} - // Take 1 single hexadecimal "digit" (as a character) and return its decimal value // Returns -1 if given invalid hex digit int HexDigitToDec(const TCHAR digit) { @@ -320,20 +233,6 @@ WCHAR *ToWide (const char *s, int len) { return w; } -const byte *BufferContains (const byte *buf, uint32 buf_len, const byte *data, uint32 data_len) { - ASSERT(data, (L"")); - ASSERT(buf, (L"")); - - for (uint32 i = 0; i < buf_len; i++) { - uint32 j = i; - uint32 k = 0; - uint32 len = 0; - while (j < buf_len && k < data_len && buf[j++] == data[k++]) { len++; } - if (len == data_len) { return buf + i; } - } - return 0; -} - // Converting the Ansi Multibyte String into unicode string. The multibyte // string is encoded using the specified codepage. // The code is pretty much like the U2W function, except the codepage can be @@ -474,23 +373,6 @@ CString Utf8BufferToWideChar(const std::vector& buffer) { return result; } -CString AbbreviateString (const CString & title, int32 max_len) { - ASSERT (max_len, (L"")); - CString s(title); - TrimCString(s); // remove whitespace at start/end - if (s.GetLength() > max_len) { - s = s.Left (max_len - 2); - CString orig(s); - // remove partial words - while (s.GetLength() > 1 && !IsSpace(s[s.GetLength()-1])) { s = s.Left (s.GetLength() - 1); } - // but not if it would make the string very short - if (s.GetLength() < max_len / 2) { s = orig; } - s += _T(".."); - } - - return s; -} - CString GetAbsoluteUri(const CString& uri) { int i = String_FindString(uri, _T("://")); if (i==-1) return uri; @@ -556,82 +438,6 @@ CString GetUriHostNameHostOnly(const CString& uri, bool strip_leading) { return ss.Left(j); } -CString AbbreviateUri(const CString& uri, int32 max_len) { - ASSERT1(max_len); - ASSERT1(!uri.IsEmpty()); - - CString s(uri); - VERIFY1(String_FindString (s, _T("://"))); - - TrimCString(s); - // SKIP_LOC_BEGIN - RemoveFromStart (s, _T("ftp://"), false); - RemoveFromStart (s, _T("http://"), false); - RemoveFromStart (s, _T("https://"), false); - RemoveFromStart (s, _T("www."), false); - RemoveFromStart (s, _T("ftp."), false); - RemoveFromStart (s, _T("www-"), false); - RemoveFromStart (s, _T("ftp-"), false); - RemoveFromEnd (s, _T(".htm")); - RemoveFromEnd (s, _T(".html")); - RemoveFromEnd (s, _T(".asp")); - // SKIP_LOC_END - if (s.GetLength() > max_len) { - // try to keep the portion after the last / - int32 last_slash = s.ReverseFind ((TCHAR)'/'); - CString after_last_slash; - if (last_slash == -1) { after_last_slash = _T(""); } - else { after_last_slash = s.Right (uri.GetLength() - last_slash - 1); } - if (after_last_slash.GetLength() > max_len / 2) { - after_last_slash = after_last_slash.Right (max_len / 2); - } - s = s.Left (max_len - after_last_slash.GetLength() - 2); - s += ".."; - s += after_last_slash; - } - return s; -} - -// normalized version of a URI intended to map duplicates to the same string -// the normalized URI is not a valid URI -CString NormalizeUri (const CString & uri) { - CString s(uri); - TrimCString(s); - MakeLowerCString(s); - // SKIP_LOC_BEGIN - ReplaceCString (s, _T(":80"), _T("")); - - RemoveFromEnd (s, _T("/index.html")); - RemoveFromEnd (s, _T("/welcome.html")); // old netscape standard - RemoveFromEnd (s, _T("/")); - - RemoveFromStart (s, _T("ftp://"), false); - RemoveFromStart (s, _T("http://"), false); - RemoveFromStart (s, _T("https://"), false); - RemoveFromStart (s, _T("www."), false); - RemoveFromStart (s, _T("ftp."), false); - RemoveFromStart (s, _T("www-"), false); - RemoveFromStart (s, _T("ftp-"), false); - - ReplaceCString (s, _T("/./"), _T("/")); - // SKIP_LOC_END - - // TODO(omaha): - // fixup URLs like a/b/../../c - // while ($s =~ m!\/\.\.\!!) { - // $s =~ s!/[^/]*/\.\./!/!; - // } - - // TODO(omaha): - // unescape characters - // Note from RFC1630: "Sequences which start with a percent sign - // but are not followed by two hexadecimal characters are reserved - // for future extension" - // $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str; - - return s; -} - CString RemoveInternetProtocolHeader (const CString& url) { int find_colon_slash_slash = String_FindString(url, NOTRANSL(L"://")); if( find_colon_slash_slash != -1 ) { @@ -662,14 +468,6 @@ HRESULT ConvertFileUriToLocalPath(const CString& uri, CString* path_out) { return hr; } -void RemoveFromStart (CString & s, const TCHAR* remove, bool ignore_case) { - ASSERT(remove, (L"")); - - // Remove the characters if it is the prefix - if (String_StartsWith(s, remove, ignore_case)) - s.Delete(0, lstrlen(remove)); -} - bool String_EndsWith(const TCHAR *str, const TCHAR *end_str, bool ignore_case) { ASSERT(end_str, (L"")); ASSERT(str, (L"")); @@ -715,75 +513,6 @@ CString String_MakeEndWith(const TCHAR* str, const TCHAR* end_str, bool ignore_c } } -void RemoveFromEnd (CString & s, const TCHAR* remove) { - ASSERT(remove, (L"")); - - // If the suffix is shorter than the string, don't bother - int remove_len = lstrlen(remove); - if (s.GetLength() < remove_len) return; - - // If the suffix is equal - int suffix_begin = s.GetLength() - remove_len; - if (0 == lstrcmp(s.GetString() + suffix_begin, remove)) - s.Delete(suffix_begin, remove_len); -} - -CString ElideIfNeeded (const CString & input_string, int max_len, int min_len) { - ASSERT (min_len <= max_len, (L"")); - ASSERT (max_len >= TSTR_SIZE(kEllipsis)+1, (L"")); - ASSERT (min_len >= TSTR_SIZE(kEllipsis)+1, (L"")); - - CString s = input_string; - - s.TrimRight(); - if (s.GetLength() > max_len) { - int truncate_at = max_len - TSTR_SIZE(kEllipsis); - // find first space going backwards from character one after the truncation point - while (truncate_at >= min_len && !IsSpace(s.GetAt(truncate_at))) - truncate_at--; - - // skip the space(s) - while (truncate_at >= min_len && IsSpace(s.GetAt(truncate_at))) - truncate_at--; - - truncate_at++; - - if (truncate_at <= min_len || truncate_at > (max_len - static_cast(TSTR_SIZE(kEllipsis)))) { - // we weren't able to break at a word boundary, may as well use more of the string - truncate_at = max_len - TSTR_SIZE(kEllipsis); - - // skip space(s) - while (truncate_at > 0 && IsSpace(s.GetAt(truncate_at-1))) - truncate_at--; - } - - s = s.Left(truncate_at); - s += kEllipsis; - } - - UTIL_LOG(L6, (L"elide (%d %d) %s -> %s", min_len, max_len, input_string, s)); - return s; -} - -// these functions untested -// UTF8 parameter supported on XP/2000 only -HRESULT AnsiToUTF8 (char * src, int src_len, char * dest, int *dest_len) { - ASSERT (dest_len, (L"")); - ASSERT (dest, (L"")); - ASSERT (src, (L"")); - - // First use MultiByteToWideChar(CP_UTF8, ...) to convert to Unicode - // then use WideCharToMultiByte to convert from Unicode to UTF8 - WCHAR *unicode = new WCHAR [(src_len + 1) * sizeof (TCHAR)]; ASSERT (unicode, (L"")); - int chars_written = MultiByteToWideChar (CP_ACP, 0, src, src_len, unicode, src_len); - ASSERT (chars_written == src_len, (L"")); - const char *unmappable = " "; - BOOL unmappable_characters = false; - *dest_len = WideCharToMultiByte (CP_UTF8, 0, unicode, chars_written, dest, *dest_len, unmappable, &unmappable_characters); - delete [] unicode; - return S_OK; -} - // Convert Wide to ANSI directly. Use only when it is all ANSI CStringA WideToAnsiDirect(const CString & in) { int in_len = in.GetLength(); @@ -799,330 +528,6 @@ CStringA WideToAnsiDirect(const CString & in) { return out; } -HRESULT UCS2ToUTF8 (LPCWSTR src, int src_len, char * dest, int *dest_len) { - ASSERT(dest_len, (L"")); - ASSERT(dest, (L"")); - - *dest_len = WideCharToMultiByte (CP_UTF8, 0, src, src_len, dest, *dest_len, NULL,NULL); - return S_OK; -} - -HRESULT UTF8ToUCS2 (const char * src, int src_len, LPWSTR dest, int *dest_len) { - ASSERT (dest_len, (L"")); - ASSERT (src, (L"")); - - *dest_len = MultiByteToWideChar (CP_UTF8, 0, src, src_len, dest, *dest_len); - ASSERT (*dest_len == src_len, (L"")); - return S_OK; -} - -HRESULT UTF8ToAnsi (char * src, int, char * dest, int *dest_len) { - ASSERT(dest_len, (L"")); - ASSERT(dest, (L"")); - ASSERT(src, (L"")); - - src; dest; dest_len; // unreferenced formal parameter - - // First use MultiByteToWideChar(CP_UTF8, ...) to convert to Unicode - // then use WideCharToMultiByte to convert from Unicode to ANSI - return E_FAIL; -} - -// clean up a string so it can be included within a JavaScript string -// mainly involves escaping characters -CString SanitizeString(const CString & in, DWORD mode) { - CString out(in); - - if (mode & kSanHtml) { - // SKIP_LOC_BEGIN - ReplaceCString(out, _T("&"), _T("&")); - ReplaceCString(out, _T("<"), _T("<")); - ReplaceCString(out, _T(">"), _T(">")); - // SKIP_LOC_END - } - - if ((mode & kSanXml) == kSanXml) { - // SKIP_LOC_BEGIN - ReplaceCString(out, _T("'"), _T("'")); - ReplaceCString(out, _T("\""), _T(""")); - // SKIP_LOC_END - } - - // Note that this SAN_JAVASCRIPT and kSanXml should not be used together. - ASSERT ((mode & (kSanJs | kSanXml)) != (kSanJs | kSanXml), (L"")); - - if ((mode & kSanJs) == kSanJs) { - // SKIP_LOC_BEGIN - ReplaceCString(out, _T("\\"), _T("\\\\")); - ReplaceCString(out, _T("\'"), _T("\\\'")); - ReplaceCString(out, _T("\""), _T("\\\"")); - ReplaceCString(out, _T("\n"), _T(" ")); - ReplaceCString(out, _T("\t"), _T(" ")); - // SKIP_LOC_END - } - - if ((mode & kSanHtmlInput) == kSanHtmlInput) { - // SKIP_LOC_BEGIN - ReplaceCString(out, _T("\""), _T(""")); - ReplaceCString(out, _T("'"), _T("'")); - // SKIP_LOC_END - } - - return out; -} - -// Bolds the periods used for abbreviation. Call this after HighlightTerms. -CString BoldAbbreviationPeriods(const CString & in) { - CString out(in); - CString abbrev; - for (int i = 0; i < kAbbreviationPeriodLength; ++i) - abbrev += _T("."); - ReplaceCString(out, abbrev, NOTRANSL(_T("")) + abbrev + NOTRANSL(_T(""))); - return out; -} - -// Unescape a escaped sequence leading by a percentage symbol '%', -// and converted the unescaped sequence (in UTF8) into unicode. -// Inputs: src is the input string. -// pos is the starting position. -// Returns: true if a EOS(null) char was encounted. -// out contains the unescaped and converted unicode string. -// consumed_length is how many bytes in the src string have been -// unescaped. -// We can avoid the expensive UTF8 conversion step if there are no higher -// ansi characters So if there aren't any, just convert it ANSI-to-WIDE -// directly, which is cheaper. -inline bool UnescapeSequence(const CString &src, int pos, - CStringW *out, int *consumed_length) { - ASSERT1(out); - ASSERT1(consumed_length); - - int length = src.GetLength(); - // (input_len - pos) / 3 is enough for un-escaping the (%xx)+ sequences. - int max_dst_length = (length - pos) / 3; - std::unique_ptr unescaped(new char[max_dst_length]); - char *buf = unescaped.get(); - if (buf == NULL) { // no enough space ??? - *consumed_length = 0; - return false; - } - char *dst = buf; - bool is_utf8 = false; - // It is possible that there is a null character '\0' in the sequence. - // Because the CStringT does't support '\0' in it, we stop - // parsing the input string when it is encounted. - bool eos_encounted = false; - uint8 ch; - int s = pos; - while (s + 2 < length && src[s] == '%' && !eos_encounted && - ExtractChar(src, s + 1, &ch)) { - if (ch != 0) - *dst++ = ch; - else - eos_encounted = true; - if (ch >= 128) - is_utf8 = true; - s += 3; - } - - ASSERT1(dst <= buf + max_dst_length); // just to make sure - - // TODO(portability): cast is unsafe. - *consumed_length = s - pos; - if (is_utf8) - AnsiToWideString(buf, static_cast(dst - buf), CP_UTF8, out); - else - *out = AnsiToWideString(buf, static_cast(dst - buf)); - return eos_encounted; -} - -// There is an encoding called "URL-encoding". This function takes a URL-encoded string -// and converts it back to the original representation -// example: "?q=moon+doggy_%25%5E%26&" = "moon doggy_%^&" -CString Unencode(const CString &input) { - const int input_len = input.GetLength(); - const TCHAR *src = input.GetString(); - // input_len is enough for containing the unencoded string. - CString out; - TCHAR *head = out.GetBuffer(input_len); - TCHAR *dst = head; - int s = 0; - bool eos_encounted = false; - bool is_utf8 = false; - CStringW fragment; - int consumed_length = 0; - while (s < input_len && !eos_encounted) { - switch (src[s]) { - case '+' : - *dst++ = ' '; - ASSERT1(dst <= head + input_len); - ++s; - break; - case '%' : - eos_encounted = - UnescapeSequence(input, s, &fragment, &consumed_length); - if (consumed_length > 0) { - s += consumed_length; - ASSERT1(dst + fragment.GetLength() <= head + input_len); - for (int i = 0; i < fragment.GetLength(); ++i) - *dst++ = fragment[i]; - } else { - *dst++ = src[s++]; - ASSERT1(dst <= head + input_len); - } - break; - default: - *dst++ = src[s]; - ASSERT1(dst <= head + input_len); - ++s; - } - } - // TODO(portability): cast is unsafe. - int out_len = static_cast(dst - head); - out.ReleaseBuffer(out_len); - return out; -} - -CString GetTextInbetween(const CString &input, const CString &start, const CString &end) { - int start_index = String_FindString(input, start); - if (start_index == -1) - return L""; - - start_index += start.GetLength(); - int end_index = String_FindString(input, end, start_index); - if (end_index == -1) - return L""; - - return input.Mid(start_index, end_index - start_index); -} - -// Given a string, get the parameter and url-unencode it -CString GetParam(const CString & input, const CString & key) { - CString my_key(_T("?")); - my_key.Append(key); - my_key += L'='; - - return Unencode(GetTextInbetween(input, my_key, NOTRANSL(L"?"))); -} - -// Get an xml-like field from a string -CString GetField (const CString & input, const CString & field) { - CString start_field(NOTRANSL(_T("<"))); - start_field += field; - start_field += L'>'; - - int32 start = String_FindString(input, start_field); - if (start == -1) { return _T(""); } - start += 2 + lstrlen (field); - - CString end_field(NOTRANSL(_T("'; - - int32 end = String_FindString(input, end_field); - if (end == -1) { return _T(""); } - - return input.Mid (start, end - start); -} - -// ------------------------------------------------------------ -// Finds a whole word match in the query. -// If the word has non-spaces either before or after, it will not qualify as -// a match. i.e. "pie!" is not a match because of the exclamation point. -// TODO(omaha): Add parameter that will consider punctuation acceptable. -// -// Optionally will look for a colon at the end. -// If not found, return -1. -int FindWholeWordMatch (const CString &query, - const CString &word_to_match, - const bool end_with_colon, - const int index_begin) { - if (word_to_match.IsEmpty()) { - return -1; - } - - int index_word_begin = index_begin; - - // Keep going until we find a whole word match, or the string ends. - do { - index_word_begin = String_FindString (query, word_to_match, index_word_begin); - - if (-1 == index_word_begin) { - return index_word_begin; - } - - // If it's not a whole word match, keep going. - if (index_word_begin > 0 && - !IsSpaceW (query[index_word_begin - 1])) { - goto LoopEnd; - } - - if (end_with_colon) { - int index_colon = String_FindChar (query, L':', index_word_begin); - - // If there is no colon in the string, return now. - if (-1 == index_colon) { - return -1; - } - - // If there is text between the end of the word and the colon, keep going. - if (index_colon - index_word_begin != word_to_match.GetLength()) { - goto LoopEnd; - } - } else { - // If there are more chars left after this word/phrase, and - // they are not spaces, return. - if (query.GetLength() > index_word_begin + word_to_match.GetLength() && - !IsSpaceW (query.GetAt (index_word_begin + word_to_match.GetLength()))) { - goto LoopEnd; - } - } - - // It fits all the requirements, so return the index to the beginning of the word. - return index_word_begin; - -LoopEnd: - ++index_word_begin; - - } while (-1 != index_word_begin); - - return index_word_begin; -} - -// -------------------------------------------------------- -// Do whole-word replacement in "str". -void ReplaceWholeWord (const CString &string_to_replace, - const CString &replacement, - const bool trim_whitespace, - CString *str) { - ASSERT (str, (L"ReplaceWholeWord")); - - if (string_to_replace.IsEmpty() || str->IsEmpty()) { - return; - } - - int index_str = 0; - do { - index_str = FindWholeWordMatch (*str, string_to_replace, false, index_str); - - if (-1 != index_str) { - // Get the strings before and after, and trim whitespace. - CString str_before_word(str->Left (index_str)); - if (trim_whitespace) { - str_before_word.TrimRight(); - } - - CString str_after_word(str->Mid (index_str + string_to_replace.GetLength())); - if (trim_whitespace) { - str_after_word.TrimLeft(); - } - - *str = str_before_word + replacement + str_after_word; - index_str += replacement.GetLength() + 1; - } - } while (index_str != -1); -} - // -------------------------------------------------------- // Reverse (big-endian<->little-endian) the shorts that make up // Unicode characters in a byte array of Unicode chars @@ -1208,71 +613,6 @@ const WCHAR *stristrW(const WCHAR *string, const WCHAR *pattern) return NULL; } -// case sensitive Unicode strstr -// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c -const WCHAR *strstrW(const WCHAR *string, const WCHAR *pattern) -{ - ASSERT (pattern, (L"")); - ASSERT (string, (L"")); - ASSERT (string && pattern, (L"")); - const WCHAR *start; - - for (start = string; *start != 0; start++) - { - // find start of pattern in string - for ( ; ((*start!=0) && (*start != *pattern)); start++) - ; - if (0 == *start) - return NULL; - - const WCHAR *pattern_ptr = pattern; - const WCHAR *string_ptr = start; - - while (*string_ptr == *pattern_ptr) - { - string_ptr++; - pattern_ptr++; - - // if end of pattern then pattern was found - if (0 == *pattern_ptr) - return (start); - } - } - - return NULL; -} - -// ------------------------------------------------------------------------- -// Helper function -float GetLenWithWordWrap (const float len_so_far, - const float len_to_add, - const uint32 len_line) { - // lint -save -e414 Possible division by 0 - ASSERT (len_line != 0, (L"")); - - float len_total = len_so_far + len_to_add; - - // Figure out if we need to word wrap by seeing if adding the second - // string will cause us to span more lines than before. - uint32 num_lines_before = static_cast (len_so_far / len_line); - uint32 num_lines_after = static_cast (len_total / len_line); - - // If it just barely fit onto the line, do not wrap to the next line. - if (num_lines_after > 0 && (len_total / len_line - num_lines_after == 0)) { - --num_lines_after; - } - - if (num_lines_after > num_lines_before) { - // Need to word wrap. - // lint -e{790} Suspicious truncation - return num_lines_after * len_line + len_to_add; - } - else - return len_total; - - // lint -restore -} - int CalculateBase64EscapedLen(int input_len, bool do_padding) { // these formulae were copied from comments that used to go with the base64 // encoding functions @@ -1368,12 +708,6 @@ case 2: #define kWebSafeBase64Chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" -int Base64Escape(const char *src, int szsrc, char *dest, int szdest) { - ASSERT(dest, (L"")); - ASSERT(src, (L"")); - - return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true); -} int WebSafeBase64Escape(const char *src, int szsrc, char *dest, int szdest, bool do_padding) { ASSERT(dest, (L"")); @@ -1655,6 +989,15 @@ int Base64Unescape(const char *src, int len_src, char *dest, int len_dest) { return Base64UnescapeInternal(src, len_src, dest, len_dest, UnBase64); } +int Base64Unescape(const CStringA& src, CStringA* dest) { + ASSERT1(dest); + + int len = src.GetLength(); + int unescape_len = Base64Unescape(src, len, dest->GetBuffer(len + 1), len); + dest->ReleaseBufferSetLength(unescape_len >= 0 ? unescape_len : 0); + return unescape_len; +} + int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) { ASSERT(dest, (L"")); ASSERT(src, (L"")); @@ -1735,68 +1078,6 @@ int HexDigitToInt (WCHAR c) { (c - L'0')); } -// ---------------------------------------------------------------------- -// int QuotedPrintableUnescape() -// -// Check out http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for -// more details, only briefly implemented. But from the web... -// Quoted-printable is an encoding method defined in the MIME -// standard. It is used primarily to encode 8-bit text (such as text -// that includes foreign characters) into 7-bit US ASCII, creating a -// document that is mostly readable by humans, even in its encoded -// form. All MIME compliant applications can decode quoted-printable -// text, though they may not necessarily be able to properly display the -// document as it was originally intended. As quoted-printable encoding -// is implemented most commonly, printable ASCII characters (values 33 -// through 126, excluding 61), tabs and spaces that do not appear at the -// end of lines, and end-of-line characters are not encoded. Other -// characters are represented by an equal sign (=) immediately followed -// by that character's hexadecimal value. Lines that are longer than 76 -// characters are shortened by line breaks, with the equal sign marking -// where the breaks occurred. -// -// Update: we really want QuotedPrintableUnescape to conform to rfc2047, -// which expands the q encoding. In particular, it specifices that _'s are -// to be treated as spaces. -// ---------------------------------------------------------------------- -int QuotedPrintableUnescape(const WCHAR *source, int slen, - WCHAR *dest, int len_dest) { - ASSERT(dest, (L"")); - ASSERT(source, (L"")); - - WCHAR* d = dest; - const WCHAR* p = source; - - while (*p != '\0' && p < source+slen && d < dest+len_dest) { - switch (*p) { - case '=': - if (p == source+slen-1) { - // End of line, no need to print the =.. - // TODO(portability): cast is unsafe. - return static_cast(d-dest); - } - // if its valid, convert to hex and insert - if (p < source+slen-2 && IsHexDigit(p[1]) && IsHexDigit(p[2])) { - // lint -e{734} Loss of precision - *d++ = static_cast( - HexDigitToInt(p[1]) * 16 + HexDigitToInt(p[2])); - p += 3; - } else { - p++; - } - break; - case '_': // According to rfc2047, _'s are to be treated as spaces - *d++ = ' '; p++; - break; - default: - *d++ = *p++; - break; - } - } - // TODO(portability): cast is unsafe. - return static_cast(d-dest); -} - // TODO(omaha): currently set not to use IsCharUpper because that is relatively slow // this is used in the QUIB; consider if we need to use IsCharUpper or a replacement bool String_IsUpper(TCHAR c) { @@ -2218,56 +1499,6 @@ bool String_IsIdentifierChar(const TCHAR c) { c == _T('_')); } -// Returns true if the string has letters in it. -// This is used by the keyword extractor to downweight numbers, -// IDs (sequences of numbers like social security numbers), etc. -bool String_HasAlphabetLetters (const TCHAR * str) { - ASSERT (str, (L"")); - - while (*str != '\0') { - // if (iswalpha (*str)) { - // Note that IsCharAlpha is slower but we want to avoid the CRT - if (IsCharAlpha (*str)) { - return true; - } - ++str; - } - - return false; -} - -CString String_LargeIntToApproximateString(uint64 value, bool base_ten, int* power) { - uint32 to_one_decimal; - - uint32 gig = base_ten ? 1000000000 : (1<<30); - uint32 gig_div_10 = base_ten ? 100000000 : (1<<30)/10; - uint32 meg = base_ten ? 1000000 : (1<<20); - uint32 meg_div_10 = base_ten ? 100000 : (1<<20)/10; - uint32 kilo = base_ten ? 1000 : (1<<10); - uint32 kilo_div_10 = base_ten ? 100 : (1<<10)/10; - - if (value >= gig) { - if (power) *power = 3; - to_one_decimal = static_cast(value / gig_div_10); - } else if (value >= meg) { - if (power) *power = 2; - to_one_decimal = static_cast(value / meg_div_10); - } else if (value >= kilo) { - if (power) *power = 1; - to_one_decimal = static_cast(value / kilo_div_10); - } else { - if (power) *power = 0; - return String_Int64ToString(static_cast(value), 10 /*radix*/); - } - - uint32 whole_part = to_one_decimal / 10; - - if (whole_part < 10) - return Show(0.1 * static_cast(to_one_decimal), 1); - - return String_Int64ToString(whole_part, 10 /*radix*/); -} - int String_FindString(const TCHAR *s1, const TCHAR *s2) { ASSERT(s2, (L"")); ASSERT(s1, (L"")); @@ -2322,25 +1553,6 @@ int String_FindChar(const TCHAR *str, const TCHAR c) { return -1; } -// taken from wcsrchr, modified to behave in the CString way -int String_ReverseFindChar(const TCHAR * str,TCHAR c) { - ASSERT (str, (L"")); - TCHAR *start = (TCHAR *)str; - - while (*str++) /* find end of string */ - ; - /* search towards front */ - while (--str != start && *str != (TCHAR)c) - ; - - if (*str == (TCHAR)c) { /* found ? */ - // TODO(portability): cast is unsafe. - return static_cast(str - start); - } - - return -1; -} - int String_FindChar(const TCHAR *str, const TCHAR c, int start_pos) { ASSERT (str, (L"")); int n = 0; @@ -2363,21 +1575,6 @@ bool String_Contains(const TCHAR *s1, const TCHAR *s2) { return -1 != String_FindString(s1, s2); } -void String_ReplaceChar(TCHAR *str, TCHAR old_char, TCHAR new_char) { - ASSERT (str, (L"")); - while (*str) { - if (*str == old_char) - *str = new_char; - - ++str; - } -} - -void String_ReplaceChar(CString & str, TCHAR old_char, TCHAR new_char) { - String_ReplaceChar (str.GetBuffer(), old_char, new_char); - str.ReleaseBuffer(); -} - int ReplaceCString (CString & src, const TCHAR *from, const TCHAR *to) { ASSERT(to, (L"")); ASSERT(from, (L"")); @@ -2519,40 +1716,6 @@ int ReplaceCString (CString & src, const TCHAR *from, unsigned int from_len, return matches; } -// Functions on arrays of strings - -// Returns true iff s is in the array strings (case-insensitive compare) -bool String_MemberOf(const TCHAR* const* strings, const TCHAR* s) { - ASSERT(s, (L"")); - // strings may be NULL - - const int s_length = lstrlen(s); - if (strings == NULL) - return false; - for (; *strings != NULL; strings++) { - if (0 == String_StrNCmp(*strings, s, s_length, true)) { - return true; // Found equal string - } - } - return false; -} - -// Returns index of s in the array of strings (or -1 for missing) (case-insensitive compare) -int String_IndexOf(const TCHAR* const* strings, const TCHAR* s) { - ASSERT(s, (L"")); - // strings may be NULL - - const int s_length = lstrlen(s); - if (strings == NULL) - return -1; - for (int i = 0; *strings != NULL; i++, strings++) { - if (0 == String_StrNCmp(*strings, s, s_length, true)) { - return i; // Found equal string - } - } - return -1; -} - // The internal format is a int64. time64 StringToTime(const CString & time) { return static_cast(String_StringToInt64(time)); @@ -2565,24 +1728,6 @@ CString TimeToString(const time64 & time) { return String_Int64ToString(static_cast(time), 10); } -const TCHAR *FindStringASpaceStringB (const TCHAR *s, const TCHAR *a, const TCHAR *b) { - ASSERT(s, (L"")); - ASSERT(a, (L"")); - ASSERT(b, (L"")); - - const TCHAR *search_from = s; - const TCHAR *pos; - while (*search_from && (pos = stristrW (search_from, a)) != NULL) { - const TCHAR *start = pos; - pos += lstrlen(a); - search_from = pos; - while (*pos == ' ' || *pos == '\t') pos++; - if (!String_StrNCmp (pos, b, lstrlen(b), true)) return start; - } - - return 0; -} - bool IsAlphaA (const char c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); } @@ -2591,46 +1736,6 @@ bool IsDigitA (const char c) { return (c >= '0' && c <= '9'); } -void SafeStrCat (TCHAR *dest, const TCHAR *src, int dest_buffer_len) { - _tcscat_s(dest, dest_buffer_len, src); -} - -// extracts next float in a string -// skips any non-digit characters -// return position after end of float -const TCHAR *ExtractNextDouble (const TCHAR *s, double *f) { - ASSERT (f, (L"")); - ASSERT (s, (L"")); - - CString num; - while (*s && !String_IsDigit (*s)) s++; - while (*s && (*s == '.' || String_IsDigit (*s))) { num += *s; s++; } - ASSERT (num.GetLength(), (L"")); - *f = String_StringToDouble (num); - return s; -} - -TCHAR *String_PathFindExtension(const TCHAR *path) { - ASSERT(path, (L"")); - - // Documentation says PathFindExtension string must be of max length - // MAX_PATH but a trusted tester hit the ASSERT and we don't really - // need it here, so commented out. We can't address where it is - // called because it's called from ATL code. - // ASSERT(lstrlen(path)<=MAX_PATH, (L"")); - - // point to terminating NULL - const TCHAR *ret = path + lstrlen(path); - const TCHAR *pos = ret; - - while (--pos >= path) { - if (*pos == '.') - return const_cast(pos); - } - - return const_cast(ret); -} - char String_ToLowerCharAnsi(char c) { if (c >= 'A' && c <= 'Z') return (c + ('a' - 'A')); return c; @@ -2644,50 +1749,6 @@ int String_ToLowerChar(int c) { return Char_ToLower(static_cast(c)); } - -bool String_PathRemoveFileSpec(TCHAR *path) { - ASSERT (path, (L"")); - - int len, pos; - len = pos = lstrlen (path); - - // You might think that the SHLWAPI API does not change "c:\windows" -> "c:\" - // when c:\windows is a directory, but it does. - - // If we don't want to match this weird API we can use the following to check - // for directories: - - // Check if we are already a directory. - WIN32_FILE_ATTRIBUTE_DATA attrs; - // Failure (if file does not exist) is OK. - BOOL success = GetFileAttributesEx(path, GetFileExInfoStandard, &attrs); - UTIL_LOG(L4, (_T("[String_PathRemoveFileSpec][path %s][success %d][dir %d]"), - path, - success, - attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)); - if (success && (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - // Remove trailing backslash, if any. - if (path[pos-1] == '\\') - path[pos-1] = '\0'; - return 1; - } - - // Find last backslash. - while (pos && path[pos] != '\\') pos--; - if (!pos && path[pos] != '\\') return 0; - - ASSERT (pos < len, (L"")); - - // The documentation says it removes backslash but it doesn't for c:\. - if (!pos || path[pos-1] == ':' || (pos == 1 && path[0] == '\\')) - // Keep the backslash in this case. - path[pos+1] = '\0'; - else - path[pos] = '\0'; - - return 1; -} - void String_EndWithChar(TCHAR *str, TCHAR c) { ASSERT (str, (L"")); int len = lstrlen(str); @@ -2745,19 +1806,6 @@ bool String_StringToDecimalIntChecked(const TCHAR* str, int* value) { return true; } -bool CLSIDToCString(const GUID& guid, CString* str) { - ASSERT(str, (L"")); - - LPOLESTR string_guid = NULL; - if (::StringFromCLSID(guid, &string_guid) != S_OK) { - return false; - } - *str = string_guid; - ::CoTaskMemFree(string_guid); - - return true; -} - HRESULT String_StringToBool(const TCHAR* str, bool* value) { ASSERT1(str); ASSERT1(value); @@ -2776,7 +1824,7 @@ HRESULT String_StringToBool(const TCHAR* str, bool* value) { return S_OK; } -const TCHAR* const String_BoolToString(bool value) { +const TCHAR* String_BoolToString(bool value) { return value ? kTrue : kFalse; } @@ -2975,54 +2023,6 @@ CString BytesToHex(const std::vector& bytes) { return result; } -void JoinStrings(const std::vector& components, - const TCHAR* delim, - CString* result) { - ASSERT1(result); - result->Empty(); - - // Compute length so we can reserve memory. - size_t length = 0; - size_t delim_length = delim ? _tcslen(delim) : 0; - for (size_t i = 0; i != components.size(); ++i) { - if (i != 0) { - length += delim_length; - } - length += components[i].GetLength(); - } - - ASSERT1(length <= INT_MAX && delim_length <= INT_MAX); - if (length > INT_MAX || delim_length > INT_MAX) { - return; - } - - result->Preallocate(static_cast(length)); - - for (size_t i = 0; i != components.size(); ++i) { - if (i != 0 && delim) { - result->Append(delim, static_cast(delim_length)); - } - result->Append(components[i]); - } -} - -void JoinStringsInArray(const TCHAR* components[], - int num_components, - const TCHAR* delim, - CString* result) { - ASSERT1(result); - result->Empty(); - - for (int i = 0; i != num_components; ++i) { - if (i != 0 && delim) { - result->Append(delim); - } - if (components[i]) { - result->Append(components[i]); - } - } -} - CString FormatResourceMessage(uint32 resource_id, ...) { CString format; const bool is_loaded = !!format.LoadString(resource_id); @@ -3151,25 +2151,6 @@ static void a2b_hex_t(const char* a, T b, size_t num) { } } -string a2b_bin(const string& a, bool byte_order_msb) { - string result; - const char *data = a.c_str(); - size_t num_bytes = (a.size()+7)/8; - for (size_t byte_offset = 0; byte_offset < num_bytes; ++byte_offset) { - unsigned char c = 0; - for (size_t bit_offset = 0; bit_offset < 8; ++bit_offset) { - if (*data == '\0') - break; - if (*data++ != '0') { - size_t bits_to_shift = (byte_order_msb) ? 7-bit_offset : bit_offset; - c |= (1 << bits_to_shift); - } - } - result.append(1, c); - } - return result; -} - // This is a templated function so that T can be either a char* // or a string. This works because we use the [] operator to access // individual characters at a time. @@ -3181,17 +2162,6 @@ static void b2a_hex_t(const unsigned char* b, T a, size_t num) { } } -string b2a_bin(const string& b, bool byte_order_msb) { - string result; - for (size_t byte_offset = 0; byte_offset < b.size(); ++byte_offset) { - for (size_t bit_offset = 0; bit_offset < 8; ++bit_offset) { - size_t x = (byte_order_msb) ? 7-bit_offset : bit_offset; - result.append(1, (b[byte_offset] & (1 << x)) ? '1' : '0'); - } - } - return result; -} - void b2a_hex(const unsigned char* b, char* a, size_t num) { b2a_hex_t(b, a, num); } @@ -3269,4 +2239,3 @@ bool SafeHexStringToVector(const CStringW& str, std::vector* vec_out) { } } // namespace omaha - diff --git a/omaha/base/string.h b/omaha/base/string.h index 25edda7b7..913115b2f 100644 --- a/omaha/base/string.h +++ b/omaha/base/string.h @@ -32,19 +32,9 @@ namespace omaha { #define STR_SIZE(str) (arraysize(str)-1) // number of characters in char array (only for single-byte string literals!!!) #define TSTR_SIZE(tstr) (arraysize(tstr)-1) // like STR_SIZE but works on _T("string literal") ONLY!!! -#define kEllipsis L".." - // The number of replacements matches we expect, before we start allocating extra memory // to process it. This is an optimizing constant -#define kExpectedMaxReplaceMatches 100 - -// TODO(omaha): above each of these function names, we should -// define what we expect the implementation to do. that way, -// implementers will know what is desired. an example would probably -// make things easiest. -CString AbbreviateString (const CString & title, int32 max_len); -CString AbbreviateUri (const CString & uri, int32 max_len); -CString NormalizeUri (const CString & uri); +constexpr size_t kExpectedMaxReplaceMatches = 100; // removes "http://", "ftp://", "mailto:" or "file://" (note that the "file" protocol is // like: "file:///~/calendar", this method removes only the first two slashes @@ -53,77 +43,12 @@ CString RemoveInternetProtocolHeader (const CString& url); // Converts a file:// URI to a valid Windows path. HRESULT ConvertFileUriToLocalPath(const CString& uri, CString* path_out); -void RemoveFromStart (CString & s, const TCHAR* remove, bool ignore_case); -void RemoveFromEnd (CString & s, const TCHAR* remove); - -// Limit string to max length, truncating and adding ellipsis if needed -// Attempts to not leave a partial word at the end, unless min_len is reached -CString ElideIfNeeded (const CString & input_string, int max_len, int min_len); - -// The ability to clean up a string for relevant target audiences. Add flags accordingly - -// Sanitizes for insertion in an HTML document, uses the basic literals [<>&] -#define kSanHtml 0x1 - -// XML is the HTML replacements, and a few more -#define kSanXml (kSanHtml | 0x2) - -// JavaScript has a separate set of encodings [which is a superset of HTML replacements] -#define kSanJs (kSanHtml | 0x4) - -// For input fields on HTML documents -#define kSanHtmlInput 0x8 - // TODO(omaha): be consistent on use of int/uint32/int32 for lengths // The input length of the string does not include the null terminator. // Caller deletes the returned buffer. WCHAR *ToWide (const char *s, int len); -// returns pointer to data if found otherwise NULL -const byte *BufferContains (const byte *buf, uint32 buf_len, const byte *data, uint32 data_len); - -// Given a string, 'protect' the characters that are invalid for a given mode -// For instance, kSanHtml will replace < with the HTML literal equivalent -// If kSanHtml is used, and bold_periods is true, then periods used for url abbreviation are bolded. -// NOTE: If you call AbbreviateLinkForDisplay before this function, then there might be periods -// used for abbreviation. BoldAbbreviationPeriods should be called after HighlightTerms. -CString SanitizeString(const CString & in, DWORD mode); - -// Bolds the periods used for abbreviation. Call this after HighlightTerms. -CString BoldAbbreviationPeriods(const CString & in); - -// Unencode a URL encoded string -CString Unencode(const CString & input); - -CString GetTextInbetween(const CString &input, const CString &start, const CString &end); - -// Given a ? seperated string, extract a particular segment, and URL-Unencode it -CString GetParam(const CString & input, const CString & key); - -// Given an XML style string, extract the contents of a ... pair -CString GetField (const CString & input, const CString & field); - -// Finds a whole word match in the query, followed by a ":". -// If not found, return -1. -// -// Note: this is case sensitive. -int FindWholeWordMatch (const CString &query, - const CString &word_to_match, - const bool end_with_colon, - const int index_begin); - -// Do whole-word replacement in "str". -// This does not do partial matches (unlike CString::Replace), -// e.g. CString::Replace will replace "ie" within "pie" and -// this function will not. -// -// Note: this is case sensitive. -void ReplaceWholeWord (const CString &string_to_replace, - const CString &replacement, - const bool trim_whitespace, - CString *str); - // Convert Wide to ANSI directly. Use only when it is all ANSI CStringA WideToAnsiDirect(const CString & in); @@ -155,13 +80,6 @@ BOOL AnsiToWideString(const char *from, int length, UINT codepage, CString *to); // Convert char to Wchar directly CString AnsiToWideString(const char *from, int length); -// these functions untested -// they should not be used unless tested -// HRESULT AnsiToUTF8 (char * src, int src_len, char * dest, int *dest_len); -// HRESULT UTF8ToAnsi (char * src, int src_len, char * dest, int *dest_len); -// HRESULT UCS2ToUTF8 (LPCWSTR src, int src_len, char * dest, int *dest_len); -// HRESULT UTF8ToUCS2 (char * src, int src_len, LPWSTR dest, int *dest_len); - // "Absolute" is perhaps not the right term, this normalizes the Uri // given http://www.google.com changes to correct http://www.google.com/ // given http://www.google.com// changes to correct http://www.google.com/ @@ -182,24 +100,6 @@ CString GetUriHostNameHostOnly(const CString& uri, bool strip_leading_www); const char *stristr(const char *string, const char *pattern); const WCHAR *stristrW(const WCHAR *string, const WCHAR *pattern); -const WCHAR *strstrW(const WCHAR *string, const WCHAR *pattern); - -// Add len_to_add to len_so_far, assuming that if it exceeds the -// length of the line, it will word wrap onto the next line. Returns -// the total length of all the lines summed together. -float GetLenWithWordWrap (const float len_so_far, - const float len_to_add, - const uint32 len_line); - -// ---------------------------------------------------------------------- -// QuotedPrintableUnescape() -// Copies "src" to "dest", rewriting quoted printable escape sequences -// =XX to their ASCII equivalents. src is not null terminated, instead -// specify len. I recommend that slen* lines); // (LinesToText puts a delimiter at the end of the last line too) @@ -277,12 +166,6 @@ void LinesToText(const std::vector& lines, const TCHAR* delimiter, CStr // Make a CString lower case void MakeLowerCString(CString & s); -// Clean up the string: replace all whitespace with spaces, and -// replace consecutive spaces with one. -// Returns the new length of the string (not including 0-terminator) -int CleanupWhitespaceCString(CString &s); -int CleanupWhitespace(TCHAR *s); - int HexDigitToInt (WCHAR c); bool IsHexDigit (WCHAR c); @@ -334,12 +217,6 @@ CString sizet_to_str(const size_t & i); CString itostr(const int i); CString itostr(const uint32 i); -// converts a large number to an approximate value, like "1.2G" or "900M" -// base_ten = true if based on powers of 10 (like disk space) otherwise based -// on powers of two. power = 0 for *10^0, 1 for *10^3 or 2^10, 2 for *10^6 -// or 2^20, and 3 for *10^9 or 2^30, in other words: no units, K, M, or G. -CString String_LargeIntToApproximateString(uint64 value, bool base_ten, int* power); - // converts a string to an int // Does not check for overflow int32 String_StringToInt(const TCHAR * str); @@ -367,11 +244,6 @@ TCHAR String_DigitToChar(unsigned int n); // Returns true if an identifier character: letter, digit, or "_" bool String_IsIdentifierChar(const TCHAR c); -// Returns true if the string has letters in it. -// This is used by the keyword extractor to downweight numbers, -// IDs (sequences of numbers like social security numbers), etc. -bool String_HasAlphabetLetters (const TCHAR *str); - // Return the index of the first occurrence of s2 in s1, or -1 if none. int String_FindString(const TCHAR *s1, const TCHAR *s2); int String_FindString(const TCHAR *s1, const TCHAR *s2, int start_pos); @@ -381,15 +253,8 @@ int String_FindChar(const TCHAR *str, const TCHAR c); // start from index start_pos int String_FindChar(const TCHAR *str, const TCHAR c, int start_pos); -// Return the index of the first occurrence of c in string, or -1 if none. -int String_ReverseFindChar(const TCHAR * str, TCHAR c); - bool String_Contains(const TCHAR *s1, const TCHAR *s2); -// Replace old_char with new_char in str. -void String_ReplaceChar(TCHAR *str, TCHAR old_char, TCHAR new_char); -void String_ReplaceChar(CString & str, TCHAR old_char, TCHAR new_char); - // Append the given character to the string if it doesn't already end with it. // There must be room in the string to append the character if necessary. void String_EndWithChar(TCHAR *str, TCHAR c); @@ -410,35 +275,13 @@ int ReplaceCString (CString & src, const TCHAR *from, unsigned int from_len, // on memory allocation error, returns the original string int ReplaceCString (CString & src, const TCHAR *from, const TCHAR *to); -// Functions on arrays of strings - -// Returns true iff s is in the array strings (case-insensitive compare) -bool String_MemberOf(const TCHAR* const* strings, const TCHAR* s); -// Returns index of s in the array of strings (or -1 for missing) (case-insensitive compare) -int String_IndexOf(const TCHAR* const* strings, const TCHAR* s); - // Serializes a time64 to a string, and then loads it out again, this string it not for human consumption time64 StringToTime(const CString & time); CString TimeToString(const time64 & time); -// looks for string A followed by any number of spaces/tabs followed by string b -// returns starting position of a if found, NULL if not -// case insensitive -const TCHAR *FindStringASpaceStringB (const TCHAR *s, const TCHAR *a, const TCHAR *b); - bool IsAlphaA (const char c); bool IsDigitA (const char c); -// TODO(omaha): deprecate since we have secure CRT now. -// dest_buffer_len includes the NULL -// always NULL terminates -// dest must be a valid string with length < dest_buffer_len -void SafeStrCat (TCHAR *dest, const TCHAR *src, int dest_buffer_len); - -const TCHAR *ExtractNextDouble (const TCHAR *s, double *f); - -TCHAR *String_PathFindExtension(const TCHAR *path); - inline TCHAR Char_ToLower(TCHAR c) { const TCHAR* result = ::CharLower(reinterpret_cast(c)); ASSERT1(HIWORD(result) == 0); @@ -451,8 +294,6 @@ int String_ToLowerChar(int c); // Replacement for the CRT tolower(c) char String_ToLowerCharAnsi(char c); -bool String_PathRemoveFileSpec(TCHAR *path); - // Escapes and unescapes strings (shlwapi-based implementation). // The indended usage for these APIs is escaping strings to make up // URLs, for example building query strings. @@ -470,14 +311,11 @@ HRESULT StringUnescape(const CString& str_in, CString* str_out); // Tests for overflow and non-int strings. bool String_StringToDecimalIntChecked(const TCHAR* str, int* value); -// Converts CLSID to a string. -bool CLSIDToCString(const GUID& guid, CString* str); - // Converts a string to a bool. HRESULT String_StringToBool(const TCHAR* str, bool* value); // Convert boolean to its string representation. -const TCHAR* const String_BoolToString(bool value); +const TCHAR* String_BoolToString(bool value); // Similar to ATL::CStringT::Replace() except that it ignores case. CString String_ReplaceIgnoreCase(const CString& string, @@ -506,15 +344,6 @@ CString BytesToHex(const uint8* bytes, size_t num_bytes); // Converts a vector of bytes to a hex string. CString BytesToHex(const std::vector& bytes); -void JoinStrings(const std::vector& components, - const TCHAR* delim, - CString* result); - -void JoinStringsInArray(const TCHAR* components[], - int num_components, - const TCHAR* delim, - CString* result); - // Formats the specified message ID. // It is similar to CStringT::FormatMessage() but it returns an empty string // instead of throwing when the message ID cannot be loaded. @@ -543,18 +372,6 @@ void a2b_hex(const char* from, char* to, size_t num); void a2b_hex(const char* from, std::string* to, size_t num); std::string a2b_hex(const std::string& a); -// ---------------------------------------------------------------------- -// a2b_bin() -// Description: Ascii-to-Binary binary conversion. This converts -// a.size() binary characters (ascii '0' or '1') to -// ceil(a.size()/8) bytes of binary data. The first character is -// considered the most significant if byte_order_msb is set. a is -// considered to be padded with trailing 0s if its size is not a -// multiple of 8. -// Return value: ceil(a.size()/8) bytes of binary data -// ---------------------------------------------------------------------- -std::string a2b_bin(const std::string& a, bool byte_order_msb); - // ---------------------------------------------------------------------- // b2a_hex() // Description: Binary-to-Ascii hex conversion. This converts diff --git a/omaha/base/string_unittest.cc b/omaha/base/string_unittest.cc index 3b1fc6ab2..4c4002d0e 100644 --- a/omaha/base/string_unittest.cc +++ b/omaha/base/string_unittest.cc @@ -16,7 +16,6 @@ #include "base/basictypes.h" #include "base/rand_util.h" #include "omaha/base/debug.h" -#include "omaha/base/localization.h" #include "omaha/base/string.h" #include "omaha/base/time.h" #include "omaha/base/timer.h" @@ -66,7 +65,8 @@ TEST(StringTest, UintToString) { ASSERT_STREQ(String_Uint64ToString(0x101fff, 16), L"101fff"); ASSERT_STREQ(String_Uint64ToString(0x999999, 16), L"999999"); ASSERT_STREQ(String_Uint64ToString(0x0, 16), L"0"); - ASSERT_STREQ(String_Uint64ToString(0xffffffffffffffff, 16), L"ffffffffffffffff"); + ASSERT_STREQ(String_Uint64ToString(0xffffffffffffffff, 16), + L"ffffffffffffffff"); ASSERT_STREQ(String_Uint64ToString(01234, 8), L"1234"); ASSERT_STREQ(String_Uint64ToString(0, 8), L"0"); @@ -181,29 +181,6 @@ TEST(StringTest, EndsWith) { ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"the brown foX", true)); } -TEST(StringTest, Unencode) { - // Normal, correct usage. - // char 0x25 is '%' - ASSERT_STREQ(Unencode(L"?q=moon+doggy_%25%5E%26"), L"?q=moon doggy_%^&"); - ASSERT_STREQ(Unencode(L"%54%68%69%73+%69%73%09%61%20%74%65%73%74%0A"), - L"This is\ta test\n"); - ASSERT_STREQ(Unencode(L"This+is%09a+test%0a"), L"This is\ta test\n"); - - // NULL char. - ASSERT_STREQ(Unencode(L"Terminated%00before+this"), L"Terminated"); - ASSERT_STREQ(Unencode(L"invalid+%a%25"), L"invalid %a%"); - ASSERT_STREQ(Unencode(L"invalid+%25%41%37"), L"invalid %A7"); - ASSERT_STREQ(Unencode(L"not a symbol %RA"), L"not a symbol %RA"); - ASSERT_STREQ(Unencode(L"%ag"), L"%ag"); - ASSERT_STREQ(Unencode(L"dontdecode%dont"), L"dontdecode%dont"); - ASSERT_STREQ(Unencode(L""), L""); - ASSERT_STREQ(Unencode(L"%1"), L"%1"); - ASSERT_STREQ(Unencode(L"\x100"), L"\x100"); - ASSERT_STREQ(Unencode(L"this is%20a%20wide%20char%20\x345"), - L"this is a wide char \x345"); - ASSERT_STREQ(Unencode(L"a utf8 string %E7%BC%9c %E4%B8%8a = 2"), - L"a utf8 string \x7f1c \x4e0a = 2"); -} #if 0 static const struct { @@ -236,13 +213,6 @@ bool TestAnsiToWideString() { } #endif -TEST(StringTest, Show) { - ASSERT_STREQ(Show(0), _T("0")); - ASSERT_STREQ(Show(1), _T("1")); - ASSERT_STREQ(Show(-1), _T("-1")); -} - - // Test international strings. TEST(StringTest, International) { CString tabs_by_lang[] = { @@ -386,15 +356,6 @@ TEST(StringTest, International) { temp.ReleaseBuffer(); ASSERT_STREQ(temp, true_lower); - - // Make sure that the normal CString::Trim works the same as our fast one - CString trim_normal(tabs_by_lang[i]); - trim_normal.Trim(); - - CString trim_fast(tabs_by_lang[i]); - TrimCString(trim_fast); - - ASSERT_STREQ(trim_normal, trim_fast); } } @@ -480,142 +441,6 @@ TEST(StringTest, ReplaceCString) { } } -TEST(StringTest, GetField) { - CString s(_T("a123aa\ndd")); - - CString a(GetField (s, L"a")); - ASSERT_STREQ(a, L"a"); - - CString b(GetField (s, L"b")); - ASSERT_STREQ(b, L"123"); - - CString c(GetField (s, L"c")); - ASSERT_STREQ(c, L"aa\ndd"); -} - -TEST(StringTest, String_HasAlphabetLetters) { - ASSERT_TRUE(String_HasAlphabetLetters (L"abc")); - ASSERT_TRUE(String_HasAlphabetLetters (L"X")); - ASSERT_TRUE(String_HasAlphabetLetters (L" pie ")); - ASSERT_FALSE(String_HasAlphabetLetters (L"1")); - ASSERT_FALSE(String_HasAlphabetLetters (L"0")); - ASSERT_FALSE(String_HasAlphabetLetters (L"010")); - ASSERT_FALSE(String_HasAlphabetLetters (L"314-159")); - ASSERT_TRUE(String_HasAlphabetLetters (L"pie0")); -} - -TEST(StringTest, String_LargeIntToApproximateString) { - int power; - ASSERT_TRUE(String_LargeIntToApproximateString(10LL, true, &power) == _T("10") && power == 0); - ASSERT_TRUE(String_LargeIntToApproximateString(99LL, true, &power) == _T("99") && power == 0); - ASSERT_TRUE(String_LargeIntToApproximateString(990LL, true, &power) == _T("990") && power == 0); - ASSERT_TRUE(String_LargeIntToApproximateString(999LL, true, &power) == _T("999") && power == 0); - - ASSERT_TRUE(String_LargeIntToApproximateString(1000LL, true, &power) == _T("1.0") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(1200LL, true, &power) == _T("1.2") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(7500LL, true, &power) == _T("7.5") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(9900LL, true, &power) == _T("9.9") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(10000LL, true, &power) == _T("10") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(11000LL, true, &power) == _T("11") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(987654LL, true, &power) == _T("987") && power == 1); - - ASSERT_TRUE(String_LargeIntToApproximateString(1000000LL, true, &power) == _T("1.0") && power == 2); - ASSERT_TRUE(String_LargeIntToApproximateString(1300000LL, true, &power) == _T("1.3") && power == 2); - ASSERT_TRUE(String_LargeIntToApproximateString(987654321LL, true, &power) == _T("987") && power == 2); - - ASSERT_TRUE(String_LargeIntToApproximateString(1000000000LL, true, &power) == _T("1.0") && power == 3); - ASSERT_TRUE(String_LargeIntToApproximateString(1999999999LL, true, &power) == _T("1.9") && power == 3); - ASSERT_TRUE(String_LargeIntToApproximateString(20000000000LL, true, &power) == _T("20") && power == 3); - ASSERT_TRUE(String_LargeIntToApproximateString(1000000000000LL, true, &power) == _T("1000") && power == 3); - ASSERT_TRUE(String_LargeIntToApproximateString(12345678901234LL, true, &power) == _T("12345") && power == 3); - - ASSERT_TRUE(String_LargeIntToApproximateString(1023LL, false, &power) == _T("1023") && power == 0); - - ASSERT_TRUE(String_LargeIntToApproximateString(1024LL, false, &power) == _T("1.0") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(1134LL, false, &power) == _T("1.1") && power == 1); - ASSERT_TRUE(String_LargeIntToApproximateString(10240LL, false, &power) == _T("10") && power == 1); - - ASSERT_TRUE(String_LargeIntToApproximateString(5242880LL, false, &power) == _T("5.0") && power == 2); - - ASSERT_TRUE(String_LargeIntToApproximateString(1073741824LL, false, &power) == _T("1.0") && power == 3); - ASSERT_TRUE(String_LargeIntToApproximateString(17179869184LL, false, &power) == _T("16") && power == 3); -} - -TEST(StringTest, FindWholeWordMatch) { - // words with spaces before / after - ASSERT_EQ(0, FindWholeWordMatch (L"pi", L"pi", false, 0)); - ASSERT_EQ(1, FindWholeWordMatch (L" pi", L"pi", false, 0)); - ASSERT_EQ(1, FindWholeWordMatch (L" pi ", L"pi", false, 0)); - ASSERT_EQ(0, FindWholeWordMatch (L"pi ", L"pi", false, 0)); - - // partial matches - ASSERT_EQ(-1, FindWholeWordMatch (L"pie ", L"pi", false, 0)); - ASSERT_EQ(-1, FindWholeWordMatch (L" pie ", L"pi", false, 0)); - ASSERT_EQ(-1, FindWholeWordMatch (L"pie", L"pi", false, 0)); - ASSERT_EQ(-1, FindWholeWordMatch (L" pie", L"pi", false, 0)); - - // partial match with non-alphanumeric chars - ASSERT_EQ(-1, FindWholeWordMatch (L" pumpkin_pie ", L"pie", false, 0)); - ASSERT_EQ(-1, FindWholeWordMatch (L" pie_crust ", L"pie", false, 0)); - ASSERT_EQ(-1, FindWholeWordMatch (L"tartar", L"tar", false, 0)); - ASSERT_EQ(-1, FindWholeWordMatch (L"pie!", L"pie", false, 0)); -} - -TEST(StringTest, ReplaceWholeWord) { - CString str (L"pie"); - ReplaceWholeWord (L"ie", L"..", false, &str); - ASSERT_STREQ(str, L"pie"); - - ReplaceWholeWord (L"pie", L"..", false, &str); - ASSERT_STREQ(str, L".."); - - str = L"banana pie"; - ReplaceWholeWord (L"pie", L"..", false, &str); - ASSERT_STREQ(str, L"banana .."); - - str = L"banana pie"; - ReplaceWholeWord (L"banana", L"..", false, &str); - ASSERT_STREQ(str, L".. pie"); - - str = L"banana pie"; - ReplaceWholeWord (L"banana pie", L" .. ", false, &str); - ASSERT_STREQ(str, L" .. "); - - str = L"banana pie"; - ReplaceWholeWord (L"pi", L" .. ", false, &str); - ASSERT_STREQ(str, L"banana pie"); - - str = L"ishniferatsu"; - ReplaceWholeWord (L"era", L" .. ", false, &str); - ASSERT_STREQ(str, L"ishniferatsu"); - - str = L"i i i hi ii i"; - ReplaceWholeWord (L"i", L"you", false, &str); - ASSERT_STREQ(str, L"you you you hi ii you"); - - str = L"a nice cream cheese pie"; - ReplaceWholeWord (L"cream cheese", L"..", false, &str); - ASSERT_STREQ(str, L"a nice .. pie"); - - // --- - // Test replacement with whitespace trimming - - // Replace in the middle of the string. - str = L"a nice cream cheese pie"; - ReplaceWholeWord (L"cream cheese", L"..", true, &str); - ASSERT_STREQ(str, L"a nice..pie"); - - // Replace in the beginning of the string. - str = L"a nice cream cheese pie"; - ReplaceWholeWord (L"a nice", L"..", true, &str); - ASSERT_STREQ(str, L"..cream cheese pie"); - - // Replace in the end of the string. - str = L"a nice cream cheese pie"; - ReplaceWholeWord (L"pie", L"..", true, &str); - ASSERT_STREQ(str, L"a nice cream cheese.."); -} - TEST(StringTest, GetAbsoluteUri) { ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com"), L"http://www.google.com/"); @@ -627,37 +452,6 @@ TEST(StringTest, GetAbsoluteUri) { L"http://www.google.com/test"); } -void TestTrim(const TCHAR *str, const TCHAR *result) { - ASSERT_TRUE(result); - ASSERT_TRUE(str); - - size_t ptr_size = _tcslen(str) + 1; - TCHAR* ptr = new TCHAR[ptr_size]; - _tcscpy_s(ptr, ptr_size, str); - - int len = Trim(ptr); - ASSERT_STREQ(ptr, result); - ASSERT_EQ(len, lstrlen(result)); - - delete [] ptr; -} - -TEST(StringTest, Trim) { - TestTrim(L"", L""); - TestTrim(L" ", L""); - TestTrim(L"\t", L""); - TestTrim(L"\n", L""); - TestTrim(L"\n\t \t \n", L""); - TestTrim(L" joe", L"joe"); - TestTrim(L"joe ", L"joe"); - TestTrim(L" joe ", L"joe"); - TestTrim(L"joe smith ", L"joe smith"); - TestTrim(L" joe smith ", L"joe smith"); - TestTrim(L" joe smith ", L"joe smith"); - TestTrim(L" The quick brown fox,\tblah", L"The quick brown fox,\tblah"); - TestTrim(L" \tblah\n joe smith ", L"blah\n joe smith"); -} - // IsSpaceA1 is much faster without the cache clearing (which is what happends // in release mode) // IsSpaceA1 is roughly the same speed as IsSpaceA2 with cache clearing (in @@ -718,8 +512,8 @@ void TestIsSpace (const char *s) { // used to try to clear the processor cache const size_t dlen = 100000; - char* dummy = new char [dlen]; - RandBytes(dummy, sizeof(*dummy) * dlen); + char* rand_bytes = new char [dlen]; + RandBytes(rand_bytes, sizeof(*rand_bytes) * dlen); size_t num_spaces = 0; size_t n = iterations * len; @@ -731,7 +525,7 @@ void TestIsSpace (const char *s) { t1.Stop(); // this cache clearing code gets optimized out in release mode size_t d2 = 0; - for (size_t j = 0; j < dlen; j++) { d2 += dummy[j]; } + for (size_t j = 0; j < dlen; j++) { d2 += rand_bytes[j]; } } num_spaces = 0; @@ -742,7 +536,7 @@ void TestIsSpace (const char *s) { } t2.Stop(); size_t d2 = 0; - for (size_t j = 0; j < dlen; j++) { d2 += dummy[j]; } + for (size_t j = 0; j < dlen; j++) { d2 += rand_bytes[j]; } } num_spaces = 0; @@ -753,10 +547,10 @@ void TestIsSpace (const char *s) { } t3.Stop(); size_t d2 = 0; - for (size_t j = 0; j < dlen; j++) { d2 += dummy[j]; } + for (size_t j = 0; j < dlen; j++) { d2 += rand_bytes[j]; } } - delete[] dummy; + delete[] rand_bytes; } TEST(StringTest, IsSpace) { @@ -764,37 +558,6 @@ TEST(StringTest, IsSpace) { TestIsSpace("sdlfhdkgheorutsgj sdlj aoi oaj gldjg opre gdsfjng oate yhdnv ;zsj fpoe v;kjae hgpaieh dajlgn aegh avn WEIf h9243y 9814cu 902t7 9[-32 [O8W759 RC90817 V9pDAHc n( ny(7LKFJAOISF *&^*^%$$%#*&^(*_*)_^& 67% 796%&$*^$ 8)6 (^ 08&^ )*^ 9-7=90z& +(^ )^* %9%4386 $& (& &+ 7- &(_* "); } -void TestCleanupWhitespace(const TCHAR *str, const TCHAR *result) { - ASSERT_TRUE(result); - ASSERT_TRUE(str); - - size_t ptr_size = _tcslen(str) + 1; - TCHAR* ptr = new TCHAR[ptr_size]; - _tcscpy_s(ptr, ptr_size, str); - - int len = CleanupWhitespace(ptr); - ASSERT_STREQ(ptr, result); - ASSERT_EQ(len, lstrlen(result)); - - delete [] ptr; -} - -TEST(StringTest, CleanupWhitespace) { - TestCleanupWhitespace(L"", L""); - TestCleanupWhitespace(L"a ", L"a"); - TestCleanupWhitespace(L" a", L"a"); - TestCleanupWhitespace(L" a ", L"a"); - TestCleanupWhitespace(L"\t\n\r a ", L"a"); - TestCleanupWhitespace(L" \n a \t \r ", L"a"); - TestCleanupWhitespace(L"a b", L"a b"); - TestCleanupWhitespace(L" a \t\n\r b", L"a b"); - TestCleanupWhitespace(L" vool voop", L"vool voop"); - TestCleanupWhitespace(L"thisisaverylongstringwithsometext", - L"thisisaverylongstringwithsometext"); - TestCleanupWhitespace(L"thisisavery longstringwithsometext", - L"thisisavery longstringwithsometext"); -} - TEST(StringTest, IsDigit) { ASSERT_TRUE(String_IsDigit('0')); ASSERT_TRUE(String_IsDigit('1')); @@ -935,100 +698,6 @@ TEST(StringTest, IsHexDigit) { } } -TEST(StringTest, Remove) { - CString temp_remove; - - // Remove everything - temp_remove = _T("ftp://"); - RemoveFromStart (temp_remove, _T("ftp://"), false); - ASSERT_STREQ(temp_remove, _T("")); - - // Remove all but 1 letter - temp_remove = _T("ftp://a"); - RemoveFromStart (temp_remove, _T("ftp://"), false); - ASSERT_STREQ(temp_remove, _T("a")); - - // Remove the first instance - temp_remove = _T("ftp://ftp://"); - RemoveFromStart (temp_remove, _T("ftp://"), false); - ASSERT_STREQ(temp_remove, _T("ftp://")); - - // Remove normal - temp_remove = _T("ftp://taz the tiger"); - RemoveFromStart (temp_remove, _T("ftp://"), false); - ASSERT_STREQ(temp_remove, _T("taz the tiger")); - - // Wrong prefix - temp_remove = _T("ftp:/taz the tiger"); - RemoveFromStart (temp_remove, _T("ftp://"), false); - ASSERT_STREQ(temp_remove, _T("ftp:/taz the tiger")); - - // Not long enough - temp_remove = _T("ftp:/"); - RemoveFromStart (temp_remove, _T("ftp://"), false); - ASSERT_STREQ(temp_remove, _T("ftp:/")); - - // Remove nothing - temp_remove = _T("ftp:/"); - RemoveFromStart (temp_remove, _T(""), false); - ASSERT_STREQ(temp_remove, _T("ftp:/")); - - // Remove 1 character - temp_remove = _T("ftp:/"); - RemoveFromStart (temp_remove, _T("f"), false); - ASSERT_STREQ(temp_remove, _T("tp:/")); - - // Wrong case - temp_remove = _T("ftp:/"); - RemoveFromStart (temp_remove, _T("F"), false); - ASSERT_STREQ(temp_remove, _T("ftp:/")); - - // Remove everything - temp_remove = _T(".edu"); - RemoveFromEnd (temp_remove, _T(".edu")); - ASSERT_STREQ(temp_remove, _T("")); - - // Remove all but 1 letter - temp_remove = _T("a.edu"); - RemoveFromEnd(temp_remove, _T(".edu")); - ASSERT_STREQ(temp_remove, _T("a")); - - // Remove the first instance - temp_remove = _T(".edu.edu"); - RemoveFromEnd(temp_remove, _T(".edu")); - ASSERT_STREQ(temp_remove, _T(".edu")); - - // Remove normal - temp_remove = _T("ftp://taz the tiger.edu"); - RemoveFromEnd(temp_remove, _T(".edu")); - ASSERT_STREQ(temp_remove, _T("ftp://taz the tiger")); - - // Wrong suffix - temp_remove = _T("ftp:/taz the tiger.edu"); - RemoveFromEnd(temp_remove, _T("/edu")); - ASSERT_STREQ(temp_remove, _T("ftp:/taz the tiger.edu")); - - // Not long enough - temp_remove = _T("edu"); - RemoveFromEnd(temp_remove, _T(".edu")); - ASSERT_STREQ(temp_remove, _T("edu")); - - // Remove nothing - temp_remove = _T(".edu"); - RemoveFromEnd(temp_remove, _T("")); - ASSERT_STREQ(temp_remove, _T(".edu")); - - // Remove 1 character - temp_remove = _T(".edu"); - RemoveFromEnd(temp_remove, _T("u")); - ASSERT_STREQ(temp_remove, _T(".ed")); - - // Wrong case - temp_remove = _T(".edu"); - RemoveFromEnd(temp_remove, _T("U")); - ASSERT_STREQ(temp_remove, _T(".edu")); -} - TEST(StringTest, WideToAnsiDirect) { CString temp_convert; ASSERT_STREQ("", WideToAnsiDirect(_T(""))); @@ -1052,115 +721,36 @@ TEST(StringTest, WideToAnsiDirect) { } } -TEST(StringTest, FindStringASpaceStringB) { - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-type:", L"text/HTML")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-TYPE:", L"text/HTML")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: sdfjsldkgjsdg content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: content-type: sdfjsldkgjsdg content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:content-type: sdfjsldkgjsdg content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"test/html content-type:content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\ttext/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\t text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"Content-Type:\t text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"aasd content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"aa content-TYPE: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"text.html content-TYPE: text/HTML", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"text/html content-TYPE: text/HTML", L"content-type:", L"text/HTML")); - ASSERT_TRUE(FindStringASpaceStringB(L"AAAA content-TYPE: text/HTML", L"content-TYPE:", L"text/HTML")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:text/html AAAAA", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/htmlaaa", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html asdsdg content-type", L"content-type:", L"text/html")); - ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\ttext/htmlconttent-type:te", L"content-type:", L"text/html")); - - ASSERT_FALSE(FindStringASpaceStringB(L"content-type: a text/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type: content-type: a text/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type: b text/html content-type: a text/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type:-text/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type:\ntext/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type: a TEXT/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type: a html/text", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"a dss content-type: a text/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"text/html content-type:-text/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"text/html sdfsd fcontent-type:\ntext/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"AAAA content-type: a TEXT/html", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type: a html/text AAA", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type:", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type:content-type:", L"content-type:", L"text/html")); - ASSERT_FALSE(FindStringASpaceStringB(L"content-type:content-type: content-type:", L"content-type:", L"text/html")); -} - -TEST(StringTest, ElideIfNeeded) { - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 3, 3), L"1.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 4, 3), L"12.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 5, 3), L"123.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 6, 3), L"1234.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 7, 3), L"1234.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 8, 3), L"1234.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 9, 3), L"1234.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 10, 3), L"1234.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 11, 3), L"1234 6789.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 12, 3), L"1234 6789.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 13, 3), L"1234 6789.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 14, 3), L"1234 6789 1234"); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 15, 3), L"1234 6789 1234"); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 16, 3), L"1234 6789 1234"); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 17, 3), L"1234 6789 1234"); - - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 7, 6), L"1234.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 8, 6), L"1234 6.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 9, 6), L"1234 67.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 10, 6), L"1234 678.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 11, 6), L"1234 6789.."); - ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 12, 6), L"1234 6789.."); -} - -TEST(StringTest, SafeStrCat) { - const int kDestLen = 7; - TCHAR dest[kDestLen]; - lstrcpyn(dest, L"short", kDestLen); - ASSERT_LT(lstrlen(dest), kDestLen); - - dest[kDestLen-1] = 'a'; - lstrcpyn(dest, L"medium123", kDestLen); - ASSERT_EQ(dest[kDestLen - 1], '\0'); - ASSERT_LT(lstrlen(dest), kDestLen); - - lstrcpyn(dest, L"longerlonger", kDestLen); - ASSERT_EQ(dest[kDestLen - 1], '\0'); - ASSERT_LT(lstrlen(dest), kDestLen); - - lstrcpyn(dest, L"12", kDestLen); - SafeStrCat(dest, L"3456", kDestLen); - ASSERT_EQ(dest[kDestLen - 1], '\0'); - ASSERT_LT(lstrlen(dest), kDestLen); -} +TEST(StringTest, Base64) { + struct { + const char* binary; + const char* base64; + } test_data[] = { + "", "", + "what", "d2hhdA==", + "what will print out", "d2hhdCB3aWxsIHByaW50IG91dA==", + "foobar", "Zm9vYmFy", + "a man, a plan, a canal: panama!", "YSBtYW4sIGEgcGxhbiwgYSBjYW5hbDogcGFuYW1hIQ==", // NOLINT + }; -void TestPathFindExtension(const TCHAR *s) { - ASSERT_STREQ(String_PathFindExtension(s), PathFindExtension(s)); + for (size_t i = 0; i != arraysize(test_data); i++) { + CStringA test_e; + Base64Escape(test_data[i].binary, + strlen(test_data[i].binary), + &test_e, + true); + EXPECT_STREQ(test_e, test_data[i].base64); + + CStringA test_d; + ASSERT_GE(Base64Unescape(test_e, &test_d), 0); + EXPECT_STREQ(test_d, test_data[i].binary); + } } -TEST(StringTest, TestPathFindExtension) { - TestPathFindExtension(L"c:\\test.tmp"); - TestPathFindExtension(L"c:\\test.temp"); - TestPathFindExtension(L"c:\\t\\e\\st.temp"); - TestPathFindExtension(L"c:\\a.temp"); - TestPathFindExtension(L"\\aaa\\a.temp"); - TestPathFindExtension(L"\\a\\a.temp"); - TestPathFindExtension(L"\\a\\a.temp"); - TestPathFindExtension(L"\\a\\a.t....emp"); - TestPathFindExtension(L"\\a.a.a...a\\a.t....emp"); - TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddddddddddd.temp"); - TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddddddddddd.te___124567mp"); - TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddd.dddddddd.te___124567mp"); +TEST(StringTest, Base64Unescape) { + CStringA input("AAAAAAAA"); + CStringA output; + ASSERT_EQ(Base64Unescape(input, &output), input.GetLength() * 3 / 4); } TEST(StringTest, TextToLinesAndBack) { @@ -1199,15 +789,6 @@ TEST(StringTest, TrimString) { ASSERT_STREQ(L"", TrimStdString(L" ")); } -TEST(StringTest, StripFirstQuotedToken) { - ASSERT_STREQ(StripFirstQuotedToken(L""), L""); - ASSERT_STREQ(StripFirstQuotedToken(L"a" ), L""); - ASSERT_STREQ(StripFirstQuotedToken(L" a b "), L"b"); - ASSERT_STREQ(StripFirstQuotedToken(L"\"abc\" def"), L" def"); - ASSERT_STREQ(StripFirstQuotedToken(L" \"abc def\" ghi "), L" ghi"); - ASSERT_STREQ(StripFirstQuotedToken(L"\"abc\" \"def\" "), L" \"def\""); -} - TEST(StringTest, EscapeUnescape) { CString original_str(_T("test <>\"#{}|\\^[]?%&/")); CString escaped_str; @@ -1375,67 +956,6 @@ TEST(StringTest, BytesToHex) { _T("0123456789abcdef")); } -TEST(StringTest, JoinStrings) { - std::vector components; - const TCHAR* delim = _T("-"); - CString result; - - JoinStrings(components, delim, &result); - EXPECT_TRUE(result.IsEmpty()); - JoinStrings(components, NULL, &result); - EXPECT_TRUE(result.IsEmpty()); - - components.push_back(CString(_T("foo"))); - JoinStrings(components, delim, &result); - EXPECT_STREQ(result, (_T("foo"))); - JoinStrings(components, NULL, &result); - EXPECT_STREQ(result, (_T("foo"))); - - components.push_back(CString(_T("bar"))); - JoinStrings(components, delim, &result); - EXPECT_STREQ(result, (_T("foo-bar"))); - JoinStrings(components, NULL, &result); - EXPECT_STREQ(result, (_T("foobar"))); - - components.push_back(CString(_T("baz"))); - JoinStrings(components, delim, &result); - EXPECT_STREQ(result, (_T("foo-bar-baz"))); - JoinStrings(components, NULL, &result); - EXPECT_STREQ(result, (_T("foobarbaz"))); - - - JoinStringsInArray(NULL, 0, delim, &result); - EXPECT_TRUE(result.IsEmpty()); - JoinStringsInArray(NULL, 0, NULL, &result); - EXPECT_TRUE(result.IsEmpty()); - - const TCHAR* array1[] = {_T("foo")}; - JoinStringsInArray(array1, arraysize(array1), delim, &result); - EXPECT_STREQ(result, (_T("foo"))); - JoinStringsInArray(array1, arraysize(array1), NULL, &result); - EXPECT_STREQ(result, (_T("foo"))); - - const TCHAR* array2[] = {_T("foo"), _T("bar")}; - JoinStringsInArray(array2, arraysize(array2), delim, &result); - EXPECT_STREQ(result, (_T("foo-bar"))); - JoinStringsInArray(array2, arraysize(array2), NULL, &result); - EXPECT_STREQ(result, (_T("foobar"))); - - const TCHAR* array3[] = {_T("foo"), _T("bar"), _T("baz")}; - JoinStringsInArray(array3, arraysize(array3), delim, &result); - EXPECT_STREQ(result, (_T("foo-bar-baz"))); - JoinStringsInArray(array3, arraysize(array3), NULL, &result); - EXPECT_STREQ(result, (_T("foobarbaz"))); - - const TCHAR* array_null_1[] = {NULL}; - JoinStringsInArray(array_null_1, arraysize(array_null_1), delim, &result); - EXPECT_STREQ(result, (_T(""))); - - const TCHAR* array_null_2[] = {NULL, NULL}; - JoinStringsInArray(array_null_2, arraysize(array_null_2), delim, &result); - EXPECT_STREQ(result, (_T("-"))); -} - TEST(StringTest, String_ToUpper) { // String_ToUpper is a wrapper over ::CharUpper. TCHAR s[] = _T("foo"); diff --git a/omaha/base/synchronized.cc b/omaha/base/synchronized.cc index d7082718d..6f8e26894 100644 --- a/omaha/base/synchronized.cc +++ b/omaha/base/synchronized.cc @@ -501,9 +501,9 @@ void CreateSyncId(const TCHAR* id, SyncScope scope, CString* sync_id) { } else { ASSERT1(scope == SYNC_USER); // make the postfix the sid - VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL, + VERIFY_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, - &postfix))); + &postfix)); } break; } diff --git a/omaha/base/system.cc b/omaha/base/system.cc index 2c03f6ef7..221dfe0df 100644 --- a/omaha/base/system.cc +++ b/omaha/base/system.cc @@ -31,6 +31,7 @@ #include "omaha/base/path.h" #include "omaha/base/safe_format.h" #include "omaha/base/scope_guard.h" +#include "omaha/base/scoped_impersonation.h" #include "omaha/base/string.h" #include "omaha/base/system_info.h" #include "omaha/base/utils.h" @@ -222,6 +223,12 @@ HRESULT System::StartProcessWithEnvironment( ASSERT1(command_line || process_name); ASSERT(!process_name, (_T("Avoid using process_name. See method comment."))); + // Prevents elevation of privilege by reverting to the process token before + // starting the process. Otherwise, a lower privilege token could for instance + // symlink `C:\` to a different folder (per-user DosDevice) and allow an + // elevation of privilege attack. + scoped_revert_to_self revert_to_self; + STARTUPINFO si = {sizeof(si), 0}; // Feedback cursor is off while the process is starting. @@ -341,6 +348,12 @@ HRESULT System::StartProcessAsUserWithEnvironment( executable_path, parameters, desktop)); ASSERT1(pi); + // Prevents elevation of privilege by reverting to the process token before + // starting the process. Otherwise, a lower privilege token could for instance + // symlink `C:\` to a different folder (per-user DosDevice) and allow an + // elevation of privilege attack. + scoped_revert_to_self revert_to_self; + CString cmd(executable_path); EnclosePath(&cmd); cmd.AppendChar(_T(' ')); diff --git a/omaha/base/system_info.cc b/omaha/base/system_info.cc index e248d0df4..d17d804d0 100644 --- a/omaha/base/system_info.cc +++ b/omaha/base/system_info.cc @@ -14,6 +14,11 @@ // ======================================================================== #include "omaha/base/system_info.h" + +#include + +#include + #include "base/basictypes.h" #include "omaha/base/debug.h" #include "omaha/base/error.h" @@ -21,9 +26,107 @@ #include "omaha/base/logging.h" #include "omaha/base/process.h" #include "omaha/base/string.h" +#include "omaha/base/utils.h" +#include "omaha/base/wmi_query.h" namespace omaha { +const TCHAR* const kArchAmd64 = _T("x64"); +const TCHAR* const kArchIntel = _T("x86"); +const TCHAR* const kArchArm64 = _T("arm64"); +const TCHAR* const kArchUnknown = _T("unknown"); + +namespace { +// Returns the processor architecture as returned by `::GetNativeSystemInfo()` +// to detect the processor architecture of the installed operating system. See +// http://msdn.microsoft.com/en-us/library/ms724340.aspx. +// +// If the function is called from an x86 or x64 application running on a 64-bit +// system that does not have an Intel64 or x64 processor (such as ARM64), it +// will return information as if the system is x86 only if x86 emulation is +// supported (or x64 if x64 emulation is also supported). +DWORD GetProcessorArchitecture() { + static DWORD processor_architecture_cached(PROCESSOR_ARCHITECTURE_UNKNOWN); + + if (processor_architecture_cached == PROCESSOR_ARCHITECTURE_UNKNOWN) { + typedef void(WINAPI * GetSystemInfoFunc)(LPSYSTEM_INFO); + + HMODULE handle = ::GetModuleHandle(_T("kernel32")); + ASSERT1(handle); + GetSystemInfoFunc get_native_system_info = + reinterpret_cast( + ::GetProcAddress(handle, "GetNativeSystemInfo")); + + if (get_native_system_info != NULL) { + SYSTEM_INFO sys_info = {0}; + + get_native_system_info(&sys_info); + + processor_architecture_cached = sys_info.wProcessorArchitecture; + } else { + // If we couldn't get the _native_ system info, then we must be on OS + // earlier than XP, so can't be 64-bit anyway. Assume Intel. + processor_architecture_cached = PROCESSOR_ARCHITECTURE_INTEL; + } + } + + return processor_architecture_cached; +} + +// Returns a string representation of the processor architecture if one exists, +// or an empty string if there is no match. +CString GetProcessorArchitectureString() { + switch (GetProcessorArchitecture()) { + case PROCESSOR_ARCHITECTURE_INTEL: + return kArchIntel; + + case PROCESSOR_ARCHITECTURE_AMD64: + return kArchAmd64; + + default: + ASSERT1(false); + return kArchUnknown; + } +} + +// Returns the native architecture if the current process is running under +// WOW64 and `::IsWow64Process2()` is available. Otherwise returns +// `IMAGE_FILE_MACHINE_UNKNOWN`. +USHORT GetNativeArchitecture() { + typedef BOOL(WINAPI * IsWow64Process2Func)(HANDLE, USHORT*, USHORT*); + const IsWow64Process2Func is_wow64_process2 = + reinterpret_cast(::GetProcAddress( + ::GetModuleHandle(_T("kernel32.dll")), "IsWow64Process2")); + if (!is_wow64_process2) { + return IMAGE_FILE_MACHINE_UNKNOWN; + } + + USHORT process_machine = 0; + USHORT native_architecture = IMAGE_FILE_MACHINE_UNKNOWN; + return is_wow64_process2(::GetCurrentProcess(), &process_machine, + &native_architecture) + ? native_architecture + : IMAGE_FILE_MACHINE_UNKNOWN; +} + +// Returns a string representation of the native architecture if one exists, +// or an empty string if there is no match. +CString GetNativeArchitectureString() { + const std::map kNativeArchitectureImagesToStrings = { + {IMAGE_FILE_MACHINE_I386, kArchIntel}, + {IMAGE_FILE_MACHINE_AMD64, kArchAmd64}, + {IMAGE_FILE_MACHINE_ARM64, kArchArm64}, + }; + + const auto native_arch = + kNativeArchitectureImagesToStrings.find(GetNativeArchitecture()); + return native_arch != kNativeArchitectureImagesToStrings.end() + ? native_arch->second + : CString(_T("")); +} + +} // namespace + bool SystemInfo::IsRunningOnXPSP2OrLater() { return ::IsWindowsXPSP2OrGreater(); } @@ -109,33 +212,46 @@ bool SystemInfo::GetSystemVersion(int* major_version, return true; } -DWORD SystemInfo::GetProcessorArchitecture() { - static DWORD processor_architecture_cached(PROCESSOR_ARCHITECTURE_UNKNOWN); - - if (processor_architecture_cached == PROCESSOR_ARCHITECTURE_UNKNOWN) { - typedef void (WINAPI * GetSystemInfoFunc)(LPSYSTEM_INFO); - - HMODULE handle = ::GetModuleHandle(_T("kernel32")); - ASSERT1(handle); - GetSystemInfoFunc get_native_system_info = - reinterpret_cast(::GetProcAddress( - handle, - "GetNativeSystemInfo")); +CString SystemInfo::GetArchitecture() { + const CString native_arch = GetNativeArchitectureString(); + return !native_arch.IsEmpty() ? native_arch + : GetProcessorArchitectureString(); +} - if (get_native_system_info != NULL) { - SYSTEM_INFO sys_info = {0}; +bool SystemInfo::IsArchitectureSupported(const CString& arch) { + if (arch.IsEmpty()) { + return true; + } - get_native_system_info(&sys_info); + const CString current_arch = GetArchitecture().MakeLower(); + if (arch == current_arch) { + return true; + } - processor_architecture_cached = sys_info.wProcessorArchitecture; - } else { - // If we couldn't get the _native_ system info, then we must be on OS - // earlier than XP, so can't be 64-bit anyway. Assume Intel. - processor_architecture_cached = PROCESSOR_ARCHITECTURE_INTEL; + typedef HRESULT(WINAPI * IsWow64GuestMachineSupportedFunc)(USHORT, BOOL*); + const IsWow64GuestMachineSupportedFunc is_wow64_guest_machine_supported = + reinterpret_cast( + ::GetProcAddress(::GetModuleHandle(_T("kernel32.dll")), + "IsWow64GuestMachineSupported")); + + if (is_wow64_guest_machine_supported) { + const std::map kNativeArchitectureStringsToImages = { + {kArchIntel, IMAGE_FILE_MACHINE_I386}, + {kArchAmd64, IMAGE_FILE_MACHINE_AMD64}, + {kArchArm64, IMAGE_FILE_MACHINE_ARM64}, + }; + + const auto image = kNativeArchitectureStringsToImages.find(arch); + if (image != kNativeArchitectureStringsToImages.end()) { + BOOL is_machine_supported = false; + if (SUCCEEDED(is_wow64_guest_machine_supported( + static_cast(image->second), &is_machine_supported))) { + return is_machine_supported; + } } } - return processor_architecture_cached; + return arch == kArchIntel; } bool SystemInfo::Is64BitWindows() { @@ -149,7 +265,7 @@ bool SystemInfo::Is64BitWindows() { HRESULT SystemInfo::GetOSVersion(OSVERSIONINFOEX* os_out) { ASSERT1(os_out); - scoped_library ntdll(::LoadLibrary(_T("ntdll.dll"))); + scoped_library ntdll(LoadSystemLibrary(_T("ntdll.dll"))); if (!ntdll) { return HRESULTFromLastError(); } @@ -172,6 +288,88 @@ HRESULT SystemInfo::GetOSVersion(OSVERSIONINFOEX* os_out) { return S_OK; } +// Keep this code in sync with Chromium's // base/win/windows_version.cc. +VersionType SystemInfo::GetOSVersionType() { + OSVERSIONINFOEX version_info = {sizeof(version_info)}; + if (!::GetVersionEx(reinterpret_cast(&version_info))) { + // Windows version is XP SP1 or older, so we return a safe default. + return SUITE_HOME; + } + + if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) { + DWORD os_type = 0; + if (!GetProductInfoWrap(version_info.dwMajorVersion, + version_info.dwMinorVersion, + 0, + 0, + &os_type)) { + // We return a safe default. + return SUITE_HOME; + } + + switch (os_type) { + case PRODUCT_CLUSTER_SERVER: + case PRODUCT_DATACENTER_SERVER: + case PRODUCT_DATACENTER_SERVER_CORE: + case PRODUCT_ENTERPRISE_SERVER: + case PRODUCT_ENTERPRISE_SERVER_CORE: + case PRODUCT_ENTERPRISE_SERVER_IA64: + case PRODUCT_SMALLBUSINESS_SERVER: + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + case PRODUCT_STANDARD_SERVER: + case PRODUCT_STANDARD_SERVER_CORE: + case PRODUCT_WEB_SERVER: + return SUITE_SERVER; + + case PRODUCT_PROFESSIONAL: + case PRODUCT_ULTIMATE: + return SUITE_PROFESSIONAL; + + case PRODUCT_ENTERPRISE: + case PRODUCT_ENTERPRISE_E: + case PRODUCT_ENTERPRISE_EVALUATION: + case PRODUCT_ENTERPRISE_N: + case PRODUCT_ENTERPRISE_N_EVALUATION: + case PRODUCT_ENTERPRISE_S: + case PRODUCT_ENTERPRISE_S_EVALUATION: + case PRODUCT_ENTERPRISE_S_N: + case PRODUCT_ENTERPRISE_S_N_EVALUATION: + case PRODUCT_BUSINESS: + case PRODUCT_BUSINESS_N: + return SUITE_ENTERPRISE; + + case PRODUCT_EDUCATION: + case PRODUCT_EDUCATION_N: + return SUITE_EDUCATION; + + case PRODUCT_HOME_BASIC: + case PRODUCT_HOME_PREMIUM: + case PRODUCT_STARTER: + default: + return SUITE_HOME; + } + } else if (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion == 2) { + if (version_info.wProductType == VER_NT_WORKSTATION && + SystemInfo::GetArchitecture() == kArchAmd64) { + return SUITE_PROFESSIONAL; + } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER) { + return SUITE_HOME; + } else { + return SUITE_SERVER; + } + } else if (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion == 1) { + if (version_info.wSuiteMask & VER_SUITE_PERSONAL) + return SUITE_HOME; + else + return SUITE_PROFESSIONAL; + } else { + // Windows is pre XP so we don't care but pick a safe default. + return SUITE_HOME; + } +} + bool SystemInfo::CompareOSVersionsInternal(OSVERSIONINFOEX* os, DWORD type_mask, BYTE oper) { @@ -206,4 +404,31 @@ bool SystemInfo::CompareOSVersions(OSVERSIONINFOEX* os, BYTE oper) { CompareOSVersionsInternal(os, os_sp_type_mask, oper); } +CString SystemInfo::GetSerialNumber() { + const TCHAR kWmiLocal[] = _T("ROOT\\CIMV2"); + const TCHAR kWmiQueryBios[] = + _T("SELECT SerialNumber FROM Win32_Bios"); + const TCHAR kWmiPropSerialNumber[] = _T("SerialNumber"); + + CString serial_number; + WmiQuery wmi_query; + HRESULT hr = wmi_query.Connect(kWmiLocal); + if (FAILED(hr)) { + return CString(); + } + hr = wmi_query.Query(kWmiQueryBios); + if (FAILED(hr)) { + return CString(); + } + if (wmi_query.AtEnd()) { + return CString(); + } + hr = wmi_query.GetValue(kWmiPropSerialNumber, &serial_number); + if (FAILED(hr)) { + return CString(); + } + + return serial_number; +} + } // namespace omaha diff --git a/omaha/base/system_info.h b/omaha/base/system_info.h index 4805528ce..c85fffd27 100644 --- a/omaha/base/system_info.h +++ b/omaha/base/system_info.h @@ -27,6 +27,20 @@ namespace omaha { +extern const TCHAR* const kArchAmd64; +extern const TCHAR* const kArchIntel; +extern const TCHAR* const kArchArm64; +extern const TCHAR* const kArchUnknown; + +enum VersionType { + SUITE_HOME = 0, + SUITE_PROFESSIONAL, + SUITE_SERVER, + SUITE_ENTERPRISE, + SUITE_EDUCATION, + SUITE_LAST, +}; + // TODO(omaha): refactor to use a namespace. class SystemInfo { public: @@ -69,12 +83,20 @@ class SystemInfo { int* service_pack_major, int* service_pack_minor); - // Returns the processor architecture. We use wProcessorArchitecture in - // SYSTEM_INFO returned by ::GetNativeSystemInfo() to detect the processor - // architecture of the installed operating system. Note the "Native" in the - // function name - this is important. See - // http://msdn.microsoft.com/en-us/library/ms724340.aspx. - static DWORD GetProcessorArchitecture(); + // Returns a string representation of the processor architecture, or an empty + // string if the processor architecture is unknown. Uses `::IsWow64Process2()` + // if available (more accurate). If not, falls back on + // `::GetNativeSystemInfo()` (less accurate, but available on most systems). + static CString GetArchitecture(); + + // Returns `true` if: + //* `arch` is empty, or + // * `arch` matches the currrent architecture, or + // * `arch` is supported on the machine, as determined by + // `::IsWow64GuestMachineSupported()`. + // * If `::IsWow64GuestMachineSupported()` is not available, returns `true` + // if `arch` is x86. + static bool IsArchitectureSupported(const CString& arch); // Returns whether this is a 64-bit Windows system. static bool Is64BitWindows(); @@ -82,6 +104,12 @@ class SystemInfo { // Retrieves a full OSVERSIONINFOEX struct describing the current OS. static HRESULT GetOSVersion(OSVERSIONINFOEX* os_out); + // Returns a rough bucketing of the type of version of Windows. This is used + // to distinguish enterprise enabled versions from home versions + // and potentially server versions. Keep this code in sync with Chromium's + // base/win/windows_version.cc. + static VersionType GetOSVersionType(); + // Compares the current OS to the supplied version. The value of |oper| // should be one of the predicate values from VerSetConditionMask() -- for // example, VER_GREATER or VER_GREATER_EQUAL. @@ -91,6 +119,9 @@ class SystemInfo { // a machine running Windows 7 or later yields true. static bool CompareOSVersions(OSVERSIONINFOEX* os, BYTE oper); + // Gets the Serial Number of the machine from the BIOS via WMI. + static CString GetSerialNumber(); + private: static bool CompareOSVersionsInternal(OSVERSIONINFOEX* os, DWORD type_mask, diff --git a/omaha/base/system_info_unittest.cc b/omaha/base/system_info_unittest.cc index 5bde2d944..5936ce99b 100644 --- a/omaha/base/system_info_unittest.cc +++ b/omaha/base/system_info_unittest.cc @@ -35,22 +35,25 @@ TEST(SystemInfoTest, GetSystemVersion) { } TEST(SystemInfoTest, Is64BitWindows) { - DWORD arch(SystemInfo::GetProcessorArchitecture()); + const CString arch = SystemInfo::GetArchitecture(); - if (arch == PROCESSOR_ARCHITECTURE_INTEL) { + if (arch == kArchIntel) { EXPECT_FALSE(SystemInfo::Is64BitWindows()); } else { EXPECT_TRUE(SystemInfo::Is64BitWindows()); } } -TEST(SystemInfoTest, GetProcessorArchitecture) { - DWORD arch(SystemInfo::GetProcessorArchitecture()); +TEST(SystemInfoTest, GetArchitecture) { + const CString arch = SystemInfo::GetArchitecture(); - // TODO(omaha3): Maybe we could look for the presence of a wow6432 key in the - // registry to detect. - EXPECT_TRUE(arch == PROCESSOR_ARCHITECTURE_INTEL || - arch == PROCESSOR_ARCHITECTURE_AMD64); + EXPECT_TRUE(arch == kArchIntel || arch == kArchAmd64) << arch; +} + +TEST(SystemInfoTest, IsArchitectureSupported) { + const CString arch = SystemInfo::GetArchitecture(); + + EXPECT_TRUE(SystemInfo::IsArchitectureSupported(arch)) << arch; } TEST(SystemInfoTest, CompareOSVersions_SameAsCurrent) { @@ -177,5 +180,12 @@ TEST(SystemInfoTest, GetKernel32OSVersion) { String_StartsWith(SystemInfo::GetKernel32OSVersion(), os_version, true)); } -} // namespace omaha +TEST(SystemInfoTest, GetOSVersionType) { + EXPECT_LT(SystemInfo::GetOSVersionType(), SUITE_LAST); +} + +TEST(SystemInfoTest, GetSerialNumber) { + EXPECT_FALSE(SystemInfo::GetSerialNumber().IsEmpty()); +} +} // namespace omaha diff --git a/omaha/base/thread_pool.cc b/omaha/base/thread_pool.cc index 0956e2a4b..5a657ab24 100644 --- a/omaha/base/thread_pool.cc +++ b/omaha/base/thread_pool.cc @@ -15,7 +15,7 @@ #include "omaha/base/thread_pool.h" -#include +#include #include "omaha/base/debug.h" #include "omaha/base/error.h" @@ -24,109 +24,123 @@ namespace omaha { -namespace { - // Context keeps track the information necessary to execute a work item // inside a thread pool thread. -class Context { +class ThreadPool::Context { public: - Context(ThreadPool* pool, UserWorkItem* work_item, DWORD coinit_flags) + Context(ThreadPool* pool, std::unique_ptr work_item, DWORD coinit_flags) : pool_(pool), - work_item_(work_item), + work_item_(std::move(work_item)), coinit_flags_(coinit_flags) { - ASSERT1(pool); - ASSERT1(work_item); + ASSERT1(pool_); + ASSERT1(work_item_); } ThreadPool* pool() const { return pool_; } - UserWorkItem* work_item() const { return work_item_; } + UserWorkItem* work_item() const { return work_item_.get(); } DWORD coinit_flags() const { return coinit_flags_; } private: ThreadPool* pool_; - UserWorkItem* work_item_; + std::unique_ptr work_item_; const DWORD coinit_flags_; - DISALLOW_COPY_AND_ASSIGN(Context); + DISALLOW_COPY_AND_ASSIGN(ThreadPool::Context); }; -} // namespace - DWORD WINAPI ThreadPool::ThreadProc(void* param) { UTIL_LOG(L4, (_T("[ThreadPool::ThreadProc]"))); ASSERT1(param); - - Context* context = static_cast(param); - - scoped_co_init init_com_apt(context->coinit_flags()); - ASSERT1(SUCCEEDED(init_com_apt.hresult())); - - context->pool()->ProcessWorkItem(context->work_item()); - delete context; + std::unique_ptr context(static_cast(param)); + auto thread_pool = context->pool(); + thread_pool->ProcessWorkItemInContext(std::move(context)); return 0; } ThreadPool::ThreadPool() : work_item_count_(0), + is_stopped_(true), shutdown_delay_(0) { UTIL_LOG(L2, (_T("[ThreadPool::ThreadPool]"))); } ThreadPool::~ThreadPool() { UTIL_LOG(L2, (_T("[ThreadPool::~ThreadPool]"))); + ASSERT1(is_stopped()); + + if (HasWorkItems()) { + // Destroying a thread pool that has active work items can result in other + // race condition and non-deterministic behavior during shutdown. + ::RaiseException(EXCEPTION_ACCESS_VIOLATION, + EXCEPTION_NONCONTINUABLE, + 0, + NULL); + } +} + +HRESULT ThreadPool::Initialize(int shutdown_delay) { + set_is_stopped(false); + shutdown_delay_ = shutdown_delay; + reset(shutdown_event_, ::CreateEvent(NULL, true, false, NULL)); + return shutdown_event_ ? S_OK : HRESULTFromLastError(); +} - if (!shutdown_event_) { - return; +void ThreadPool::Stop() { + UTIL_LOG(L2, (_T("[ThreadPool::Stop]"))); + + if (is_stopped()) { + return; } DWORD baseline_tick_count = ::GetTickCount(); if (::SetEvent(get(shutdown_event_))) { - while (work_item_count_ != 0) { + while (HasWorkItems()) { ::Sleep(1); if (TimeHasElapsed(baseline_tick_count, shutdown_delay_)) { - UTIL_LOG(LE, (_T("[ThreadPool::~ThreadPool][timeout elapsed]"))); - - // Exiting a thread pool that has active work items can result in a - // race condition and undefined behavior during shutdown. - ::RaiseException(EXCEPTION_ACCESS_VIOLATION, - EXCEPTION_NONCONTINUABLE, - 0, - NULL); + UTIL_LOG(LE, (_T("[ThreadPool::Stop][timeout elapsed]"))); break; } } } -} -HRESULT ThreadPool::Initialize(int shutdown_delay) { - shutdown_delay_ = shutdown_delay; - reset(shutdown_event_, ::CreateEvent(NULL, true, false, NULL)); - return shutdown_event_ ? S_OK : HRESULTFromLastError(); + set_is_stopped(true); } -void ThreadPool::ProcessWorkItem(UserWorkItem* work_item) { - ASSERT1(work_item); - work_item->Process(); - delete work_item; +void ThreadPool::ProcessWorkItemInContext(std::unique_ptr context) { + ASSERT1(context); + + { + scoped_co_init init_com_apt(context->coinit_flags()); + ASSERT1(SUCCEEDED(init_com_apt.hresult())); + + context->work_item()->Process(); + context.reset(); + } + ::InterlockedDecrement(&work_item_count_); } -HRESULT ThreadPool::QueueUserWorkItem(UserWorkItem* work_item, +HRESULT ThreadPool::QueueUserWorkItem(std::unique_ptr work_item, DWORD coinit_flags, uint32 flags) { UTIL_LOG(L4, (_T("[ThreadPool::QueueUserWorkItem]"))); ASSERT1(work_item); - auto context = std::make_unique(this, work_item, coinit_flags); + if (is_stopped()) { + return E_FAIL; + } + work_item->set_shutdown_event(get(shutdown_event_)); + auto context = std::make_unique(this, + std::move(work_item), + coinit_flags); ::InterlockedIncrement(&work_item_count_); if (!::QueueUserWorkItem(&ThreadPool::ThreadProc, context.get(), flags)) { ::InterlockedDecrement(&work_item_count_); return HRESULTFromLastError(); } - // The thread pool has the ownership of the work item thereon. context.release(); return S_OK; } diff --git a/omaha/base/thread_pool.h b/omaha/base/thread_pool.h index 72ca797ee..96e743c6f 100644 --- a/omaha/base/thread_pool.h +++ b/omaha/base/thread_pool.h @@ -19,6 +19,8 @@ #include #include +#include + #include "base/basictypes.h" #include "omaha/third_party/smartany/scoped_any.h" @@ -56,24 +58,40 @@ class ThreadPool { ~ThreadPool(); HRESULT Initialize(int shutdown_delay); + void Stop(); - // Returns true if any work items are still in progress. - bool HasWorkItems() const { return (0 != work_item_count_); } - - // Adds a work item to the queue. If the add fails the ownership of the - // work items remains with the caller. - HRESULT QueueUserWorkItem(UserWorkItem* work_item, + // Adds a work item to the queue. + HRESULT QueueUserWorkItem(std::unique_ptr work_item, DWORD coinit_flags, uint32 flags); + bool HasWorkItems() const { + return work_item_count_ > 0; + } + private: + class Context; + // Calls UserWorkItem::Process() in the context of the worker thread. - void ProcessWorkItem(UserWorkItem* work_item); + void ProcessWorkItemInContext(std::unique_ptr context); // This is the thread callback required by the underlying windows API. static DWORD WINAPI ThreadProc(void* context); - // Approximate number of work items in the pool. + bool is_stopped() const { + return !!is_stopped_; + } + + void set_is_stopped(bool is_stopped) { + ::InterlockedExchange(&is_stopped_, is_stopped); + } + + // True if the |Stop| function has been called and all work items + // have been processed, or the |Stop| function has returned with + // a timeout. + volatile LONG is_stopped_; + + // Number of work items in the pool. volatile LONG work_item_count_; // This event signals when the thread pool destructor is in progress. diff --git a/omaha/base/thread_pool_unittest.cc b/omaha/base/thread_pool_unittest.cc index 9d84acaf1..3021f23c4 100644 --- a/omaha/base/thread_pool_unittest.cc +++ b/omaha/base/thread_pool_unittest.cc @@ -60,6 +60,51 @@ class MyJob3 : public UserWorkItem { DISALLOW_COPY_AND_ASSIGN(MyJob3); }; +class ReentrantJob3 : public UserWorkItem { + public: + explicit ReentrantJob3(ThreadPool* thread_pool) + : thread_pool_(thread_pool) {} + private: + virtual void DoProcess() { + EXPECT_TRUE(thread_pool_->HasWorkItems()); + } + ThreadPool* thread_pool_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(ReentrantJob3); +}; + +class ReentrantJob2 : public UserWorkItem { + public: + explicit ReentrantJob2(ThreadPool* thread_pool) + : thread_pool_(thread_pool) {} + private: + virtual void DoProcess() { + EXPECT_TRUE(thread_pool_->HasWorkItems()); + thread_pool_->QueueUserWorkItem( + std::make_unique(thread_pool_), + COINIT_MULTITHREADED, + WT_EXECUTEDEFAULT); + thread_pool_->Stop(); + } + ThreadPool* thread_pool_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(ReentrantJob2); +}; + +class ReentrantJob1 : public UserWorkItem { + public: + explicit ReentrantJob1(ThreadPool* thread_pool) + : thread_pool_(thread_pool) {} + private: + virtual void DoProcess() { + EXPECT_TRUE(thread_pool_->HasWorkItems()); + thread_pool_->QueueUserWorkItem( + std::make_unique(thread_pool_), + COINIT_MULTITHREADED, + WT_EXECUTEDEFAULT); + } + ThreadPool* thread_pool_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(ReentrantJob1); +}; + // ThreadPool COM initialization test class. The class tests that the thread // pool has COM initialized for the DoProcess method as well as in the Work // Item destructor. @@ -87,53 +132,33 @@ class UserWorkItemCoInitTest : public UserWorkItem { }; HRESULT QueueMyJob1(ThreadPool* thread_pool) { - std::unique_ptr job(new MyJob1); - HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), - COINIT_MULTITHREADED, - WT_EXECUTEDEFAULT); - if (FAILED(hr)) { - return hr; - } - job.release(); - return S_OK; + return thread_pool->QueueUserWorkItem(std::make_unique(), + COINIT_MULTITHREADED, + WT_EXECUTEDEFAULT); + } HRESULT QueueMyJob2(ThreadPool* thread_pool) { - std::unique_ptr job(new MyJob2); - HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), - COINIT_MULTITHREADED, - WT_EXECUTEDEFAULT); - if (FAILED(hr)) { - return hr; - } - job.release(); - return S_OK; + return thread_pool->QueueUserWorkItem(std::make_unique(), + COINIT_MULTITHREADED, + WT_EXECUTEDEFAULT); } HRESULT QueueMyJob3(ThreadPool* thread_pool) { - std::unique_ptr job(new MyJob3); - HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), - COINIT_MULTITHREADED, - WT_EXECUTEDEFAULT); - if (FAILED(hr)) { - return hr; - } - job.release(); - return S_OK; + return thread_pool->QueueUserWorkItem(std::make_unique(), + COINIT_MULTITHREADED, + WT_EXECUTEDEFAULT); } HRESULT QueueUserWorkItemCoInitTest(ThreadPool* thread_pool, DWORD coinit_flags_workitem, HRESULT coinit_expected_hresult, DWORD coinit_flags_threadpool) { - std::unique_ptr job( - new UserWorkItemCoInitTest(coinit_flags_workitem, - coinit_expected_hresult)); EXPECT_HRESULT_SUCCEEDED( - thread_pool->QueueUserWorkItem(job.get(), - coinit_flags_threadpool, - WT_EXECUTEDEFAULT)); - job.release(); + thread_pool->QueueUserWorkItem(std::make_unique( + coinit_flags_workitem, coinit_expected_hresult), + coinit_flags_threadpool, + WT_EXECUTEDEFAULT)); return S_OK; } @@ -161,6 +186,7 @@ TEST(ThreadPoolTest, ThreadPool) { ::Sleep(100); } EXPECT_EQ(g_completed_count, 6 * kNumJobsEachType); + thread_pool.Stop(); } TEST(ThreadPoolTest, UserWorkItemCoInitTest) { @@ -199,6 +225,20 @@ TEST(ThreadPoolTest, UserWorkItemCoInitTest) { t.GetMilliseconds() < kMaxWaitForJobsMs) { ::Sleep(100); } + thread_pool.Stop(); +} + +// Creates a couple of reentrant work items. A work item schedules another, +// then that work item stops the thread pool, then it schedules one more work +// item. Expects the work items to complete while the thread pool is spinning +// in its ThreadPool::Stop function. +TEST(ThreadPoolTest, Reentrant) { + ThreadPool thread_pool; + thread_pool.QueueUserWorkItem(std::make_unique(&thread_pool), + COINIT_MULTITHREADED, + WT_EXECUTEDEFAULT); + thread_pool.Stop(); + EXPECT_FALSE(thread_pool.HasWorkItems()); } } // namespace omaha diff --git a/omaha/base/time.cc b/omaha/base/time.cc index 7c4c0e88c..b9b7b4138 100644 --- a/omaha/base/time.cc +++ b/omaha/base/time.cc @@ -32,7 +32,7 @@ namespace omaha { #define kNumOfMonth 12 static const TCHAR kRFC822_DateDelimiters[] = _T(" ,:"); -static const TCHAR kRFC822_TimeDelimiter[] = _T(":"); + SELECTANY const TCHAR* kRFC822_Day[kNumOfDays] = { _T("Mon"), _T("Tue"), diff --git a/omaha/base/user_info_unittest.cc b/omaha/base/user_info_unittest.cc index 5452f78ee..e8bbd11b8 100644 --- a/omaha/base/user_info_unittest.cc +++ b/omaha/base/user_info_unittest.cc @@ -89,7 +89,6 @@ TEST(UserInfoTest, GetProcessUserSid) { SECURITY_LOCAL_SYSTEM_RID : SECURITY_NT_NON_UNIQUE; EXPECT_EQ(expected_authority, sid.GetSubAuthority(0)); - EXPECT_LT(static_cast(DOMAIN_USER_RID_MAX), sid.GetSubAuthority(4)); } // Expect the unit tests do not run impersonated. diff --git a/omaha/base/utils.cc b/omaha/base/utils.cc index de3d91623..9dca66938 100644 --- a/omaha/base/utils.cc +++ b/omaha/base/utils.cc @@ -34,9 +34,11 @@ #include "omaha/base/const_timeouts.h" #include "omaha/base/const_object_names.h" #include "omaha/base/file.h" +#include "omaha/base/path.h" #include "omaha/base/process.h" #include "omaha/base/safe_format.h" #include "omaha/base/scope_guard.h" +#include "omaha/base/scoped_impersonation.h" #include "omaha/base/shell.h" #include "omaha/base/string.h" #include "omaha/base/system.h" @@ -51,9 +53,8 @@ namespace omaha { namespace { // Private object namespaces for Vista processes. -const TCHAR* const kGoopdateBoundaryDescriptor = _T("GoogleUpdate_BD"); -const TCHAR* const kGoopdatePrivateNamespace = _T("GoogleUpdate"); -const TCHAR* const kGoopdatePrivateNamespacePrefix = _T("GoogleUpdate\\"); +const TCHAR* const kGoopdateBoundaryDescriptor = MAIN_EXE_BASE_NAME _T("_BD"); +const TCHAR* const kGoopdatePrivateNamespace = MAIN_EXE_BASE_NAME; // Helper for IsPrivateNamespaceAvailable(). // For simplicity, the handles opened here are leaked. We need these until @@ -113,6 +114,28 @@ bool EnsurePrivateNamespaceAvailable() { return false; } +// Returns true if AddDllDirectory function is available, meaning +// LOAD_LIBRARY_SEARCH_* flags are available on the host system. +bool AreSearchFlagsAvailable() { + // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have + // KB2533623 installed. To determine whether the flags are available, use + // GetProcAddress to get the address of the AddDllDirectory, + // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress + // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx. + static const auto add_dll_dir_func = + reinterpret_cast( + GetProcAddress(GetModuleHandle(_T("kernel32.dll")), + "AddDllDirectory")); + return !!add_dll_dir_func; +} + +HMODULE LoadSystemLibraryHelper(const CString& library_path) { + const DWORD kFlags = AreSearchFlagsAvailable() + ? LOAD_LIBRARY_SEARCH_SYSTEM32 + : LOAD_WITH_ALTERED_SEARCH_PATH; + return ::LoadLibraryExW(library_path.GetString(), nullptr, kFlags); +} + } // namespace // Returns 0 if an error occurs. @@ -407,7 +430,7 @@ void GetNamedObjectAttributes(const TCHAR* base_name, if (!is_machine) { CString user_sid; - VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid))); + VERIFY_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid)); attr->name += user_sid; VERIFY1(GetCurrentUserDefaultSecurityAttributes(&attr->sa)); } else { @@ -460,78 +483,6 @@ HRESULT AddAllowedAce(const TCHAR* object_name, return S_OK; } -HRESULT CreateDir(const TCHAR* in_dir, - LPSECURITY_ATTRIBUTES security_attr) { - ASSERT1(in_dir); - CString path; - if (!PathCanonicalize(CStrBuf(path, MAX_PATH), in_dir)) { - return E_FAIL; - } - // Standardize path on backslash so Find works. - path.Replace(_T('/'), _T('\\')); - int next_slash = path.Find(_T('\\')); - while (true) { - int len = 0; - if (next_slash == -1) { - len = path.GetLength(); - } else { - len = next_slash; - } - CString dir(path.Left(len)); - // The check for File::Exists should not be needed. However in certain - // cases, i.e. when the program is run from a n/w drive or from the - // root drive location, the first CreateDirectory fails with an - // E_ACCESSDENIED instead of a ALREADY_EXISTS. Hence we protect the call - // with the exists. - if (!File::Exists(dir)) { - if (!::CreateDirectory(dir, security_attr)) { - DWORD error = ::GetLastError(); - if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) { - return HRESULT_FROM_WIN32(error); - } - } - } - if (next_slash == -1) { - break; - } - next_slash = path.Find(_T('\\'), next_slash + 1); - } - - return S_OK; -} - -HRESULT GetFolderPath(int csidl, CString* path) { - if (!path) { - return E_INVALIDARG; - } - path->Empty(); - - TCHAR buffer[MAX_PATH] = {0}; - HRESULT hr = ::SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buffer); - if (FAILED(hr)) { - UTIL_LOG(LW, (_T("SHGetFolderPath failed][%d][%#x]"), csidl, hr)); - - // In locked-down environments or with registry redirection, - // ::SHGetFolderPath can fail. We try to fall back on environment variables - // for the CSIDL values below. - csidl &= CSIDL_FLAG_MASK ^ 0xFFFF; - if (csidl == CSIDL_PROGRAM_FILES) { - *path = GetEnvironmentVariableAsString(_T("ProgramFiles")); - } else if (csidl == CSIDL_LOCAL_APPDATA) { - *path = GetEnvironmentVariableAsString(_T("LocalAppData")); - } - - if (!path->IsEmpty()) { - return S_FALSE; - } - - return hr; - } - - *path = buffer; - return S_OK; -} - HRESULT DeleteDirectoryContents(const TCHAR* dir_name) { ASSERT1(dir_name); @@ -651,8 +602,9 @@ HRESULT DeleteDirectory(const TCHAR* dir_name) { else return HRESULTFromLastError(); } - // Confirm it is a directory - if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) { + // Confirm it is a non-redirected directory. + if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY) || + (dir_attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { return E_FAIL; } @@ -753,11 +705,13 @@ HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname) { } HRESULT hr = E_FAIL; + bool is_reparse_point = true; if (File::IsDirectory(targetname)) { // DeleteDirectory will schedule deletion at next reboot if it cannot delete // immediately. hr = DeleteDirectory(targetname); - } else { + } else if (SUCCEEDED(File::IsReparsePoint(targetname, &is_reparse_point)) && + !is_reparse_point) { hr = File::Remove(targetname); // If failed, schedule deletion at next reboot if (FAILED(hr)) { @@ -1429,84 +1383,6 @@ HRESULT WriteEntireFile(const TCHAR * filepath, return S_OK; } -// Conversions between a byte stream and a std::string -HRESULT BufferToString(const std::vector& buffer_in, CStringA* str_out) { - ASSERT1(str_out); - - if (buffer_in.size() > INT_MAX) { - return E_INVALIDARG; - } - - str_out->Append(reinterpret_cast(&buffer_in.front()), - static_cast(buffer_in.size())); - return S_OK; -} - -HRESULT StringToBuffer(const CStringA& str_in, std::vector* buffer_out) { - ASSERT1(buffer_out); - buffer_out->assign(str_in.GetString(), - str_in.GetString() + str_in.GetLength()); - return S_OK; -} - -HRESULT BufferToString(const std::vector& buffer_in, CString* str_out) { - ASSERT1(str_out); - - const size_t len2 = buffer_in.size(); - ASSERT1(len2 % 2 == 0); - const size_t len = len2 / 2; - - if (len > INT_MAX) { - return E_INVALIDARG; - } - - str_out->Append(reinterpret_cast(&buffer_in.front()), - static_cast(len)); - - return S_OK; -} - -HRESULT StringToBuffer(const CString& str_in, std::vector* buffer_out) { - ASSERT1(buffer_out); - - size_t len = str_in.GetLength(); - size_t len2 = len * 2; - - buffer_out->resize(len2); - ::memcpy(&buffer_out->front(), str_in.GetString(), len2); - - return S_OK; -} - -HRESULT RegSplitKeyvalueName(const CString& keyvalue_name, - CString* key_name, - CString* value_name) { - ASSERT1(key_name); - ASSERT1(value_name); - - const TCHAR kDefault[] = _T("\\(default)"); - - if (String_EndsWith(keyvalue_name, _T("\\"), false)) { - key_name->SetString(keyvalue_name, keyvalue_name.GetLength() - 1); - value_name->Empty(); - } else if (String_EndsWith(keyvalue_name, kDefault, true)) { - key_name->SetString(keyvalue_name, - keyvalue_name.GetLength() - TSTR_SIZE(kDefault)); - value_name->Empty(); - } else { - int last_slash = String_ReverseFindChar(keyvalue_name, _T('\\')); - if (last_slash == -1) { - // No slash found - bizzare and wrong - return E_FAIL; - } - key_name->SetString(keyvalue_name, last_slash); - value_name->SetString(keyvalue_name.GetString() + last_slash + 1, - keyvalue_name.GetLength() - last_slash - 1); - } - - return S_OK; -} - HRESULT ExpandEnvLikeStrings(const TCHAR* src, const std::map& keywords, CString* dest) { @@ -1616,13 +1492,13 @@ void VariantToStringList(VARIANT var, std::vector* list) { ASSERT1(obj); CComVariant var_length; - VERIFY1(SUCCEEDED(obj.GetPropertyByName(_T("length"), &var_length))); + VERIFY_SUCCEEDED(obj.GetPropertyByName(_T("length"), &var_length)); ASSERT1(V_VT(&var_length) == VT_I4); int length = V_I4(&var_length); for (int i = 0; i < length; ++i) { CComVariant value; - VERIFY1(SUCCEEDED(obj.GetPropertyByName(itostr(i), &value))); + VERIFY_SUCCEEDED(obj.GetPropertyByName(itostr(i), &value)); if (V_VT(&value) == VT_BSTR) { list->push_back(V_BSTR(&value)); } else { @@ -1887,33 +1763,17 @@ HRESULT IsUserLoggedOn(bool* is_logged_on) { return UserRights::UserIsLoggedOnInteractively(is_logged_on); } -bool IsClickOnceDisabled() { - CComPtr zone_mgr; - HRESULT hr = zone_mgr.CoCreateInstance(CLSID_InternetZoneManager); - if (FAILED(hr)) { - UTIL_LOG(LE, (_T("[InternetZoneManager CreateInstance fail][0x%08x]"), hr)); - return true; - } - - DWORD policy = URLPOLICY_DISALLOW; - const DWORD policy_size = sizeof(policy); - hr = zone_mgr->GetZoneActionPolicy(URLZONE_INTERNET, - URLACTION_MANAGED_UNSIGNED, - reinterpret_cast(&policy), - policy_size, - URLZONEREG_DEFAULT); - if (FAILED(hr)) { - UTIL_LOG(LE, (_T("[GetZoneActionPolicy failed][0x%08x]"), hr)); - return true; - } - - return policy == URLPOLICY_DISALLOW; -} - bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info) { UTIL_LOG(L3, (_T("[ShellExecuteExEnsureParent]"))); ASSERT1(shell_exec_info); + + // Prevents elevation of privilege by reverting to the process token before + // starting the process. Otherwise, a lower privilege token could for instance + // symlink `C:\` to a different folder (per-user DosDevice) and allow an + // elevation of privilege attack. + scoped_revert_to_self revert_to_self; + bool shell_exec_succeeded(false); DWORD last_error(ERROR_SUCCESS); @@ -1927,7 +1787,7 @@ bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info) { if (!hwnd_parent) { last_error = ::GetLastError(); - UTIL_LOG(LE, (_T("[CreateDummyOverlappedWindow failed]"))); + UTIL_LOG(LE, (_T("[CreateForegroundParentWindowForUAC failed]"))); // Restore last error in case the logging reset it. ::SetLastError(last_error); return false; @@ -2048,23 +1908,6 @@ HRESULT WaitForMSIExecute(int timeout_ms) { } } -CString GetEnvironmentVariableAsString(const TCHAR* name) { - UTIL_LOG(L6, (_T("GetEnvironmentVariableAsString][%s]"), name)); - - CString value; - DWORD value_length = ::GetEnvironmentVariable(name, NULL, 0); - if (value_length) { - VERIFY1(::GetEnvironmentVariable(name, - CStrBuf(value, value_length), - value_length)); - } else { - HRESULT hr = HRESULTFromLastError(); - UTIL_LOG(LW, (_T("GetEnvironmentVariable failed][%s][%#x]"), name, hr)); - } - - return value; -} - // States are documented at // http://technet.microsoft.com/en-us/library/cc721913.aspx. bool IsWindowsInstalling() { @@ -2157,20 +2000,6 @@ CString GetTempFilename(const TCHAR* prefix) { return GetTempFilenameAt(temp_dir, prefix); } -CString GetTempFilenameAt(const TCHAR* dir, const TCHAR* prefix) { - ASSERT1(dir); - ASSERT1(prefix); - - CString temp_file; - UINT result = ::GetTempFileName(dir, prefix, 0, CStrBuf(temp_file, MAX_PATH)); - ASSERT1(result != 0 && result != ERROR_BUFFER_OVERFLOW); - if (result == 0 || result == ERROR_BUFFER_OVERFLOW) { - temp_file.Empty(); - } - - return temp_file; -} - DWORD WaitForAllObjects(size_t count, const HANDLE* handles, DWORD timeout) { if (count <= MAXIMUM_WAIT_OBJECTS) { return ::WaitForMultipleObjects(static_cast(count), @@ -2223,11 +2052,6 @@ DWORD WaitForAllObjects(size_t count, const HANDLE* handles, DWORD timeout) { return abandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0; } -// The following code is cloned/derived from -// https://cs.chromium.org/chromium/src/base/win/win_util.cc. -enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED}; -static volatile LONG g_domain_state = UNKNOWN; - bool IsEnrolledToDomain() { DWORD is_enrolled(false); if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, @@ -2236,26 +2060,33 @@ bool IsEnrolledToDomain() { return !!is_enrolled; } + return EnrolledToDomainStatus() == ENROLLED; +} + +// The following code is cloned/derived from +// https://cs.chromium.org/chromium/src/base/win/win_util.cc. +static volatile LONG g_domain_state = UNKNOWN; + +DomainEnrollmentState EnrolledToDomainStatus() { if (g_domain_state == UNKNOWN) { LPWSTR domain; NETSETUP_JOIN_STATUS join_status; if (::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) { - return false; + return UNKNOWN; } ::NetApiBufferFree(domain); ::InterlockedCompareExchange(&g_domain_state, join_status == ::NetSetupDomainName ? - ENROLLED : NOT_ENROLLED, + ENROLLED : + (join_status == ::NetSetupUnknownStatus ? + UNKNOWN_ENROLLED : NOT_ENROLLED), UNKNOWN); } - return g_domain_state == ENROLLED; + return static_cast(g_domain_state); } -// TODO(omaha): This code needs to be used instead of IsEnrolledToDomain() once -// Chrome enables IsDeviceRegisteredWithManagement. - enum DeviceRegisteredState {NOT_KNOWN = -1, NOT_REGISTERED, REGISTERED}; static volatile LONG g_registered_state = NOT_KNOWN; @@ -2272,8 +2103,53 @@ bool IsDeviceRegisteredWithManagement() { return g_registered_state == REGISTERED; } +enum AzureADState {AZUREAD_NOT_KNOWN = -1, AZUREAD_NOT_JOINED, AZUREAD_JOINED}; +static volatile LONG g_azure_ad_state = AZUREAD_NOT_KNOWN; + +bool IsJoinedToAzureAD() { + if (g_azure_ad_state == AZUREAD_NOT_KNOWN) { + PDSREG_JOIN_INFO join_info = NULL; + + // |join_info| is non-NULL if the device is joined to Azure AD or the + // current user added Azure AD work accounts. + HRESULT hr(NetGetAadJoinInformationWrap(NULL, &join_info)); + ::InterlockedCompareExchange(&g_azure_ad_state, + SUCCEEDED(hr) && join_info ? + AZUREAD_JOINED : AZUREAD_NOT_JOINED, + AZUREAD_NOT_KNOWN); + if (SUCCEEDED(hr)) { + NetFreeAadJoinInformationWrap(join_info); + } + } + + return g_azure_ad_state == AZUREAD_JOINED; +} + +static volatile LONG g_os_version_type = SUITE_LAST; + bool IsEnterpriseManaged() { - return IsEnrolledToDomain() || IsDeviceRegisteredWithManagement(); + if (g_os_version_type == SUITE_LAST) { + ::InterlockedCompareExchange(&g_os_version_type, + SystemInfo::GetOSVersionType(), + SUITE_LAST); + } + + bool is_enterprise_version = (g_os_version_type != SUITE_HOME); + + return IsEnrolledToDomain() || + (is_enterprise_version && + ((EnrolledToDomainStatus() == UNKNOWN_ENROLLED) || + IsDeviceRegisteredWithManagement() || + IsJoinedToAzureAD())); +} + +HMODULE LoadSystemLibrary(const TCHAR* library_name) { + ASSERT1(!IsAbsolutePath(library_name)); + const CString system_dir = app_util::GetSystemDir(); + if (system_dir.IsEmpty()) { + return nullptr; + } + return LoadSystemLibraryHelper(ConcatenatePath(system_dir, library_name)); } } // namespace omaha diff --git a/omaha/base/utils.h b/omaha/base/utils.h index a3b3af329..cf48bfd7c 100644 --- a/omaha/base/utils.h +++ b/omaha/base/utils.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "omaha/base/constants.h" #include "omaha/base/debug.h" #include "omaha/base/error.h" +#include "omaha/base/file.h" #include "omaha/base/logging.h" #include "omaha/base/reg_key.h" #include "omaha/base/static_assert.h" @@ -75,6 +77,11 @@ ULONGLONG VersionFromString(const CString& s); CString StringFromVersion(ULONGLONG version); +// Loads a DLL from the system directory, using LOAD_LIBRARY_SEARCH_SYSTEM32, if +// the flag is supported by the OS. The presence of the feature is detected at +// runtime. `library_name` constains the name of the DLL, without the path. +HMODULE LoadSystemLibrary(const TCHAR* library_name); + // Gets current directory CString GetCurrentDir(); @@ -151,10 +158,6 @@ HRESULT IsSystemProcess(bool* is_system_process); // interactive session: console, terminal services, or fast user switching. HRESULT IsUserLoggedOn(bool* is_logged_on); -// Returns true if URLACTION_MANAGED_UNSIGNED is disabled for the Internet zone -// for the current user. -bool IsClickOnceDisabled(); - // Wrapper around ::GetProcAddress(). template bool GPA(HMODULE module, const char* function_name, T* function_pointer) { @@ -180,13 +183,17 @@ bool GPA(HMODULE module, const char* function_name, T* function_pointer) { result_error) \ typedef result_type (calling_convention *function##_pointer) proto; \ inline result_type function##Wrap proto { \ - scoped_library dll(::LoadLibrary(_T(#module))); \ - ASSERT1(dll); \ + scoped_library dll(LoadSystemLibrary(_T(#module))); \ + ASSERT(dll, (_T("::GetLastError[%d]"), ::GetLastError())); \ if (!dll) { \ return result_error; \ } \ function##_pointer fn = NULL; \ - return GPA(get(dll), #function, &fn) ? (*fn) call : result_error; \ + if (GPA(get(dll), #function, &fn)) { \ + return (*fn) call; \ + } else { \ + return result_error; \ + } \ } GPA_WRAP(RasApi32.dll, @@ -197,14 +204,6 @@ GPA_WRAP(RasApi32.dll, DWORD, ERROR_MOD_NOT_FOUND); -GPA_WRAP(kernel32.dll, - AttachConsole, - (DWORD process_id), - (process_id), - WINAPI, - BOOL, - 0); - // Private Object Namespaces for Vista and above. More information here: // http://msdn2.microsoft.com/en-us/library/ms684295(VS.85).aspx GPA_WRAP(kernel32.dll, @@ -251,10 +250,46 @@ bool IsPrivateNamespaceAvailable(); // S_OK: Created directory // S_FALSE: Directory already existed // E_FAIL: Couldn't create -HRESULT CreateDir(const TCHAR* dirname, LPSECURITY_ATTRIBUTES security_attr); +inline HRESULT CreateDir(const TCHAR* in_dir, + LPSECURITY_ATTRIBUTES security_attr) { + _ASSERTE(in_dir); + CString path; + if (!PathCanonicalize(CStrBuf(path, MAX_PATH), in_dir)) { + return E_FAIL; + } + // Standardize path on backslash so Find works. + path.Replace(_T('/'), _T('\\')); + int next_slash = path.Find(_T('\\')); + while (true) { + int len = 0; + if (next_slash == -1) { + len = path.GetLength(); + } else { + len = next_slash; + } + CString dir(path.Left(len)); + // The check for File::Exists should not be needed. However in certain + // cases, i.e. when the program is run from a n/w drive or from the + // root drive location, the first CreateDirectory fails with an + // E_ACCESSDENIED instead of a ALREADY_EXISTS. Hence we protect the call + // with the exists. + if (!File::Exists(dir)) { + if (!::CreateDirectory(dir, security_attr)) { + DWORD error = ::GetLastError(); + if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) { + return HRESULT_FROM_WIN32(error); + } + } + } + if (next_slash == -1) { + break; + } + next_slash = path.Find(_T('\\'), next_slash + 1); + } + + return S_OK; +} -// Gets the path for the specified special folder. -HRESULT GetFolderPath(int csidl, CString* path); // Returns true if this directory name is 'safe' for deletion: // - it doesn't contain ".." @@ -361,16 +396,14 @@ inline void WINAPI NullAPCFunc(ULONG_PTR) {} void EnsureRasmanLoaded(); // Returns if the HRESULT argument is a COM error -// TODO(omaha): use an ANONYMOUS_VARIABLE to avoid the situation in which the -// macro gets called like RET_IF_FAILED(hr); // For now, use a quick fix hr -> __hr. Leading underscore names are not to be // used in application code. -#define RET_IF_FAILED(x) \ - do { \ - HRESULT __hr(x); \ - if (FAILED(__hr)) { \ - return __hr; \ - } \ +#define RET_IF_FAILED(x) \ + do { \ + auto ANONYMOUS_VARIABLE(__hr)(x); \ + if (FAILED(ANONYMOUS_VARIABLE(__hr))) {\ + return ANONYMOUS_VARIABLE(__hr); \ + } \ } while (false) // return error if the first argument evaluates to false @@ -399,36 +432,35 @@ void EnsureRasmanLoaded(); // return if the HRESULT argument evaluates to FAILED - but also assert // if failed -#define RET_IF_FAILED_ASSERT(x, msg) \ - do { \ - HRESULT hr(x); \ - if (FAILED(hr)) { \ - ASSERT(false, msg); \ - return hr; \ - } \ +#define RET_IF_FAILED_ASSERT(x, msg) \ + do { \ + auto ANONYMOUS_VARIABLE(__hr)(x); \ + if (FAILED(ANONYMOUS_VARIABLE(__hr))) { \ + ASSERT(false, msg); \ + return ANONYMOUS_VARIABLE(__hr); \ + } \ } while (false) - // return if the HRESULT argument evaluates to FAILED - but also log an error // message if failed -#define RET_IF_FAILED_LOG(x, cat, msg) \ - do { \ - HRESULT hr(x); \ - if (FAILED(hr)) { \ - LC_LOG(cat, LEVEL_ERROR, msg); \ - return hr; \ - } \ +#define RET_IF_FAILED_LOG(x, cat, msg) \ + do { \ + auto ANONYMOUS_VARIABLE(__hr)(x); \ + if (FAILED(ANONYMOUS_VARIABLE(__hr))) { \ + LC_LOG(cat, LEVEL_ERROR, msg); \ + return ANONYMOUS_VARIABLE(__hr); \ + } \ } while (false) // return if the HRESULT argument evaluates to FAILED - but also REPORT an error // message if failed -#define RET_IF_FAILED_REPORT(x, msg, n) \ - do { \ - HRESULT hr(x); \ - if (FAILED(hr)) { \ - REPORT(false, R_ERROR, msg, n); \ - return hr; \ - } \ +#define RET_IF_FAILED_REPORT(x, msg, n) \ + do { \ + ANONYMOUS_VARIABLE(__hr)(x); \ + if (FAILED(ANONYMOUS_VARIABLE(__hr))) { \ + REPORT(false, R_ERROR, msg, n); \ + return ANONYMOUS_VARIABLE(__hr); \ + } \ } while (false) // Initializes a POD to zero. @@ -716,18 +748,6 @@ HRESULT ReadEntireFileShareMode(const TCHAR* filepath, HRESULT WriteEntireFile(const TCHAR * filepath, const std::vector& buffer_in); -// Conversions between a byte stream and a std::string -HRESULT BufferToString(const std::vector& buffer_in, CStringA* str_out); -HRESULT BufferToString(const std::vector& buffer_in, CString* str_out); -HRESULT StringToBuffer(const CStringA& str_in, std::vector* buffer_out); -HRESULT StringToBuffer(const CString& str_in, std::vector* buffer_out); - -// Splits a "full regkey name" into a key name part and a value name part. -// Handles "(default)" as a value name. Treats a trailing "/" as "(default)". -HRESULT RegSplitKeyvalueName(const CString& keyvalue_name, - CString* key_name, - CString* value_name); - // Expands string with embedded special variables which are enclosed // in '%' pair. For example, "%PROGRAMFILES%\Google" expands to // "C:\Program Files\Google". @@ -856,7 +876,7 @@ class LocalCallAccessPermissionHelper { RegKey key; RET_IF_FAILED(key.Open(key_app_id.Key(), T::GetAppIdT(), KEY_WRITE)); - VERIFY1(SUCCEEDED(key.DeleteValue(_T("AccessPermission")))); + VERIFY_SUCCEEDED(key.DeleteValue(_T("AccessPermission"))); // Now, call the base ATL module implementation to unregister the AppId RET_IF_FAILED(T::UpdateRegistryAppId(FALSE)); @@ -879,6 +899,23 @@ inline bool IsLocalSystemSid(const TCHAR* sid) { return _tcsicmp(sid, kLocalSystemSid) == 0; } +// Returns true if the argument is a uuid. In Microsoft parlance, a UUID is a +// GUID without the curly braces, as defined by ::UuidFromString(). +inline bool IsUuid(const CString& s) { + if (s.IsEmpty()) { + return false; + } + + // We can use ::UuidFromString() instead of the following code. However, + // ::UuidFromString() requires taking a dependency on Rpcrt4.lib, and also + // uses NT types such as RPC_STATUS for the return code and RPC_WSTR for the + // input string. So this code reuses IsGuid() instead. + CString guid(s); + guid.Insert(0, _T('{')); + guid.AppendChar(_T('}')); + return IsGuid(guid); +} + // Deletes an object. The functor is useful in for_each algorithms. struct DeleteFun { template void operator()(T ptr) { delete ptr; } @@ -898,8 +935,151 @@ HRESULT GetExePathFromCommandLine(const TCHAR* command_line, // Waits for MSI to complete, if MSI is busy installing or uninstalling apps. HRESULT WaitForMSIExecute(int timeout_ms); +// Gets the full path name to a temporary file in the specified directory. +// Returns an empty string in case of errors. +inline CString GetTempFilenameAt(const TCHAR* dir, const TCHAR* prefix) { + _ASSERTE(dir); + _ASSERTE(prefix); + + CString temp_file; + UINT result = ::GetTempFileName(dir, prefix, 0, CStrBuf(temp_file, MAX_PATH)); + if (result == 0 || result == ERROR_BUFFER_OVERFLOW) { + temp_file.Empty(); + } + + return temp_file; +} + // Returns the value of the specified environment variable. -CString GetEnvironmentVariableAsString(const TCHAR* name); +inline CString GetEnvironmentVariableAsString(const TCHAR* name) { + CString value; + DWORD value_length = ::GetEnvironmentVariable(name, NULL, 0); + if (value_length) { + ::GetEnvironmentVariable(name, CStrBuf(value, value_length), value_length); + } + + return value; +} + +// Gets the path for the specified special folder. +inline HRESULT GetFolderPath(int csidl, CString* path) { + if (!path) { + return E_INVALIDARG; + } + path->Empty(); + + TCHAR buffer[MAX_PATH] = {0}; + HRESULT hr = ::SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buffer); + if (FAILED(hr)) { + // In locked-down environments or with registry redirection, + // ::SHGetFolderPath can fail. We try to fall back on environment variables + // for the CSIDL values below. + csidl &= CSIDL_FLAG_MASK ^ 0xFFFF; + if (csidl == CSIDL_PROGRAM_FILES) { + *path = GetEnvironmentVariableAsString(_T("ProgramFiles")); + } else if (csidl == CSIDL_LOCAL_APPDATA) { + *path = GetEnvironmentVariableAsString(_T("LocalAppData")); + } + + if (!path->IsEmpty()) { + return S_FALSE; + } + + return hr; + } + + *path = buffer; + return S_OK; +} + +inline int MapCSIDLFor64Bit(int csidl) { + // We assume, for now, that Omaha will always be deployed in a 32-bit form. + // If any 64-bit components (such as the crash handler) need to query paths, + // they need to be directed to the 32-bit equivalents. + + switch (csidl) { + case CSIDL_PROGRAM_FILES: + return CSIDL_PROGRAM_FILESX86; + case CSIDL_PROGRAM_FILES_COMMON: + return CSIDL_PROGRAM_FILES_COMMONX86; + case CSIDL_SYSTEM: + return CSIDL_SYSTEMX86; + default: + return csidl; + } +} + +// GetDir32 is named as such because it will always look for 32-bit versions +// of directories; i.e. on a 64-bit OS, CSIDL_PROGRAM_FILES will return +// Program Files (x86). If we need to genuinely find 64-bit locations on +// 64-bit code in the future, we need to add a GetDir64() which omits the call +// to MapCSIDLFor64Bit(). +inline HRESULT GetDir32(int csidl, + const CString& path_tail, + bool create_dir, + CString* dir) { + _ASSERTE(dir); + +#ifdef _WIN64 + csidl = MapCSIDLFor64Bit(csidl); +#endif + + CString path; + HRESULT hr = GetFolderPath(csidl | CSIDL_FLAG_DONT_VERIFY, &path); + if (FAILED(hr)) { + return hr; + } + if (!::PathAppend(CStrBuf(path, MAX_PATH), path_tail)) { + return GOOPDATE_E_PATH_APPEND_FAILED; + } + dir->SetString(path); + + // Try to create the directory. Continue if the directory can't be created. + if (create_dir) { + CreateDir(path, NULL); + } + return S_OK; +} + +// Returns a secure temp path if the caller is admin and the path is writable by +// the caller. Returns an empty string otherwise. +inline CString GetSecureSystemTempDir() { + if (!::IsUserAnAdmin()) { + return {}; + } + + // Retrieves the path `%windir%\SystemTemp` if available, else retrieves + // `%programfiles%\Google\Temp`. + const struct { + const int csidl; + const CString path_tail; + const bool create_dir; + } keys[] = { + {CSIDL_WINDOWS, _T("SystemTemp"), false}, + {CSIDL_PROGRAM_FILES, OMAHA_REL_TEMP_DIR, true}, + }; + + for (const auto& key : keys) { + CString secure_system_temp; + const HRESULT hr = GetDir32(key.csidl, + key.path_tail, + key.create_dir, + &secure_system_temp); + if (FAILED(hr) || !File::IsDirectory(secure_system_temp)) { + continue; + } + + const CString temp_file(GetTempFilenameAt(secure_system_temp, _T("GUM"))); + if (temp_file.IsEmpty()) { + continue; + } + ::DeleteFile(temp_file); + + return secure_system_temp; + } + + return {}; +} // Returns true if the OS is installing (e.g., Audit Mode at an OEM factory). // NOTE: This is unreliable on Windows Vista and later. Some computers remain in @@ -948,10 +1128,6 @@ inline T CeilingDivide(T m, T n) { // Returns an empty string in case of errors. CString GetTempFilename(const TCHAR* prefix); -// Gets the full path name to a temporary file in the specified directory. -// Returns an empty string in case of errors. -CString GetTempFilenameAt(const TCHAR* dir, const TCHAR* prefix); - // This function is roughly equivalent to ::WaitForMultipleObjects() with the // bWaitAll parameter set to TRUE; however, it supports more than 64 handles. DWORD WaitForAllObjects(size_t count, const HANDLE* handles, DWORD timeout); @@ -968,12 +1144,52 @@ GPA_WRAP(MDMRegistration.dll, HRESULT, E_FAIL); +GPA_WRAP(kernel32.dll, + GetProductInfo, + (DWORD major_version, DWORD minor_version, DWORD sp_major, DWORD sp_minor, PDWORD product_type), // NOLINT + (major_version, minor_version, sp_major, sp_minor, product_type), + WINAPI, + BOOL, + FALSE); + +GPA_WRAP(NetApi32.dll, + NetGetAadJoinInformation, + (LPCWSTR tenant_id, PDSREG_JOIN_INFO* join_info), + (tenant_id, join_info), + NET_API_FUNCTION, + HRESULT, + E_FAIL); + +GPA_WRAP(NetApi32.dll, + NetFreeAadJoinInformation, + (PDSREG_JOIN_INFO join_info), + (join_info), + NET_API_FUNCTION, + VOID, + /* No return value for void function */); + +enum DomainEnrollmentState { + UNKNOWN = -1, + NOT_ENROLLED, + UNKNOWN_ENROLLED, + ENROLLED, +}; + +// Returns ENROLLED if ::NetGetJoinInformation() returns ::NetSetupDomainName. +// Returns UNKNOWN_ENROLLED if ::NetGetJoinInformation() returns +// ::NetSetupUnknownStatus. Returns NOT_ENROLLED in other cases. +DomainEnrollmentState EnrolledToDomainStatus(); + // Returns true if the machine is being managed by an MDM system. bool IsDeviceRegisteredWithManagement(); +// Returns true if the device is joined to Azure AD or the current user added +// Azure AD work accounts. +bool IsJoinedToAzureAD(); + // Returns true if the current machine is considered enterprise managed in some // fashion. A machine is considered managed if it is either domain enrolled -// or registered with an MDM. +// or an enterprise Windows SKU registered with an MDM. bool IsEnterpriseManaged(); } // namespace omaha diff --git a/omaha/base/utils_unittest.cc b/omaha/base/utils_unittest.cc index 7108678ec..58cd6421c 100644 --- a/omaha/base/utils_unittest.cc +++ b/omaha/base/utils_unittest.cc @@ -16,11 +16,12 @@ #include #include #include + #include #include + #include "base/rand_util.h" #include "omaha/base/app_util.h" -#include "omaha/base/atl_regexp.h" #include "omaha/base/constants.h" #include "omaha/base/dynamic_link_kernel32.h" #include "omaha/base/file.h" @@ -144,7 +145,7 @@ TEST(UtilsTest, ReadEntireFile) { ASSERT_FAILED(ReadEntireFile(L"C:\\F00Bar\\ImaginaryFile", 0, &buffer)); ASSERT_SUCCEEDED(ReadEntireFile(file_name, 0, &buffer)); - ASSERT_EQ(9405, buffer.size()); + ASSERT_TRUE(9405 == buffer.size() /*LF*/ || 9514 == buffer.size() /*CRLF*/); buffer.resize(0); ASSERT_FAILED(ReadEntireFile(L"C:\\WINDOWS\\Greenstone.bmp", 1000, &buffer)); } @@ -153,27 +154,6 @@ TEST(UtilsTest, ReadEntireFile) { // TEST(UtilsTest, WriteEntireFile) { // } -TEST(UtilsTest, RegSplitKeyvalueName) { - CString key_name, value_name; - ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\"), - &key_name, - &value_name)); - ASSERT_STREQ(key_name, L"HKLM\\Foo"); - ASSERT_TRUE(value_name.IsEmpty()); - - ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\(default)"), - &key_name, - &value_name)); - ASSERT_STREQ(key_name, L"HKLM\\Foo"); - ASSERT_TRUE(value_name.IsEmpty()); - - ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\Bar"), - &key_name, - &value_name)); - ASSERT_STREQ(key_name, L"HKLM\\Foo"); - ASSERT_STREQ(value_name, L"Bar"); -} - TEST(UtilsTest, ExpandEnvLikeStrings) { std::map mapping; ASSERT_SUCCEEDED(Shell::GetSpecialFolderKeywordsMapping(&mapping)); @@ -404,10 +384,6 @@ TEST(UtilsTest, IsUserLoggedOn) { ASSERT_TRUE(is_logged_on); } -TEST(UtilsTest, IsClickOnceDisabled) { - EXPECT_FALSE(IsClickOnceDisabled()); -} - TEST(UtilsTest, ConfigureRunAtStartup) { const TCHAR kRunKeyPath[] = _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"); @@ -551,15 +527,7 @@ TEST(UtilsTest, GetCurrentUserDefaultSecurityAttributes) { } TEST(UtilsTest, AddAllowedAce) { - CString test_file_path = ConcatenatePath( - app_util::GetCurrentModuleDirectory(), _T("TestAddAllowedAce.exe")); - EXPECT_SUCCEEDED(File::Remove(test_file_path)); - - EXPECT_SUCCEEDED(File::Copy( - ConcatenatePath(app_util::GetCurrentModuleDirectory(), - _T("GoogleUpdate.exe")), - test_file_path, - false)); + const CString test_file_path(GetTempFilename(_T("AddAllowedAce_"))); CDacl dacl; EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl)); @@ -568,7 +536,7 @@ TEST(UtilsTest, AddAllowedAce) { EXPECT_SUCCEEDED(AddAllowedAce(test_file_path, SE_FILE_OBJECT, Sids::Dialup(), - FILE_GENERIC_READ, + FILE_GENERIC_WRITE, 0)); dacl.SetEmpty(); @@ -579,18 +547,18 @@ TEST(UtilsTest, AddAllowedAce) { EXPECT_SUCCEEDED(AddAllowedAce(test_file_path, SE_FILE_OBJECT, Sids::Dialup(), - FILE_GENERIC_READ, + FILE_GENERIC_WRITE, 0)); dacl.SetEmpty(); EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl)); EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount()); // Add a subset of the existing access. No ACE is added. - EXPECT_EQ(FILE_READ_ATTRIBUTES, FILE_GENERIC_READ & FILE_READ_ATTRIBUTES); + EXPECT_EQ(FILE_WRITE_ATTRIBUTES, FILE_GENERIC_WRITE & FILE_WRITE_ATTRIBUTES); EXPECT_SUCCEEDED(AddAllowedAce(test_file_path, SE_FILE_OBJECT, Sids::Dialup(), - FILE_READ_ATTRIBUTES, + FILE_WRITE_ATTRIBUTES, 0)); dacl.SetEmpty(); EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl)); @@ -717,38 +685,29 @@ TEST(UtilsTest, interlocked_exchange_pointer) { EXPECT_EQ(static_cast(NULL), pi); } -TEST(UtilsTest, GetGuid) { +TEST(UtilsTest, GetGuid) { CString guid; EXPECT_HRESULT_SUCCEEDED(GetGuid(&guid)); - // ATL regexp has many problems including: - // * not supporting {n} to repeat a previous item n times. - // * not allowing matching on - unless the items around the dash are - // enclosed in {}. - AtlRE guid_regex(_T("^{\\{{\\h\\h\\h\\h\\h\\h\\h\\h}-{\\h\\h\\h\\h}-{\\h\\h\\h\\h}-{\\h\\h\\h\\h}-{\\h\\h\\h\\h\\h\\h\\h\\h\\h\\h\\h\\h}\\}}$")); // NOLINT - - CString matched_guid; - EXPECT_TRUE(AtlRE::PartialMatch(guid, guid_regex, &matched_guid)); - EXPECT_STREQ(guid, matched_guid); + IID iid = {0}; + EXPECT_HRESULT_SUCCEEDED(::IIDFromString(guid, &iid)); // Missing {}. - guid = _T("5F5280C6-9674-429b-9FEB-551914EF96B8"); - EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex)); + EXPECT_HRESULT_FAILED( + ::IIDFromString(_T("5F5280C6-9674-429b-9FEB-551914EF96B8"), &iid)); // Missing -. - guid = _T("{5F5280C6.9674-429b-9FEB-551914EF96B8}"); - EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex)); + EXPECT_HRESULT_FAILED( + ::IIDFromString(_T("{5F5280C6.9674-429b-9FEB-551914EF96B8}"), &iid)); // Whitespaces. - guid = _T(" {5F5280C6.9674-429b-9FEB-551914EF96B8}"); - EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex)); - - guid = _T("{5F5280C6.9674-429b-9FEB-551914EF96B8} "); - EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex)); + EXPECT_HRESULT_FAILED( + ::IIDFromString(_T(" {5F5280C6-9674-429b-9FEB-551914EF96B8}"), &iid)); + EXPECT_HRESULT_FAILED( + ::IIDFromString(_T("{5F5280C6-9674-429b-9FEB-551914EF96B8} "), &iid)); // Empty string. - guid = _T(""); - EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex)); + EXPECT_HRESULT_FAILED(::IIDFromString(_T(""), &iid)); } TEST(UtilsTest, GetMessageForSystemErrorCode) { @@ -988,5 +947,12 @@ TEST(UtilsTest, DeleteDirectoryContents_FilesAndDirs) { EXPECT_SUCCEEDED(DeleteDirectory(source_dir)); } +TEST(UtilsTest, LoadSystemLibrary) { + scoped_library winhttp_dll(LoadSystemLibrary(_T("winhttp.dll"))); + EXPECT_TRUE(winhttp_dll); + scoped_library no_dll(LoadSystemLibrary(_T("no_such_dll.dll"))); + EXPECT_FALSE(no_dll); +} + } // namespace omaha diff --git a/omaha/base/vistautil_unittest.cc b/omaha/base/vistautil_unittest.cc index 7c5d7ff98..b5babea24 100644 --- a/omaha/base/vistautil_unittest.cc +++ b/omaha/base/vistautil_unittest.cc @@ -50,7 +50,7 @@ TEST(VistaUtilTest, IsUACOn) { TEST(VistaUtilTest, IsElevatedWithUACOn) { bool is_elevated_with_uac_on(false); - VERIFY1(SUCCEEDED(vista_util::IsElevatedWithUACOn(&is_elevated_with_uac_on))); + VERIFY_SUCCEEDED(vista_util::IsElevatedWithUACOn(&is_elevated_with_uac_on)); EXPECT_EQ(IsElevatedWithEnableLUAOn(), is_elevated_with_uac_on); } diff --git a/omaha/clickonce/add_trusturlparams.py b/omaha/clickonce/add_trusturlparams.py deleted file mode 100644 index 18a7357e6..000000000 --- a/omaha/clickonce/add_trusturlparams.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2008-2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -""" -mage.exe does not provide a way to add the trustURLParameters attribute to an -application manifest. This script fills that gap. It also adds in the -localized display name, to get around issues with the Python commands -module. -""" - -import sys -import os -import getopt -import commands - - -def _AddTrustURLParametersAndName(manifest_file, output_file, display_name): - f_in = open(manifest_file, 'r') - manifest_contents = f_in.read() - f_in.close() - - manifest_contents = manifest_contents.replace(' - - - - True/PM - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/omaha/client/build.scons b/omaha/client/build.scons index 5e5576a99..cc2b08262 100644 --- a/omaha/client/build.scons +++ b/omaha/client/build.scons @@ -17,10 +17,6 @@ Import('env') local_env = env.Clone() -local_env['CPPPATH'] += [ - '$OBJ_ROOT', # Needed for generated files. - ] - inputs = [ 'bundle_creator.cc', 'bundle_installer.cc', @@ -29,6 +25,7 @@ inputs = [ 'help_url_builder.cc', 'install.cc', 'install_apps.cc', + 'install_progress_observer.cc', 'install_self.cc', 'shutdown_events.cc', 'ua.cc', diff --git a/omaha/client/bundle_creator.cc b/omaha/client/bundle_creator.cc index 4b11f0bf7..3143c678e 100644 --- a/omaha/client/bundle_creator.cc +++ b/omaha/client/bundle_creator.cc @@ -460,6 +460,109 @@ HRESULT CreateFromCommandLine(bool is_machine, return S_OK; } +HRESULT CreateForceInstallBundle(bool is_machine, + const CString& display_language, + const CString& install_source, + const CString& session_id, + bool is_interactive, + bool send_pings, + IAppBundle** app_bundle) { + CORE_LOG(L2, (_T("[bundle_creator::CreateForceInstallBundle]"))); + ASSERT1(app_bundle); + + std::vector force_install_app_ids; + HRESULT hr = ConfigManager::Instance()->GetForceInstallApps( + is_machine, + &force_install_app_ids, + NULL); + if (FAILED(hr)) { + return S_FALSE; + } + + std::vector app_ids; + for (const auto& app_id : force_install_app_ids) { + const CString app_id_key = + app_registry_utils::GetAppClientsKey(is_machine, app_id); + + if (!RegKey::HasKey(app_id_key)) { + app_ids.push_back(app_id); + } + } + + if (app_ids.empty()) { + return S_FALSE; + } + + CComPtr server; + hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class][%#x]"), hr)); + return hr; + } + + CComPtr app_bundle_ptr; + hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[CreateAppBundle failed][%#x]"), hr)); + return hr; + } + + hr = internal::SetBundleProperties(display_language, + client_utils::GetDefaultBundleName(), + install_source, + session_id, + send_pings, + app_bundle_ptr); + if (FAILED(hr)) { + return hr; + } + + hr = internal::SetAltTokens(is_machine, app_bundle_ptr); + if (FAILED(hr)) { + return hr; + } + + hr = app_bundle_ptr->put_priority(is_interactive ? + INSTALL_PRIORITY_HIGH : + INSTALL_PRIORITY_LOW); + if (FAILED(hr)) { + return hr; + } + + hr = app_bundle_ptr->initialize(); + if (FAILED(hr)) { + return hr; + } + + for (const auto& app_id_string : app_ids) { + CComBSTR app_id(app_id_string); + CComPtr app; + hr = update3_utils::CreateApp(app_id, app_bundle_ptr, &app); + if (FAILED(hr)) { + return hr; + } + + hr = app->put_displayName( + CComBSTR(client_utils::GetDefaultApplicationName())); + if (FAILED(hr)) { + return hr; + } + + const bool is_eula_accepted = + app_registry_utils::IsAppEulaAccepted(is_machine, + app_id_string, + false); + hr = app->put_isEulaAccepted(is_eula_accepted ? VARIANT_TRUE : + VARIANT_FALSE); + if (FAILED(hr)) { + return hr; + } + } + + *app_bundle = app_bundle_ptr.Detach(); + return S_OK; +} + HRESULT CreateForOnDemand(bool is_machine, const CString& app_id, const CString& install_source, diff --git a/omaha/client/bundle_creator.h b/omaha/client/bundle_creator.h index dd56e5640..261c285f0 100644 --- a/omaha/client/bundle_creator.h +++ b/omaha/client/bundle_creator.h @@ -52,6 +52,17 @@ HRESULT CreateFromCommandLine(bool is_machine, bool send_pings, IAppBundle** app_bundle); +// Creates app bundle interface by finding apps that need to be force-installed +// according to policy set by a domain administrator and that are not already +// installed. +HRESULT CreateForceInstallBundle(bool is_machine, + const CString& display_language, + const CString& install_source, + const CString& session_id, + bool is_interactive, + bool send_pings, + IAppBundle** app_bundle); + // Creates app bundle interface that contains the given app (app_id). HRESULT CreateForOnDemand(bool is_machine, const CString& app_id, diff --git a/omaha/client/bundle_creator_test.cc b/omaha/client/bundle_creator_test.cc index aa3664fdb..3046e2002 100644 --- a/omaha/client/bundle_creator_test.cc +++ b/omaha/client/bundle_creator_test.cc @@ -325,6 +325,80 @@ TEST_F(BundleCreatorTest, CreateFromCommandLine) { } } +TEST_F(BundleCreatorTest, CreateForceInstallBundle) { + const CString kDisplayLanguage = _T("en"); + const CString kInstallSource = _T("TestInstallSourceForceInstallBundle"); + const CString kSessionId = _T("{6cb069db-b073-4a40-9983-846a3819876a}"); + const bool is_machine = true; + const bool is_interactive = true; + const bool send_pings = true; + + EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain, + 1UL)); + EXPECT_SUCCEEDED(RegKey::CreateKey(kRegKeyGoopdateGroupPolicy)); + + #define APP_ID1 _T("{D9F05AEA-BEDA-4f91-B216-BE45DAE330CB}") + const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_ID1; + #define APP_ID2 _T("{EF3CACD4-89EB-46b7-B9BF-B16B15F08584}") + const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_ID2; + + EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, kPolicyForceInstallMachine)); + EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, kPolicyForceInstallMachine)); + + CComPtr app_bundle; + ASSERT_EQ(S_OK, bundle_creator::CreateForceInstallBundle(is_machine, + kDisplayLanguage, + kInstallSource, + kSessionId, + is_interactive, + send_pings, + &app_bundle)); + + CComBSTR display_name; + EXPECT_SUCCEEDED(app_bundle->get_displayName(&display_name)); + EXPECT_STREQ(client_utils::GetDefaultBundleName(), display_name); + + CComBSTR install_source; + EXPECT_SUCCEEDED(app_bundle->get_installSource(&install_source)); + EXPECT_STREQ(kInstallSource, install_source); + + CComBSTR session_id; + EXPECT_SUCCEEDED(app_bundle->get_sessionId(&session_id)); + EXPECT_STREQ(kSessionId, session_id); + + long priority = INSTALL_PRIORITY_LOW; // NOLINT(runtime/int) + EXPECT_SUCCEEDED(app_bundle->get_priority(&priority)); + EXPECT_EQ(INSTALL_PRIORITY_HIGH, priority); + + CComBSTR display_language; + EXPECT_SUCCEEDED(app_bundle->get_displayLanguage(&display_language)); + EXPECT_STREQ(kDisplayLanguage, display_language); + + long num_apps = 0; // NOLINT(runtime/int) + EXPECT_SUCCEEDED(app_bundle->get_Count(&num_apps)); + EXPECT_EQ(2, num_apps); + + for (long i = 0; i < num_apps; ++i) { // NOLINT(runtime/int) + CComPtr app; + EXPECT_SUCCEEDED(update3_utils::GetApp(app_bundle, i, &app)); + + CComBSTR app_id_bstr; + EXPECT_SUCCEEDED(app->get_appId(&app_id_bstr)); + CString app_id(app_id_bstr); + EXPECT_TRUE(!app_id.CompareNoCase(APP_ID1) || + !app_id.CompareNoCase(APP_ID2)); + + CComBSTR app_name; + EXPECT_SUCCEEDED(app->get_displayName(&app_name)); + EXPECT_STREQ(client_utils::GetDefaultApplicationName(), app_name); + } + + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain)); +} + TEST_F(BundleCreatorTest, CreateForOnDemand) { const CString& kAppId = _T("{5dace97e-9d8f-430b-acc7-ef04708b4725}"); const CString kInstallSource = _T("TestInstallSourceOnDemand"); diff --git a/omaha/client/bundle_installer.cc b/omaha/client/bundle_installer.cc index 93ca69293..640850b55 100644 --- a/omaha/client/bundle_installer.cc +++ b/omaha/client/bundle_installer.cc @@ -797,7 +797,7 @@ HRESULT BundleInstaller::HandleProcessingState() { return HandleUpdateAvailable(); case STATE_WAITING_TO_DOWNLOAD: { CComBSTR app_id; - VERIFY1(SUCCEEDED(app->get_appId(&app_id))); + VERIFY_SUCCEEDED(app->get_appId(&app_id)); observer_->OnWaitingToDownload(app_id.m_str, internal::GetAppDisplayName(app)); return S_OK; @@ -855,7 +855,7 @@ HRESULT BundleInstaller::NotifyUpdateAvailable(IApp* app) { CORE_LOG(L3, (_T("[Next Version Update Available][%s]"), CString(ver))); CComBSTR app_id; - VERIFY1(SUCCEEDED(app->get_appId(&app_id))); + VERIFY_SUCCEEDED(app->get_appId(&app_id)); // TODO(omaha3): Until we force app teams to provide a version, the string // may be empty. @@ -879,7 +879,7 @@ HRESULT BundleInstaller::NotifyDownloadProgress(IApp* app, &percentage, &next_retry_time); CComBSTR app_id; - VERIFY1(SUCCEEDED(app->get_appId(&app_id))); + VERIFY_SUCCEEDED(app->get_appId(&app_id)); if (next_retry_time != 0) { observer_->OnWaitingRetryDownload(app_id.m_str, @@ -902,7 +902,7 @@ HRESULT BundleInstaller::NotifyWaitingToInstall(IApp* app) { ASSERT1(observer_); CComBSTR app_id; - VERIFY1(SUCCEEDED(app->get_appId(&app_id))); + VERIFY_SUCCEEDED(app->get_appId(&app_id)); // can_start_install is ignored because download and install are no longer // discrete phases. @@ -926,7 +926,7 @@ HRESULT BundleInstaller::NotifyInstallProgress(IApp* app, GetAppInstallProgress(icurrent_state, &time_remaining_ms, &percentage); CComBSTR app_id; - VERIFY1(SUCCEEDED(app->get_appId(&app_id))); + VERIFY_SUCCEEDED(app->get_appId(&app_id)); observer_->OnInstalling(app_id.m_str, internal::GetAppDisplayName(app), time_remaining_ms, @@ -1053,7 +1053,7 @@ HRESULT BundleInstaller::HandleUpdateCheckResults(int* num_updates) { } ++*num_updates; - VERIFY1(SUCCEEDED(NotifyUpdateAvailable(app))); + VERIFY_SUCCEEDED(NotifyUpdateAvailable(app)); } return S_OK; @@ -1115,10 +1115,10 @@ void BundleInstaller::GetAppInstallProgress(ICurrentState* icurrent_state, ASSERT1(percentage); LONG local_time_remaining_ms = kCurrentStateProgressUnknown; - VERIFY1(SUCCEEDED( - icurrent_state->get_installTimeRemainingMs(&local_time_remaining_ms))); + VERIFY_SUCCEEDED( + icurrent_state->get_installTimeRemainingMs(&local_time_remaining_ms)); LONG local_percentage = kCurrentStateProgressUnknown; - VERIFY1(SUCCEEDED(icurrent_state->get_installProgress(&local_percentage))); + VERIFY_SUCCEEDED(icurrent_state->get_installProgress(&local_percentage)); ASSERT1(local_percentage <= 100); *time_remaining_ms = local_time_remaining_ms; @@ -1131,7 +1131,7 @@ void BundleInstaller::GetAppInstallProgress(ICurrentState* icurrent_state, void BundleInstaller::CancelBundle() { CORE_LOG(L1, (_T("[BundleInstaller::CancelBundle]"))); if (app_bundle_) { - VERIFY1(SUCCEEDED(app_bundle_->stop())); + VERIFY_SUCCEEDED(app_bundle_->stop()); } } @@ -1155,8 +1155,8 @@ void BundleInstaller::Complete(const BundleCompletionInfo& bundle_info) { } if (help_url_builder_.get()) { - VERIFY1(SUCCEEDED(help_url_builder_->BuildUrl(app_install_results, - &help_url))); + VERIFY_SUCCEEDED(help_url_builder_->BuildUrl(app_install_results, + &help_url)); ASSERT1(!help_url.IsEmpty()); } } diff --git a/omaha/client/client_utils.cc b/omaha/client/client_utils.cc index 6e28436e4..ec177177e 100644 --- a/omaha/client/client_utils.cc +++ b/omaha/client/client_utils.cc @@ -111,7 +111,7 @@ bool DisplayError(bool is_machine, // browser launch from the link will fail. Don't display a link that will not // work. if (CanLaunchBrowser()) { - VERIFY1(SUCCEEDED(url_builder.BuildUrl(app_install_result, &help_url))); + VERIFY_SUCCEEDED(url_builder.BuildUrl(app_install_result, &help_url)); } HRESULT hr = error_wnd.Initialize(); @@ -152,7 +152,7 @@ bool DisplayContinueAsNonAdmin(const CString& bundle_name, return button_id != 0; } - VERIFY1(SUCCEEDED(continue_dialog.Show())); + VERIFY_SUCCEEDED(continue_dialog.Show()); message_loop.Run(); *should_continue = continue_dialog.yes_clicked(); diff --git a/omaha/client/help_url_builder.cc b/omaha/client/help_url_builder.cc index f36a099cd..a04e58a40 100644 --- a/omaha/client/help_url_builder.cc +++ b/omaha/client/help_url_builder.cc @@ -38,7 +38,7 @@ HRESULT HelpUrlBuilder::BuildUrl(const std::vector& app_results, help_url->Empty(); CString more_info_url; - VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetMoreInfoUrl(&more_info_url))); + VERIFY_SUCCEEDED(ConfigManager::Instance()->GetMoreInfoUrl(&more_info_url)); const TCHAR* const kHelpLinkSourceId = _T("gethelp"); HRESULT hr = BuildHttpGetString(more_info_url, diff --git a/omaha/client/help_url_builder_test.cc b/omaha/client/help_url_builder_test.cc index 71871aa17..d3d37d720 100644 --- a/omaha/client/help_url_builder_test.cc +++ b/omaha/client/help_url_builder_test.cc @@ -17,8 +17,10 @@ #include #include #include + +#include #include -#include "omaha/base/atl_regexp.h" + #include "omaha/base/error.h" #include "omaha/base/omaha_version.h" #include "omaha/base/reg_key.h" @@ -49,15 +51,17 @@ int VerifyOSInUrl(const CString& url, int* length) { ASSERT1(length); *length = 0; - const AtlRE expected_os_string = - _T("{(5\\.1)|(5\\.2)|(6\\.0)|(6\\.1)|(6\\.3)|(10\\.0)\\.\\d+\\.\\d+") - _T("&sp=(Service%20Pack%20(1|2|3))?}"); + const std::wregex expected_os_string { + _T("(?:5\\.[12]|6\\.[013]|10\\.0)\\.\\d+\\.\\d+") + _T("&sp=(?:Service%20Pack%20[123])?") + }; - CString os_string; - EXPECT_TRUE(AtlRE::PartialMatch(url, expected_os_string, &os_string)); + std::wcmatch m; + EXPECT_TRUE(std::regex_search(url.GetString(), m, expected_os_string)); + EXPECT_EQ(1, m.size()); - *length = os_string.GetLength(); - return url.Find(os_string); + *length = m.length(0); + return url.Find(m.str(0).c_str()); } } // namespace @@ -354,7 +358,7 @@ TEST_F(HelpUrlBuilderTest, BuildHttpGetString_MultipleApps) { TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_User) { // The URL has a begin, middle which is OS-specific and not checked, and end. const CString kExpectedUrlBegin = - _T("https://www.google.com/support/installer/?hl=en-GB&") + _T("https://www.") COMPANY_DOMAIN _T("/support/installer/?hl=en-GB&") _T("product=%7Btest-user-app-id%7D&error=0x80004005&") _T("extra_code=-2147418113&guver=5.6.7.8&m=0&os="); const CString kExpectedUrlAfterOs = _T("iid=&brand=&source=gethelp") @@ -384,7 +388,7 @@ TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_User) { TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_Machine) { // The URL has a begin, middle which is OS-specific and not checked, and end. const CString kExpectedUrlBegin = - _T("https://www.google.com/support/installer/?hl=en-GB&") + _T("https://www.") COMPANY_DOMAIN _T("/support/installer/?hl=en-GB&") _T("product=%7Btest-machine-app-id%7D&error=0x80004004&") _T("extra_code=99&guver=5.6.7.8&m=1&os="); const CString kExpectedUrlAfterOs = @@ -417,7 +421,7 @@ TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_Machine) { TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_InstallerErrorWithExtraCode) { // The URL has a begin, middle which is OS-specific and not checked, and end. const CString kExpectedUrlBegin = - _T("https://www.google.com/support/installer/?hl=en-GB&") + _T("https://www.") COMPANY_DOMAIN _T("/support/installer/?hl=en-GB&") _T("product=AppName&error=1666&from_extra_code=1&") _T("guver=5.6.7.8&m=1&os="); const CString kExpectedUrlAfterOs = @@ -450,7 +454,7 @@ TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_InstallerErrorWithExtraCode) { TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_InstallerErrorWithoutExtraCode) { // The URL has a begin, middle which is OS-specific and not checked, and end. const CString kExpectedUrlBegin = - _T("https://www.google.com/support/installer/?hl=en-GB&") + _T("https://www.") COMPANY_DOMAIN _T("/support/installer/?hl=en-GB&") _T("product=AppName&error=0x80040902&extra_code=0&") _T("guver=5.6.7.8&m=1&os="); const CString kExpectedUrlAfterOs = diff --git a/omaha/client/install.cc b/omaha/client/install.cc index 6f0509011..3ec1e7b9a 100644 --- a/omaha/client/install.cc +++ b/omaha/client/install.cc @@ -313,14 +313,14 @@ bool CopyOfflineFiles(bool is_machine, } GUID guid(GUID_NULL); - VERIFY1(SUCCEEDED(::CoCreateGuid(&guid))); + VERIFY_SUCCEEDED(::CoCreateGuid(&guid)); CString parent_offline_dir( is_machine ? ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() : ConfigManager::Instance()->GetUserOfflineStorageDir()); CString offline_dir_guid(GuidToString(guid)); CString offline_path(ConcatenatePath(parent_offline_dir, offline_dir_guid)); - VERIFY1(SUCCEEDED(CreateDir(offline_path, NULL))); + VERIFY_SUCCEEDED(CreateDir(offline_path, NULL)); HRESULT hr = CopyOfflineManifest(offline_path); if (FAILED(hr)) { @@ -387,7 +387,7 @@ HRESULT CopyOfflineFilesForApp(const CString& app_id, CString offline_app_dir = ConcatenatePath(offline_dir, app_id); if (File::IsDirectory(offline_app_dir)) { - VERIFY1(SUCCEEDED(DeleteDirectoryFiles(offline_app_dir))); + VERIFY_SUCCEEDED(DeleteDirectoryFiles(offline_app_dir)); } else { hr = CreateDir(offline_app_dir, NULL); if (FAILED(hr)) { @@ -449,6 +449,7 @@ HRESULT LaunchHandoffProcess(bool is_machine, CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL); builder.set_is_silent_set(install_args.is_silent_set); + builder.set_is_always_launch_cmd_set(install_args.is_always_launch_cmd_set); builder.set_is_eula_required_set(install_args.is_eula_required_set); builder.set_is_enterprise_set(install_args.is_enterprise_set); builder.set_extra_args(install_args.extra_args_str); @@ -472,6 +473,7 @@ HRESULT LaunchHandoffProcess(bool is_machine, CString cmd_line = builder.GetCommandLineArgs(); HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine, + StartMode::kForeground, cmd_line, process); if (FAILED(hr)) { @@ -699,7 +701,7 @@ HRESULT Install(bool is_interactive, // with this run of Omaha. This will need to be passed along to any child // processes we create. CString session_id; - VERIFY1(SUCCEEDED(GetGuid(&session_id))); + VERIFY_SUCCEEDED(GetGuid(&session_id)); // 'current_version' corresponds to the value of 'pv' read from // registry. This value is either empty, in the case of a new install or @@ -871,7 +873,7 @@ HRESULT OemInstall(bool is_interactive, has_ui_been_displayed); if (FAILED(hr)) { - VERIFY1(SUCCEEDED(oem_install_utils::ResetOemInstallState(*is_machine))); + VERIFY_SUCCEEDED(oem_install_utils::ResetOemInstallState(*is_machine)); return hr; } diff --git a/omaha/client/install_apps.cc b/omaha/client/install_apps.cc index dc18c68bd..06150e793 100644 --- a/omaha/client/install_apps.cc +++ b/omaha/client/install_apps.cc @@ -16,16 +16,25 @@ #include "omaha/client/install_apps.h" #include +#include +#include +#include +#include + +#include "goopdate/omaha3_idl.h" +#include "omaha/base/const_addresses.h" #include "omaha/base/const_object_names.h" #include "omaha/base/debug.h" #include "omaha/base/error.h" #include "omaha/base/logging.h" #include "omaha/base/reactor.h" +#include "omaha/base/safe_format.h" #include "omaha/base/scope_guard.h" #include "omaha/base/shutdown_callback.h" #include "omaha/base/shutdown_handler.h" #include "omaha/base/string.h" +#include "omaha/base/thread_pool_callback.h" #include "omaha/base/time.h" #include "omaha/base/utils.h" #include "omaha/base/vista_utils.h" @@ -44,7 +53,7 @@ #include "omaha/common/lang.h" #include "omaha/common/ping.h" #include "omaha/common/update3_utils.h" -#include "goopdate/omaha3_idl.h" +#include "omaha/goopdate/goopdate.h" #include "omaha/ui/progress_wnd.h" namespace omaha { @@ -54,8 +63,12 @@ namespace { // Implements the UI progress window. class SilentProgressObserver : public InstallProgressObserver { public: - explicit SilentProgressObserver(BundleInstaller* installer) - : installer_(installer) { + SilentProgressObserver(BundleInstaller* installer, + bool is_machine, + bool always_launch_cmd) + : installer_(installer), + is_machine_(is_machine), + always_launch_cmd_(always_launch_cmd) { ASSERT1(installer); } @@ -135,9 +148,12 @@ class SilentProgressObserver : public InstallProgressObserver { // Terminates the message loop. virtual void OnComplete(const ObserverCompletionInfo& observer_info) { - CORE_LOG(L3, (_T("[SilentProgressObserver::OnComplete][%s]"), - observer_info.ToString())); - UNREFERENCED_PARAMETER(observer_info); + CORE_LOG(L3, (_T("[SilentProgressObserver::OnComplete][%s][%d]"), + observer_info.ToString(), always_launch_cmd_)); + + if (always_launch_cmd_) { + LaunchCommandLines(observer_info, is_machine_); + } installer_->DoExit(); CORE_LOG(L1, (_T("[SilentProgressObserver][DoExit() called]"))); @@ -145,6 +161,8 @@ class SilentProgressObserver : public InstallProgressObserver { private: BundleInstaller *const installer_; + const bool is_machine_; + const bool always_launch_cmd_; }; class OnDemandEvents : public OnDemandEventsInterface { @@ -219,6 +237,15 @@ class BundleAtlModule : public CAtlExeModuleT { namespace internal { +struct LoadLogoParameters { + public: + LoadLogoParameters(const CString& appid, HWND wnd) + : app_id(appid), logo_wnd(wnd) {} + + CString app_id; + HWND logo_wnd; +}; + bool IsBrowserRestartSupported(BrowserType browser_type) { return (browser_type != BROWSER_UNKNOWN && browser_type != BROWSER_DEFAULT && @@ -277,13 +304,13 @@ bool InstallAppsWndEvents::DoRestartBrowser(bool terminate_all_browsers, TerminateBrowserResult browser_res; TerminateBrowserResult default_res; if (terminate_all_browsers) { - VERIFY1(SUCCEEDED(goopdate_utils::TerminateAllBrowsers(browser, + VERIFY_SUCCEEDED(goopdate_utils::TerminateAllBrowsers(browser, &browser_res, - &default_res))); + &default_res)); } else { - VERIFY1(SUCCEEDED(goopdate_utils::TerminateBrowserProcesses(browser, + VERIFY_SUCCEEDED(goopdate_utils::TerminateBrowserProcesses(browser, &browser_res, - &default_res))); + &default_res)); } BrowserType default_browser_type = BROWSER_UNKNOWN; @@ -334,6 +361,96 @@ CString GetBundleDisplayName(IAppBundle* app_bundle) { CString(bundle_name) : client_utils::GetDefaultBundleName(); } +// The app logo is expected to be hosted at `{kUrlAppLogo}{url escaped +// app_id}.bmp`. If `{url escaped app_id}.bmp` exists, a logo is shown in +// the updater UI for that app install. +// +// For example, if `app_id` is `{8A69D345-D564-463C-AFF1-A69D9E530F96}`, +// the `{url escaped app_id}.bmp` is +// `%7b8A69D345-D564-463C-AFF1-A69D9E530F96%7d.bmp`. +// +// `kUrlAppLogo` is specified in omaha/base/const_addresses.h. +void LoadLogo(LoadLogoParameters params) { + CString escaped_app_id; + HRESULT hr = StringEscape(params.app_id, false, &escaped_app_id); + if (FAILED(hr)) { + CORE_LOG(LW, (_T("[StringEscape failed][%#x]"), hr)); + return; + } + + CString app_logo_url; + hr = ConfigManager::Instance()->GetAppLogoUrl(&app_logo_url); + if (FAILED(hr)) { + CORE_LOG(LW, (_T("[GetAppLogoUrl failed][%#x]"), hr)); + return; + } + + CString url; + SafeCStringFormat(&url, _T("%s%s.bmp"), app_logo_url, escaped_app_id); + CORE_LOG(L1, (_T("[Attempting to load logo from][%s]"), url)); + + // Load the logo in BMP format if it exists at the provided `url`, and set the + // resultant image onto the app bitmap for the logo window. + CComPtr picture; + hr = ::OleLoadPicturePath(const_cast(url.GetString()), nullptr, 0, 0, + IID_PPV_ARGS(&picture)); + if (FAILED(hr)) { + CORE_LOG(LW, (_T("[::OleLoadPicturePath failed][%#x]"), hr)); + return; + } + + HBITMAP bitmap = nullptr; + hr = picture->get_Handle(reinterpret_cast(&bitmap)); + if (FAILED(hr)) { + CORE_LOG(LW, (_T("[picture->get_Handle failed][%#x]"), hr)); + return; + } + + if (!::IsWindow(params.logo_wnd)) { + CORE_LOG(LW, (_T("[logo_wnd not valid anymore][%d]"), params.logo_wnd)); + return; + } + + ::SendDlgItemMessage(params.logo_wnd, IDC_APP_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, + reinterpret_cast(::CopyImage( + bitmap, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG))); +} + +// Loads the logo for the first app in `app_bundle`, and shows it in `logo_wnd`. +HRESULT LoadLogoAsync(IAppBundle* app_bundle, HWND logo_wnd) { + ASSERT1(app_bundle); + ASSERT1(logo_wnd); + + CComPtr app; + HRESULT hr = update3_utils::GetApp(app_bundle, 0, &app); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[update3_utils::GetApp failed][%#x]"), hr)); + return hr; + } + + CComBSTR primary_app_id; + hr = app->get_appId(&primary_app_id); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[app->get_appId failed][%#x]"), hr)); + return hr; + } + + // Create a thread pool work item for deferred execution of loading the logo. + // The thread pool owns this call back object. + using Callback = StaticThreadPoolCallBack1; + hr = Goopdate::Instance().QueueUserWorkItem( + std::make_unique( + &LoadLogo, LoadLogoParameters(CString(primary_app_id), logo_wnd)), + COINIT_APARTMENTTHREADED, WT_EXECUTELONGFUNCTION); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[QueueUserWorkItem failed][0x%x]"), hr)); + return hr; + } + + return S_OK; +} + HRESULT CreateClientUI(bool is_machine, BrowserType browser_type, BundleInstaller* installer, @@ -364,6 +481,13 @@ HRESULT CreateClientUI(bool is_machine, progress_wnd->Show(); installer->SetBundleParentWindow(progress_wnd->m_hWnd); + // Load the logo. + hr = LoadLogoAsync(app_bundle, progress_wnd->m_hWnd); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[LoadLogoAsync failed][%#x]"), hr)); + // Ignore error and fall through. + } + destroy_window_guard.Dismiss(); observer->reset(progress_wnd.release()); ui_sink->reset(progress_wnd_events.release()); @@ -379,6 +503,7 @@ HRESULT DoInstallApps(BundleInstaller* installer, IAppBundle* app_bundle, bool is_machine, bool is_interactive, + bool always_launch_cmd, BrowserType browser_type, bool* has_ui_been_displayed) { CORE_LOG(L2, (_T("[DoInstallApps]"))); @@ -405,7 +530,8 @@ HRESULT DoInstallApps(BundleInstaller* installer, } *has_ui_been_displayed = true; } else { - observer.reset(new SilentProgressObserver(installer)); + observer.reset( + new SilentProgressObserver(installer, is_machine, always_launch_cmd)); } hr = installer->InstallBundle(is_machine, @@ -561,6 +687,7 @@ HRESULT UpdateAppOnDemand(bool is_machine, HRESULT InstallApps(bool is_machine, bool is_interactive, + bool always_launch_cmd, bool is_eula_accepted, bool is_oem_install, bool is_offline, @@ -571,10 +698,10 @@ HRESULT InstallApps(bool is_machine, const CString& session_id, bool* has_ui_been_displayed) { CORE_LOG(L2, (_T("[InstallApps][is_machine: %u][is_interactive: %u]") - _T("[is_eula_accepted: %u][is_oem_install: %u][is_offline: %u]") - _T("[is_enterprise_install: %u][offline_directory: %s]"), is_machine, - is_interactive, is_eula_accepted, is_oem_install, is_offline, - is_enterprise_install, offline_directory)); + _T("[always_launch_cmd: %u][is_eula_accepted: %u][is_oem_install: %u]") + _T("[is_offline: %u][is_enterprise_install: %u][offline_directory: %s]"), + is_machine, is_interactive, always_launch_cmd, is_eula_accepted, + is_oem_install, is_offline, is_enterprise_install, offline_directory)); ASSERT1(has_ui_been_displayed); BundleAtlModule atl_module; @@ -625,10 +752,62 @@ HRESULT InstallApps(bool is_machine, app_bundle.Detach(), is_machine, is_interactive, + always_launch_cmd, extra_args.browser_type, has_ui_been_displayed); } +HRESULT InstallForceInstallApps(bool is_machine, + bool is_interactive, + const CString& install_source, + const CString& display_language, + const CString& session_id, + bool* has_ui_been_displayed) { + CORE_LOG(L2, (_T("[InstallForceInstallApps][%u][%u]"), + is_machine, is_interactive)); + ASSERT1(has_ui_been_displayed); + + BundleAtlModule atl_module; + const bool send_pings = true; + + CComPtr app_bundle; + HRESULT hr = bundle_creator::CreateForceInstallBundle(is_machine, + display_language, + install_source, + session_id, + is_interactive, + send_pings, + &app_bundle); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[bundle_creator::CreateForceInstallBundle][%#x]"), hr)); + return hr; + } else if (hr == S_FALSE) { + CORE_LOG(L3, (_T("[No apps to force install]"))); + return hr; + } + + BundleInstaller installer(new HelpUrlBuilder(is_machine, + display_language, + GUID_NULL, + CString()), + false, // is_update_all_apps + false, // is_update_check_only + BROWSER_UNKNOWN); + hr = installer.Initialize(); + if (FAILED(hr)) { + return hr; + } + + atl_module.enable_quit(); + return internal::DoInstallApps(&installer, + app_bundle.Detach(), + is_machine, + is_interactive, + /*always_launch_cmd=*/false, + BROWSER_UNKNOWN, + has_ui_been_displayed); +} + HRESULT UpdateAllApps(bool is_machine, bool is_interactive, const CString& install_source, @@ -672,6 +851,7 @@ HRESULT UpdateAllApps(bool is_machine, app_bundle.Detach(), is_machine, is_interactive, + /*always_launch_cmd=*/false, BROWSER_UNKNOWN, has_ui_been_displayed); } diff --git a/omaha/client/install_apps.h b/omaha/client/install_apps.h index 8605ea407..945903303 100644 --- a/omaha/client/install_apps.h +++ b/omaha/client/install_apps.h @@ -52,6 +52,7 @@ HRESULT UpdateAppOnDemand(bool is_machine, HRESULT InstallApps(bool is_machine, bool is_interactive, + bool always_launch_cmd, bool is_eula_accepted, bool is_oem_install, bool is_offline, @@ -62,6 +63,13 @@ HRESULT InstallApps(bool is_machine, const CString& session_id, bool* has_ui_been_displayed); +HRESULT InstallForceInstallApps(bool is_machine, + bool is_interactive, + const CString& install_source, + const CString& display_language, + const CString& session_id, + bool* has_ui_been_displayed); + HRESULT UpdateAllApps(bool is_machine, bool is_interactive, const CString& install_source, diff --git a/omaha/client/install_progress_observer.cc b/omaha/client/install_progress_observer.cc new file mode 100644 index 000000000..6107224d6 --- /dev/null +++ b/omaha/client/install_progress_observer.cc @@ -0,0 +1,74 @@ +// Copyright 2024 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +#include "omaha/client/install_progress_observer.h" + +#include "omaha/base/debug.h" +#include "omaha/base/logging.h" +#include "omaha/common/goopdate_utils.h" + +namespace omaha { + +namespace { + +HRESULT LaunchCommandLine(const AppCompletionInfo& app_info, bool is_machine) { + CORE_LOG(L3, (_T("[LaunchCommandLine][%s]"), + app_info.post_install_launch_command_line)); + if (app_info.post_install_launch_command_line.IsEmpty()) { + return S_OK; + } + + if (app_info.completion_code != COMPLETION_CODE_LAUNCH_COMMAND && + app_info.completion_code != + COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND) { + CORE_LOG(LW, (_T("Launch command line [%s] is not empty but completion ") + _T("code [%d] doesn't require a launch"), + app_info.post_install_launch_command_line.GetString(), + app_info.completion_code)); + return S_OK; + } + + ASSERT1(SUCCEEDED(app_info.error_code)); + ASSERT1(!app_info.is_noupdate); + + HRESULT hr = goopdate_utils::LaunchCmdLine( + is_machine, app_info.post_install_launch_command_line, NULL, NULL); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[goopdate_utils::LaunchCommandLine failed][0x%x]"), hr)); + return hr; + } + + return S_OK; +} + +} // namespace + +bool LaunchCommandLines(const ObserverCompletionInfo& info, bool is_machine) { + bool result = true; + + CORE_LOG(L3, (_T("[LaunchCommandLines]"))); + for (size_t i = 0; i < info.apps_info.size(); ++i) { + const AppCompletionInfo& app_info = info.apps_info[i]; + if (FAILED(app_info.error_code)) { + continue; + } + result &= SUCCEEDED(LaunchCommandLine(app_info, is_machine)); + VERIFY1(result); + } + + return result; +} + +} // namespace omaha diff --git a/omaha/client/install_progress_observer.h b/omaha/client/install_progress_observer.h index 3def26d66..9cf2d7158 100644 --- a/omaha/client/install_progress_observer.h +++ b/omaha/client/install_progress_observer.h @@ -32,7 +32,7 @@ namespace omaha { // restart browser AND launchcmd? // If we keep this enum, rename to something like CompletionTypes to make it // clear that it is describing the desired behavior of the observer. This also -// differentiates the name from LegacyCompetionCodes. +// differentiates the name from LegacyCompletionCodes. // TODO(omaha): If the codes below change, need a conversion method to convert // to LegacyCompletionCodes. typedef enum { @@ -155,6 +155,9 @@ class InstallProgressObserver { virtual void OnComplete(const ObserverCompletionInfo& observer_info) = 0; }; +// Launches the post-install launch command lines for each app in `info`. +bool LaunchCommandLines(const ObserverCompletionInfo& info, bool is_machine); + } // namespace omaha #endif // OMAHA_CLIENT_INSTALL_PROGRESS_OBSERVER_H_ diff --git a/omaha/client/install_self.cc b/omaha/client/install_self.cc index 0e0a81ce8..75140521f 100644 --- a/omaha/client/install_self.cc +++ b/omaha/client/install_self.cc @@ -56,7 +56,11 @@ HRESULT DoSelfUpdate(bool is_machine, int* extra_code1) { *extra_code1 = 0; - HRESULT hr = DoInstallSelf(is_machine, true, false, false, extra_code1); + HRESULT hr = DoInstallSelf(is_machine, + true, + false, + RUNTIME_MODE_NOT_SET, + extra_code1); if (FAILED(hr)) { PersistUpdateErrorInfo(is_machine, hr, *extra_code1, GetVersionString()); return hr; @@ -66,11 +70,11 @@ HRESULT DoSelfUpdate(bool is_machine, int* extra_code1) { } // Does not need to update the UI during Omaha install. This should be quick -// with a simple throbbing UI. UI will transition when product install begins. +// with a simple UI. UI will transition when product install begins. HRESULT DoInstallSelf(bool is_machine, bool is_self_update, bool is_eula_required, - bool set_keepalive, + RuntimeMode runtime_mode, int* extra_code1) { ASSERT1(extra_code1); ASSERT1(!is_self_update || !is_eula_required); @@ -104,7 +108,7 @@ HRESULT DoInstallSelf(bool is_machine, Setup setup(is_machine); setup.set_is_self_update(is_self_update); - hr = setup.Install(set_keepalive); + hr = setup.Install(runtime_mode); *extra_code1 = setup.extra_code1(); if (FAILED(hr)) { @@ -198,15 +202,15 @@ void PersistUpdateErrorInfo(bool is_machine, const CString& version) { const TCHAR* update_key_name = ConfigManager::Instance()->registry_update(is_machine); - VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name, + VERIFY_SUCCEEDED(RegKey::SetValue(update_key_name, kRegValueSelfUpdateErrorCode, - static_cast(error)))); - VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name, + static_cast(error))); + VERIFY_SUCCEEDED(RegKey::SetValue(update_key_name, kRegValueSelfUpdateExtraCode1, - static_cast(extra_code1)))); - VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name, + static_cast(extra_code1))); + VERIFY_SUCCEEDED(RegKey::SetValue(update_key_name, kRegValueSelfUpdateVersion, - version))); + version)); } } // namespace internal @@ -235,14 +239,14 @@ bool ReadAndClearUpdateErrorInfo(bool is_machine, return false; } - VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateErrorCode, - error_code))); + VERIFY_SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateErrorCode, + error_code)); ASSERT1(FAILED(*error_code)); - VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateExtraCode1, - extra_code1))); + VERIFY_SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateExtraCode1, + extra_code1)); - VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateVersion, version))); + VERIFY_SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateVersion, version)); if (FAILED(update_key.DeleteValue(kRegValueSelfUpdateErrorCode)) || FAILED(update_key.DeleteValue(kRegValueSelfUpdateExtraCode1)) || @@ -278,7 +282,7 @@ HRESULT InstallSelf(bool is_machine, HRESULT hr = internal::DoInstallSelf(is_machine, false, is_eula_required, - extra_args.runtime_only, + extra_args.runtime_mode, extra_code1); if (FAILED(hr)) { CORE_LOG(LE, (_T("[DoInstallSelf failed][0x%08x]"), hr)); @@ -293,14 +297,14 @@ HRESULT InstallSelf(bool is_machine, ConfigManager::Instance()->registry_client_state_goopdate(is_machine); // TODO(omaha): move SetInstallationId to app_registry_utils - VERIFY1(SUCCEEDED(internal::SetInstallationId(omaha_client_state_key_path, - extra_args.installation_id))); - VERIFY1(SUCCEEDED(ExperimentLabels::WriteRegistry( - is_machine, kGoogleUpdateAppId, extra_args.experiment_labels))); - VERIFY1(SUCCEEDED(app_registry_utils::SetGoogleUpdateBranding( + VERIFY_SUCCEEDED(internal::SetInstallationId(omaha_client_state_key_path, + extra_args.installation_id)); + VERIFY_SUCCEEDED(ExperimentLabels::WriteRegistry( + is_machine, kGoogleUpdateAppId, extra_args.experiment_labels)); + VERIFY_SUCCEEDED(app_registry_utils::SetGoogleUpdateBranding( omaha_client_state_key_path, extra_args.brand_code, - extra_args.client_id))); + extra_args.client_id)); if (is_eula_required || is_oem_install || is_enterprise_install) { return S_OK; diff --git a/omaha/client/install_self_internal.h b/omaha/client/install_self_internal.h index abee77fd2..015f4ee29 100644 --- a/omaha/client/install_self_internal.h +++ b/omaha/client/install_self_internal.h @@ -19,6 +19,8 @@ #include #include +#include "omaha/common/const_goopdate.h" + namespace omaha { namespace install_self { @@ -32,7 +34,7 @@ HRESULT DoSelfUpdate(bool is_machine, int* extra_code1); HRESULT DoInstallSelf(bool is_machine, bool is_self_update, bool is_eula_required, - bool set_keepalive, + RuntimeMode runtime_mode, int* extra_code1); // Checks that the Omaha system requirements are met. Returns an error if not. diff --git a/omaha/client/install_self_unittest.cc b/omaha/client/install_self_unittest.cc index 43608dcd5..afdf6de32 100644 --- a/omaha/client/install_self_unittest.cc +++ b/omaha/client/install_self_unittest.cc @@ -27,11 +27,11 @@ namespace install_self { namespace { const TCHAR* const kAppMachineClientStatePath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\"); const TCHAR* const kAppUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\"); } // namespace diff --git a/omaha/client/install_self_unittest_no_xml_parser.cc b/omaha/client/install_self_unittest_no_xml_parser.cc index b9215027c..9bb8a3d19 100644 --- a/omaha/client/install_self_unittest_no_xml_parser.cc +++ b/omaha/client/install_self_unittest_no_xml_parser.cc @@ -59,7 +59,7 @@ TEST_F(RegistryProtectedTest, DISABLED_InstallOmaha_XmlParserNotPresent) { install_self::internal::DoInstallSelf(false, false, false, - false, + RUNTIME_MODE_NOT_SET, &extra_code1)); EXPECT_EQ(0, extra_code1); EXPECT_FALSE(RegKey::HasKey(USER_REG_GOOGLE)); diff --git a/omaha/client/ua.cc b/omaha/client/ua.cc index 4193926a2..f2479b759 100644 --- a/omaha/client/ua.cc +++ b/omaha/client/ua.cc @@ -99,39 +99,6 @@ void WriteUpdateAppsStartEvent(bool is_machine) { update_event.WriteEvent(); } -// If kRegValueIsMSIHelperRegistered is 0, the MSI helper is registered. -HRESULT RegisterMSIHelperIfNeeded(bool is_machine) { - const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE; - DWORD is_registered(0); - VERIFY1(SUCCEEDED(RegKey::GetValue(key_name, - kRegValueIsMSIHelperRegistered, - &is_registered))); - if (is_registered) { - return S_OK; - } - - CommandLineBuilder builder(COMMANDLINE_MODE_REGISTER_MSI_HELPER); - const CString cmd_line = builder.GetCommandLineArgs(); - scoped_process process; - const HRESULT hr(goopdate_utils::StartGoogleUpdateWithArgs(is_machine, - cmd_line, - address(process))); - if (FAILED(hr)) { - SETUP_LOG(LE, (_T("[RegisterMsiHelper mode failed to start][%#x]"), hr)); - return hr; - } - - const int kMaxWaitForRegisterMsiProcessMs = 30000; - const DWORD result(::WaitForSingleObject(get(process), - kMaxWaitForRegisterMsiProcessMs)); - DWORD exit_code(static_cast(E_UNEXPECTED)); - VERIFY1(result == WAIT_OBJECT_0 && - ::GetExitCodeProcess(get(process), &exit_code) && - SUCCEEDED(exit_code)); - - return exit_code; -} - // Ensures there is only one instance of /ua per session per Omaha instance. bool EnsureSingleUAProcess(bool is_machine, std::unique_ptr* instance) { @@ -187,7 +154,7 @@ bool ShouldCheckForUpdates(bool is_machine) { bool should_check_for_updates = false; - if (ConfigManager::AreUpdatesSuppressedNow()) { + if (ConfigManager::Instance()->AreUpdatesSuppressedNow()) { should_check_for_updates = false; } else if (time_since_last_check < update_interval) { // Too soon. @@ -211,8 +178,8 @@ bool ShouldCheckForUpdates(bool is_machine) { should_check_for_updates = true; } - CORE_LOG(L3, (_T("[ShouldCheckForUpdates returned %d][%u]"), - should_check_for_updates, is_period_overridden)); + OPT_LOG(L3, (_T("[ShouldCheckForUpdates returned %d][%u]"), + should_check_for_updates, is_period_overridden)); return should_check_for_updates; } @@ -254,17 +221,15 @@ HRESULT UpdateApps(bool is_machine, return GOOPDATE_E_UA_ALREADY_RUNNING; } - VERIFY1(SUCCEEDED(ConfigManager::Instance()->SetLastStartedAU(is_machine))); - - VERIFY1(SUCCEEDED(RegisterMSIHelperIfNeeded(is_machine))); + VERIFY_SUCCEEDED(ConfigManager::Instance()->SetLastStartedAU(is_machine)); if (ConfigManager::Instance()->CanUseNetwork(is_machine)) { - VERIFY1(SUCCEEDED(Ping::SendPersistedPings(is_machine))); + VERIFY_SUCCEEDED(Ping::SendPersistedPings(is_machine)); } // Generate a session ID for network accesses. CString session_id; - VERIFY1(SUCCEEDED(GetGuid(&session_id))); + VERIFY_SUCCEEDED(GetGuid(&session_id)); // A tentative uninstall check is done here. There are stronger checks, // protected by locks, which are done by Setup. @@ -281,6 +246,25 @@ HRESULT UpdateApps(bool is_machine, return goopdate_utils::LaunchUninstallProcess(is_machine); } + // We first install any apps that need to be force-installed according to + // policy set by a domain administrator. + HRESULT hr = InstallForceInstallApps(is_machine, + is_interactive, + install_source, + display_language, + session_id, + has_ui_been_displayed); + if (FAILED(hr)) { + CORE_LOG(LW, (_T("[InstallForceInstallApps failed][%#x]"), hr)); + } + + // InstallForceInstallApps creates a BundleAtlModule instance on the stack, so + // we reset the _pAtlModule to allow for a fresh ATL module for UpdateAllApps. + _pAtlModule = NULL; + + // Generate a new session ID for UpdateAllApps. + VERIFY_SUCCEEDED(GetGuid(&session_id)); + const bool should_check_for_updates = ShouldCheckForUpdates(is_machine); if (!(is_on_demand || should_check_for_updates)) { OPT_LOG(L1, (_T("[Update check not needed at this time]"))); @@ -295,14 +279,14 @@ HRESULT UpdateApps(bool is_machine, ::Sleep(au_jitter_ms); } - HRESULT hr = UpdateAllApps(is_machine, - is_interactive, - install_source, - display_language, - session_id, - has_ui_been_displayed); + hr = UpdateAllApps(is_machine, + is_interactive, + install_source, + display_language, + session_id, + has_ui_been_displayed); if (FAILED(hr)) { - CORE_LOG(LW, (_T("[UpdateAllApps failed][0x%08x]"), hr)); + OPT_LOG(LW, (_T("[UpdateAllApps failed][0x%08x]"), hr)); } return hr; } diff --git a/omaha/client/ua_unittest.cc b/omaha/client/ua_unittest.cc index 607d49d83..448f44d30 100644 --- a/omaha/client/ua_unittest.cc +++ b/omaha/client/ua_unittest.cc @@ -64,6 +64,8 @@ class UATest : public testing::TestWithParam > { RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueLastCheckPeriodSec); + + ConfigManager::DeleteInstance(); } DISALLOW_COPY_AND_ASSIGN(UATest); @@ -118,10 +120,7 @@ TEST_P(UATest, ShouldCheckForUpdates_LastCheckedInFuture) { } TEST_P(UATest, ShouldCheckForUpdates_PeriodZero) { - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueAutoUpdateCheckPeriodOverrideMinutes, - static_cast(0))); + EXPECT_SUCCEEDED(SetPolicy(kRegValueAutoUpdateCheckPeriodOverrideMinutes, 0)); EXPECT_EQ(!is_domain_, ShouldCheckForUpdates(is_machine_)); } @@ -131,10 +130,8 @@ TEST_P(UATest, ShouldCheckForUpdates_PeriodOverride) { const DWORD kOverrideSeconds = kOverrideMinutes * 60; const uint32 now = Time64ToInt32(GetCurrent100NSTime()); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueAutoUpdateCheckPeriodOverrideMinutes, - kOverrideMinutes)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueAutoUpdateCheckPeriodOverrideMinutes, + kOverrideMinutes)); ConfigManager::Instance()->SetLastCheckedTime(is_machine_, now - 10); EXPECT_FALSE(ShouldCheckForUpdates(is_machine_)); @@ -175,10 +172,8 @@ TEST_P(UATest, ShouldCheckForUpdates_SkipUpdate) { // Verify the overriding the update check period is not causing skips. const DWORD kOverrideMinutes = last_check_period_sec_ / 60; - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueAutoUpdateCheckPeriodOverrideMinutes, - kOverrideMinutes)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueAutoUpdateCheckPeriodOverrideMinutes, + kOverrideMinutes)); EXPECT_TRUE(ShouldCheckForUpdates(is_machine_)); } @@ -204,25 +199,15 @@ TEST_P(UATest, ShouldCheckForUpdates_UpdatesSuppressed) { now.GetLocalTm(&local); EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartHour, - static_cast(local.tm_hour))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartMin, - static_cast(0))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedDurationMin, - static_cast(60))); + SetPolicy(kRegValueUpdatesSuppressedStartHour, local.tm_hour)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartMin, 0)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedDurationMin, 60)); EXPECT_EQ(!is_domain_, ShouldCheckForUpdates(is_machine_)); if (local.tm_min) { EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedDurationMin, - static_cast(local.tm_min - 1))); + SetPolicy(kRegValueUpdatesSuppressedDurationMin, local.tm_min - 1)); EXPECT_EQ(true, ShouldCheckForUpdates(is_machine_)); } @@ -233,18 +218,9 @@ TEST_P(UATest, ShouldCheckForUpdates_UpdatesSuppressed_InvalidHour) { tm local = {}; now.GetLocalTm(&local); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartHour, - static_cast(26))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartMin, - static_cast(0))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedDurationMin, - static_cast(60))); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartHour, 26)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartMin, 0)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedDurationMin, 60)); EXPECT_EQ(true, ShouldCheckForUpdates(is_machine_)); } @@ -255,17 +231,9 @@ TEST_P(UATest, ShouldCheckForUpdates_UpdatesSuppressed_InvalidMin) { now.GetLocalTm(&local); EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartHour, - static_cast(local.tm_hour))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartMin, - static_cast(456))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedDurationMin, - static_cast(60))); + SetPolicy(kRegValueUpdatesSuppressedStartHour, local.tm_hour)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartMin, 456)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedDurationMin, 60)); EXPECT_EQ(true, ShouldCheckForUpdates(is_machine_)); } @@ -276,17 +244,10 @@ TEST_P(UATest, ShouldCheckForUpdates_UpdatesSuppressed_InvalidDuration) { now.GetLocalTm(&local); EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartHour, - static_cast(local.tm_hour))); - EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartMin, - static_cast(0))); + SetPolicy(kRegValueUpdatesSuppressedStartHour, local.tm_hour)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartMin, 0)); EXPECT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedDurationMin, - static_cast(200 * kMinPerHour))); + SetPolicy(kRegValueUpdatesSuppressedDurationMin, 200 * kMinPerHour)); EXPECT_EQ(true, ShouldCheckForUpdates(is_machine_)); } diff --git a/omaha/common/app_registry_utils.cc b/omaha/common/app_registry_utils.cc index 7ab22ee3a..1ee2a8df5 100644 --- a/omaha/common/app_registry_utils.cc +++ b/omaha/common/app_registry_utils.cc @@ -89,9 +89,9 @@ bool IsAppEulaAccepted(bool is_machine, return false; } - VERIFY1(SUCCEEDED(RegKey::SetValue(state_key, + VERIFY_SUCCEEDED(RegKey::SetValue(state_key, kRegValueEulaAccepted, - eula_accepted))); + eula_accepted)); return true; } @@ -201,16 +201,16 @@ HRESULT SetInitialDayOfValues(const CString& client_state_key_path, if (num_days_since_datum == 0) { initial_day_of_install = kInitialValue; } - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueDayOfInstall, - initial_day_of_install))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueDayOfInstall, + initial_day_of_install)); if (!state_key.HasValue(kRegValueDayOfLastActivity)) { - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueDayOfLastActivity, - kInitialValue))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueDayOfLastActivity, + kInitialValue)); } if (!state_key.HasValue(kRegValueDayOfLastRollCall)) { - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueDayOfLastRollCall, - kInitialValue))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueDayOfLastRollCall, + kInitialValue)); } return S_OK; } @@ -242,7 +242,7 @@ HRESULT SetGoogleUpdateBranding(const CString& client_state_key_path, if (FAILED(state_key.GetValue(kRegValueInstallTimeSec, &install_time)) || !install_time) { const DWORD now = Time64ToInt32(GetCurrent100NSTime()); - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)); CORE_LOG(L3, (_T("[InstallTime missing. Setting it here.][%u]"), now)); } @@ -279,8 +279,8 @@ HRESULT SetAppBranding(const CString& client_state_key_path, ASSERT1(SUCCEEDED(hr)); if (existing_brand_code.GetLength() > kBrandIdLength) { // Bug 1358852: Brand code garbled with one click. - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueBrandCode, - existing_brand_code.Left(kBrandIdLength)))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueBrandCode, + existing_brand_code.Left(kBrandIdLength))); } return S_OK; } @@ -308,9 +308,9 @@ HRESULT SetAppBranding(const CString& client_state_key_path, } const DWORD now = Time64ToInt32(GetCurrent100NSTime()); - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now))); - VERIFY1(SUCCEEDED(SetInitialDayOfValues(client_state_key_path, - num_days_since_datum))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)); + VERIFY_SUCCEEDED(SetInitialDayOfValues(client_state_key_path, + num_days_since_datum)); return S_OK; } @@ -337,9 +337,9 @@ void PersistSuccessfulInstall(const CString& client_state_key_path, if (is_update) { const DWORD now = Time64ToInt32(GetCurrent100NSTime()); - VERIFY1(SUCCEEDED(RegKey::SetValue(client_state_key_path, + VERIFY_SUCCEEDED(RegKey::SetValue(client_state_key_path, kRegValueLastUpdateTimeSec, - now))); + now)); } } @@ -347,9 +347,9 @@ void PersistSuccessfulUpdateCheck(const CString& client_state_key_path) { CORE_LOG(L3, (_T("[app_registry_utils::PersistSuccessfulUpdateCheck][%s]"), client_state_key_path)); const DWORD now = Time64ToInt32(GetCurrent100NSTime()); - VERIFY1(SUCCEEDED(RegKey::SetValue(client_state_key_path, + VERIFY_SUCCEEDED(RegKey::SetValue(client_state_key_path, kRegValueLastSuccessfulCheckSec, - now))); + now)); } void ClearUpdateAvailableStats(const CString& client_state_key_path) { @@ -362,8 +362,8 @@ void ClearUpdateAvailableStats(const CString& client_state_key_path) { return; } - VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableCount))); - VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableSince))); + VERIFY_SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableCount)); + VERIFY_SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableSince)); } HRESULT GetNumClients(bool is_machine, size_t* num_clients) { @@ -575,8 +575,8 @@ HRESULT WriteCohort(bool is_machine, return hr; } - VERIFY1(SUCCEEDED(cohort_key.SetValue(kRegValueCohortHint, cohort.hint))); - VERIFY1(SUCCEEDED(cohort_key.SetValue(kRegValueCohortName, cohort.name))); + VERIFY_SUCCEEDED(cohort_key.SetValue(kRegValueCohortHint, cohort.hint)); + VERIFY_SUCCEEDED(cohort_key.SetValue(kRegValueCohortName, cohort.name)); return S_OK; } diff --git a/omaha/common/app_registry_utils_unittest.cc b/omaha/common/app_registry_utils_unittest.cc index 4d78754ab..b83ff9e42 100644 --- a/omaha/common/app_registry_utils_unittest.cc +++ b/omaha/common/app_registry_utils_unittest.cc @@ -26,34 +26,34 @@ namespace { #define APP_GUID _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}") const TCHAR* const kAppGuid = APP_GUID; const TCHAR* const kAppMachineClientStatePath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") APP_GUID; const TCHAR* const kAppUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") APP_GUID; const TCHAR* const kAppMachineClientStateMediumPath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\") APP_GUID; // This should never exist. This contant is only used to verify it is not used. const TCHAR* const kAppUserClientStateMediumPath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\") APP_GUID; const TCHAR* const kOmahaMachineClientsPath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") GOOPDATE_APP_ID; const TCHAR* const kOmahaUserClientsPath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") GOOPDATE_APP_ID; const TCHAR* const kOmahaMachineClientStatePath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") GOOPDATE_APP_ID; const TCHAR* const kOmahaUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") GOOPDATE_APP_ID; int GetFirstDayOfWeek(int day) { @@ -72,11 +72,11 @@ namespace app_registry_utils { TEST(AppRegistryUtilsTest, GetAppClientsKey) { const TCHAR kAppGuid1[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"); - EXPECT_STREQ(_T("HKCU\\Software\\") SHORT_COMPANY_NAME + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"), GetAppClientsKey(false, kAppGuid1)); - EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"), GetAppClientsKey(true, kAppGuid1)); @@ -85,11 +85,11 @@ TEST(AppRegistryUtilsTest, GetAppClientsKey) { TEST(AppRegistryUtilsTest, GetAppClientStateKey) { const TCHAR kAppGuid1[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"); - EXPECT_STREQ(_T("HKCU\\Software\\") SHORT_COMPANY_NAME + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"), GetAppClientStateKey(false, kAppGuid1)); - EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"), GetAppClientStateKey(true, kAppGuid1)); @@ -99,7 +99,7 @@ TEST(AppRegistryUtilsTest, GetAppClientStateKey) { TEST(AppRegistryUtilsTest, GetAppClientStateMediumKey_User) { const TCHAR kAppGuid1[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"); ExpectAsserts expect_asserts; - EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\") _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"), GetAppClientStateMediumKey(false, kAppGuid1)); @@ -107,7 +107,7 @@ TEST(AppRegistryUtilsTest, GetAppClientStateMediumKey_User) { TEST(AppRegistryUtilsTest, GetAppClientStateMediumKey_Machine) { const TCHAR kAppGuid1[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"); - EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\") _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"), GetAppClientStateMediumKey(true, kAppGuid1)); @@ -140,11 +140,11 @@ class AppRegistryUtilsRegistryProtectedTest : ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true)); } - const bool IsMachine() { + bool IsMachine() { return GetParam(); } - const CString GetClientStatePath() { + CString GetClientStatePath() { return IsMachine() ? kOmahaMachineClientStatePath : kOmahaUserClientStatePath; } diff --git a/omaha/common/build.scons b/omaha/common/build.scons index 09ca81a43..033d60fd7 100644 --- a/omaha/common/build.scons +++ b/omaha/common/build.scons @@ -18,10 +18,6 @@ Import('env') def BuildCommonLib(env): local_env = env.Clone() - local_env['CPPPATH'] += [ - '$OBJ_ROOT', # Needed for generated files. - ] - inputs = [ 'app_registry_utils.cc', 'command_line.cc', @@ -46,7 +42,6 @@ def BuildCommonLib(env): 'update_request.cc', 'update_response.cc', 'url_utils.cc', - 'webplugin_utils.cc', 'web_services_client.cc', 'xml_const.cc', 'xml_parser.cc', diff --git a/omaha/common/command_line.h b/omaha/common/command_line.h index 6a2732bf5..db9e77004 100644 --- a/omaha/common/command_line.h +++ b/omaha/common/command_line.h @@ -60,7 +60,7 @@ enum CommandLineMode { COMMANDLINE_MODE_SERVICE = 3, COMMANDLINE_MODE_REGSERVER = 4, COMMANDLINE_MODE_UNREGSERVER = 5, - COMMANDLINE_MODE_NETDIAGS = 6, + // Obsolete: COMMANDLINE_MODE_NETDIAGS = 6, COMMANDLINE_MODE_CRASH = 7, COMMANDLINE_MODE_REPORTCRASH = 8, COMMANDLINE_MODE_INSTALL = 9, @@ -70,7 +70,7 @@ enum CommandLineMode { // Obsolete: COMMANDLINE_MODE_UG = 13, COMMANDLINE_MODE_UA = 14, COMMANDLINE_MODE_RECOVER = 15, - COMMANDLINE_MODE_WEBPLUGIN = 16, + // Obsolete: COMMANDLINE_MODE_WEBPLUGIN = 16, COMMANDLINE_MODE_CODE_RED_CHECK = 17, COMMANDLINE_MODE_COMSERVER = 18, // Obsolete: COMMANDLINE_MODE_LEGACYUI = 19, @@ -86,7 +86,7 @@ enum CommandLineMode { COMMANDLINE_MODE_UNINSTALL = 29, COMMANDLINE_MODE_PING = 30, COMMANDLINE_MODE_HEALTH_CHECK = 31, - COMMANDLINE_MODE_REGISTER_MSI_HELPER = 32, + // Obsolete: COMMANDLINE_MODE_REGISTER_MSI_HELPER = 32, }; struct CommandLineExtraArgs { @@ -94,7 +94,7 @@ struct CommandLineExtraArgs { : installation_id(GUID_NULL), browser_type(BROWSER_UNKNOWN), usage_stats_enable(TRISTATE_NONE), - runtime_only(false) {} + runtime_mode(RUNTIME_MODE_NOT_SET) {} CString bundle_name; GUID installation_id; @@ -108,7 +108,7 @@ struct CommandLineExtraArgs { #endif BrowserType browser_type; Tristate usage_stats_enable; - bool runtime_only; + RuntimeMode runtime_mode; std::vector apps; }; @@ -121,6 +121,7 @@ struct CommandLineArgs { is_crash_handler_disabled(false), is_install_elevated(false), is_silent_set(false), + is_always_launch_cmd_set(false), is_eula_required_set(false), is_offline_set(false), is_enterprise_set(false), @@ -132,6 +133,7 @@ struct CommandLineArgs { bool is_crash_handler_disabled; bool is_install_elevated; bool is_silent_set; + bool is_always_launch_cmd_set; bool is_eula_required_set; bool is_offline_set; bool is_enterprise_set; @@ -142,8 +144,6 @@ struct CommandLineArgs { CString crash_filename; CString custom_info_filename; CString legacy_manifest_path; - CString webplugin_urldomain; - CString webplugin_args; CString code_red_metainstaller_path; CString ping_string; CString offline_dir_name; diff --git a/omaha/common/command_line_builder.cc b/omaha/common/command_line_builder.cc index 6e7e4489f..f30926a6b 100644 --- a/omaha/common/command_line_builder.cc +++ b/omaha/common/command_line_builder.cc @@ -32,6 +32,7 @@ CommandLineBuilder::CommandLineBuilder(CommandLineMode mode) is_interactive_set_(false), is_machine_set_(false), is_silent_set_(false), + is_always_launch_cmd_set_(false), is_eula_required_set_(false), is_enterprise_set_(false) { } @@ -56,6 +57,13 @@ void CommandLineBuilder::set_is_silent_set(bool is_silent_set) { is_silent_set_ = is_silent_set; } +void CommandLineBuilder::set_is_always_launch_cmd_set( + bool is_always_launch_cmd_set) { + ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL || + mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL); + is_always_launch_cmd_set_ = is_always_launch_cmd_set; +} + void CommandLineBuilder::set_is_eula_required_set(bool is_eula_required_set) { ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL || mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL); @@ -83,8 +91,7 @@ void CommandLineBuilder::set_app_args(const CString& app_args) { } void CommandLineBuilder::set_install_source(const CString& install_source) { - ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN || - mode_ == COMMANDLINE_MODE_INSTALL || + ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL || mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL || mode_ == COMMANDLINE_MODE_UA); install_source_ = install_source; @@ -108,17 +115,6 @@ void CommandLineBuilder::set_custom_info_filename( custom_info_filename_ = custom_info_filename; } -void CommandLineBuilder::set_webplugin_url_domain( - const CString& webplugin_url_domain) { - ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN); - webplugin_url_domain_ = webplugin_url_domain; -} - -void CommandLineBuilder::set_webplugin_args(const CString& webplugin_args) { - ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN); - webplugin_args_ = webplugin_args; -} - void CommandLineBuilder::set_code_red_metainstaller_path( const CString& code_red_metainstaller_path) { ASSERT1(mode_ == COMMANDLINE_MODE_RECOVER); @@ -165,9 +161,6 @@ CString CommandLineBuilder::GetCommandLineArgs() const { case COMMANDLINE_MODE_UNREGSERVER: cmd_line_args = GetUnregServer(); break; - case COMMANDLINE_MODE_NETDIAGS: - cmd_line_args = GetNetDiags(); - break; case COMMANDLINE_MODE_CRASH: cmd_line_args = GetCrash(); break; @@ -189,9 +182,6 @@ CString CommandLineBuilder::GetCommandLineArgs() const { case COMMANDLINE_MODE_RECOVER: cmd_line_args = GetRecover(); break; - case COMMANDLINE_MODE_WEBPLUGIN: - cmd_line_args = GetWebPlugin(); - break; case COMMANDLINE_MODE_CODE_RED_CHECK: cmd_line_args = GetCodeRedCheck(); break; @@ -231,9 +221,6 @@ CString CommandLineBuilder::GetCommandLineArgs() const { case COMMANDLINE_MODE_HEALTH_CHECK: cmd_line_args = GetHealthCheck(); break; - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: - cmd_line_args = GetRegisterMsiHelper(); - break; case COMMANDLINE_MODE_UNKNOWN: default: ASSERT1(false); @@ -302,10 +289,6 @@ CString CommandLineBuilder::GetUnregServer() const { return GetSingleSwitch(kCmdUnregServer); } -CString CommandLineBuilder::GetNetDiags() const { - return GetSingleSwitch(kCmdLineNetDiags); -} - CString CommandLineBuilder::GetCrash() const { CString cmd_line = GetSingleSwitch(kCmdLineCrash); return cmd_line; @@ -385,6 +368,9 @@ CString CommandLineBuilder::GetInstall() const { if (is_silent_set_) { SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineSilent); } + if (is_always_launch_cmd_set_) { + SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineAlwaysLaunchCmd); + } if (is_enterprise_set_) { SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineEnterprise); } @@ -421,6 +407,9 @@ CString CommandLineBuilder::GetHandoffInstall() const { if (is_silent_set_) { SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineSilent); } + if (is_always_launch_cmd_set_) { + SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineAlwaysLaunchCmd); + } if (is_eula_required_set_) { SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineEulaRequired); } @@ -464,30 +453,6 @@ CString CommandLineBuilder::GetRecover() const { return cmd_line; } -CString CommandLineBuilder::GetWebPlugin() const { - ASSERT1(!webplugin_url_domain_.IsEmpty()); - ASSERT1(!webplugin_args_.IsEmpty()); - ASSERT1(!install_source_.IsEmpty()); - if (webplugin_url_domain_.IsEmpty() || - webplugin_args_.IsEmpty() || - install_source_.IsEmpty()) { - return CString(); - } - CString cmd_line; - CString enclosed_webplugin_url_domain_(webplugin_url_domain_); - CString enclosed_webplugin_args_(webplugin_args_); - EnclosePath(&enclosed_webplugin_url_domain_); - EnclosePath(&enclosed_webplugin_args_); - // TODO(omaha): Do we want this to handle the urlencoding for us? - SafeCStringFormat(&cmd_line, _T("/%s %s %s /%s %s"), - kCmdLineWebPlugin, - enclosed_webplugin_url_domain_.GetString(), - enclosed_webplugin_args_.GetString(), - kCmdLineInstallSource, - install_source_.GetString()); - return cmd_line; -} - CString CommandLineBuilder::GetCodeRedCheck() const { return GetSingleSwitch(kCmdLineCodeRedCheck); } @@ -533,8 +498,4 @@ CString CommandLineBuilder::GetHealthCheck() const { return GetSingleSwitch(kCmdLineHealthCheck); } -CString CommandLineBuilder::GetRegisterMsiHelper() const { - return GetSingleSwitch(kCmdLineRegisterMsiHelper); -} - } // namespace omaha diff --git a/omaha/common/command_line_builder.h b/omaha/common/command_line_builder.h index 4cbec672a..c0fcb773f 100644 --- a/omaha/common/command_line_builder.h +++ b/omaha/common/command_line_builder.h @@ -42,6 +42,9 @@ class CommandLineBuilder { bool is_silent_set() const { return is_silent_set_; } void set_is_silent_set(bool is_silent_set); + bool is_always_launch_cmd_set() const { return is_always_launch_cmd_set_; } + void set_is_always_launch_cmd_set(bool is_always_launch_cmd_set); + bool is_eula_required_set() const { return is_eula_required_set_; } void set_is_eula_required_set(bool is_eula_required_set); @@ -66,12 +69,6 @@ class CommandLineBuilder { CString custom_info_filename() const { return custom_info_filename_; } void set_custom_info_filename(const CString& custom_info_filename); - CString webplugin_url_domain() const { return webplugin_url_domain_; } - void set_webplugin_url_domain(const CString& webplugin_url_domain); - - CString webplugin_args() const { return webplugin_args_; } - void set_webplugin_args(const CString& webplugin_args); - CString code_red_metainstaller_path() const { return code_red_metainstaller_path_; } @@ -107,7 +104,6 @@ class CommandLineBuilder { CString GetServiceUnregister() const; CString GetRegServer() const; CString GetUnregServer() const; - CString GetNetDiags() const; CString GetCrash() const; CString GetReportCrash() const; CString GetInstall() const; @@ -115,7 +111,6 @@ class CommandLineBuilder { CString GetHandoffInstall() const; CString GetUA() const; CString GetRecover() const; - CString GetWebPlugin() const; CString GetCodeRedCheck() const; CString GetComServer() const; CString GetComBroker() const; @@ -132,6 +127,7 @@ class CommandLineBuilder { bool is_interactive_set_; bool is_machine_set_; bool is_silent_set_; + bool is_always_launch_cmd_set_; bool is_eula_required_set_; bool is_enterprise_set_; CString extra_args_; @@ -139,8 +135,6 @@ class CommandLineBuilder { CString install_source_; CString crash_filename_; CString custom_info_filename_; - CString webplugin_url_domain_; - CString webplugin_args_; CString code_red_metainstaller_path_; CString ping_string_; CString offline_dir_name_; diff --git a/omaha/common/command_line_builder_unittest.cc b/omaha/common/command_line_builder_unittest.cc index 783f031b2..a9b2faa70 100644 --- a/omaha/common/command_line_builder_unittest.cc +++ b/omaha/common/command_line_builder_unittest.cc @@ -61,12 +61,6 @@ TEST(CommandLineBuilder, BuildUnregServer) { EXPECT_STREQ(_T("/unregserver"), cmd_line); } -TEST(CommandLineBuilder, BuildNetDiags) { - CommandLineBuilder builder(COMMANDLINE_MODE_NETDIAGS); - CString cmd_line = builder.GetCommandLineArgs(); - EXPECT_STREQ(_T("/netdiags"), cmd_line); -} - TEST(CommandLineBuilder, BuildCrashNoFilename) { CommandLineBuilder builder(COMMANDLINE_MODE_CRASH); CString cmd_line = builder.GetCommandLineArgs(); @@ -186,6 +180,20 @@ TEST(CommandLineBuilder, BuildInstallWithExtraArgsSilent) { cmd_line); } +TEST(CommandLineBuilder, BuildInstallWithExtraArgsSilentAndAlwaysLaunchCmd) { + CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL); + builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") + _T("appname=YouTubeUploader&needsadmin=False&") + _T("lang=en")); + builder.set_is_silent_set(true); + builder.set_is_always_launch_cmd_set(true); + CString cmd_line = builder.GetCommandLineArgs(); + EXPECT_STREQ(_T("/install \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") + _T("appname=YouTubeUploader&needsadmin=False&lang=en\" /silent ") + _T("/alwayslaunchcmd"), + cmd_line); +} + TEST(CommandLineBuilder, BuildInstallWithExtraArgsSessionId) { CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL); builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") @@ -230,16 +238,16 @@ TEST(CommandLineBuilder, BuildUpdateWithSessionId) { // The /update builder works when not used with GoogleUpdate.exe. TEST(CommandLineBuilder, BuildUpdateAndGetCommandLineWithNonGoogleUpdateExe) { CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE); - CString cmd_line = builder.GetCommandLine(_T("C:\\GoogleUpdateSetup_en.exe")); - EXPECT_STREQ(_T("\"C:\\GoogleUpdateSetup_en.exe\" /update"), cmd_line); + CString cmd_line = builder.GetCommandLine(_T("C:\\") MAIN_EXE_BASE_NAME _T("Setup_en.exe")); + EXPECT_STREQ(_T("\"C:\\") MAIN_EXE_BASE_NAME _T("Setup_en.exe\" /update"), cmd_line); } // The /update builder should not be used with GoogleUpdate.exe directly. TEST(CommandLineBuilder, BuildUpdateAndGetCommandLineWithGoogleUpdateExe) { CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE); ExpectAsserts expect_asserts; - CString cmd_line = builder.GetCommandLine(_T("C:\\GoogleUpdate.exe")); - EXPECT_STREQ(_T("\"C:\\GoogleUpdate.exe\" /update"), cmd_line); + CString cmd_line = builder.GetCommandLine(_T("C:\\") MAIN_EXE_BASE_NAME _T(".exe")); + EXPECT_STREQ(_T("\"C:\\") MAIN_EXE_BASE_NAME _T(".exe\" /update"), cmd_line); } TEST(CommandLineBuilder, BuildComServer) { @@ -266,24 +274,6 @@ TEST(CommandLineBuilder, BuildCodeRedCheck) { EXPECT_STREQ(_T("/cr"), cmd_line); } -TEST(CommandLineBuilder, BuildWebPlugin) { - CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN); - ExpectAsserts expect_asserts; - CString cmd_line = builder.GetCommandLineArgs(); - EXPECT_STREQ(_T(""), cmd_line); -} - -TEST(CommandLineBuilder, BuildWebPluginWithUrlArgsAndInstallSource) { - CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN); - builder.set_webplugin_args(_T("piargs")); - builder.set_webplugin_url_domain(_T("http://www.google.com/")); - builder.set_install_source(_T("oneclick")); - CString cmd_line = builder.GetCommandLineArgs(); - EXPECT_STREQ(_T("/pi \"http://www.google.com/\" \"piargs\"") - _T(" /installsource oneclick"), - cmd_line); -} - TEST(CommandLineBuilder, BuildRecover) { CommandLineBuilder builder(COMMANDLINE_MODE_RECOVER); ExpectAsserts expect_asserts; @@ -411,6 +401,27 @@ TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgsSilentOffline) { cmd_line); } +TEST(CommandLineBuilder, + BuildHandoffInstallWithExtraArgsSilentAlwaysLaunchCmd) { + CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL); + builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") + _T("appname=YouTubeUploader&needsadmin=False&") + _T("lang=en")); + builder.set_install_source(_T("offline")); + builder.set_is_silent_set(true); + builder.set_is_always_launch_cmd_set(true); + EXPECT_SUCCEEDED( + builder.SetOfflineDirName(_T("{B851CC84-A5C4-4769-92C1-DC6B0BB368B4}"))); + + CString cmd_line = builder.GetCommandLineArgs(); + EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") + _T("appname=YouTubeUploader&needsadmin=False&lang=en\"") + _T(" /installsource offline") + _T(" /silent /alwayslaunchcmd") + _T(" /offlinedir \"{B851CC84-A5C4-4769-92C1-DC6B0BB368B4}\""), + cmd_line); +} + TEST(CommandLineBuilder, BuildHandoffWithAppArgsSilentOffline) { CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL); builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") @@ -513,11 +524,5 @@ TEST(CommandLineBuilder, BuildHealthCheck) { EXPECT_STREQ(_T("/healthcheck"), cmd_line); } -TEST(CommandLineBuilder, BuildRegisterMsiHelper) { - CommandLineBuilder builder(COMMANDLINE_MODE_REGISTER_MSI_HELPER); - CString cmd_line = builder.GetCommandLineArgs(); - EXPECT_STREQ(_T("/registermsihelper"), cmd_line); -} - } // namespace omaha diff --git a/omaha/common/command_line_unittest.cc b/omaha/common/command_line_unittest.cc index f5fb4ffe4..5835cbea0 100644 --- a/omaha/common/command_line_unittest.cc +++ b/omaha/common/command_line_unittest.cc @@ -47,6 +47,7 @@ void VerifyCommandLineArgs(const CommandLineArgs& expected, EXPECT_EQ(expected.is_machine_set, actual.is_machine_set); EXPECT_EQ(expected.is_install_elevated, actual.is_install_elevated); EXPECT_EQ(expected.is_silent_set, actual.is_silent_set); + EXPECT_EQ(expected.is_always_launch_cmd_set, actual.is_always_launch_cmd_set); EXPECT_EQ(expected.is_eula_required_set, actual.is_eula_required_set); EXPECT_EQ(expected.is_enterprise_set, actual.is_enterprise_set); EXPECT_EQ(expected.is_offline_set, actual.is_offline_set); @@ -58,8 +59,6 @@ void VerifyCommandLineArgs(const CommandLineArgs& expected, EXPECT_STREQ(expected.crash_filename, actual.crash_filename); EXPECT_STREQ(expected.custom_info_filename, actual.custom_info_filename); EXPECT_STREQ(expected.legacy_manifest_path, actual.legacy_manifest_path); - EXPECT_STREQ(expected.webplugin_urldomain, actual.webplugin_urldomain); - EXPECT_STREQ(expected.webplugin_args, actual.webplugin_args); EXPECT_STREQ(expected.code_red_metainstaller_path, actual.code_red_metainstaller_path); EXPECT_STREQ(expected.offline_dir_name, actual.offline_dir_name); @@ -115,7 +114,7 @@ void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected_val, #endif EXPECT_EQ(expected_val.browser_type, actual_val.browser_type); EXPECT_EQ(expected_val.usage_stats_enable, actual_val.usage_stats_enable); - EXPECT_EQ(expected_val.runtime_only, actual_val.runtime_only); + EXPECT_EQ(expected_val.runtime_mode, actual_val.runtime_mode); EXPECT_EQ(expected_val.apps.size(), actual_val.apps.size()); @@ -302,9 +301,9 @@ TEST_F(CommandLineTest, ParseCommandLine_Install) { _T("{C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&") _T("appname=TestApp&needsadmin=true&lang=en") #if defined(HAS_DEVICE_MANAGEMENT) - _T("&etoken=f6f767ba-8cfb-4d95-a26a-b3d714ddf1a2") + _T("&etoken=f6f767ba-8cfb-4d95-a26a-b3d714ddf1a2") #endif - ; + _T(""); CommandLineAppArgs app_args; const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33, {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}}; @@ -430,6 +429,20 @@ TEST_F(CommandLineTest, ParseCommandLine_InstallWithAppArgsSilent) { VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true); } +// Parse /install "extraargs" /appargs /silent /alwayslaunchcmd +TEST_F(CommandLineTest, + ParseCommandLine_InstallWithAppArgsSilentAlwaysLaunchCmd) { + const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG + _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS + _T(" /silent /alwayslaunchcmd"); + EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); + + expected_.mode = COMMANDLINE_MODE_INSTALL; + expected_.is_silent_set = true; + expected_.is_always_launch_cmd_set = true; + VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true); +} + // Parse: /install "extraargs" /oem /appargs /silent TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAppArgsSilent) { const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG @@ -475,27 +488,27 @@ TEST_F(CommandLineTest, ParseCommandLine_InstallEulaRequired) { VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } -// Parse: /install "extraargs" /oem /installsource oneclick +// Parse: /install "extraargs" /oem /installsource taggedmi TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAndSource) { const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG _T(" /oem") - _T(" /installsource oneclick"); + _T(" /installsource taggedmi"); EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); expected_.mode = COMMANDLINE_MODE_INSTALL; expected_.is_oem_set = true; - expected_.install_source = _T("oneclick"); + expected_.install_source = _T("taggedmi"); VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } -// Parse: /install "extraargs" /installsource oneclick +// Parse: /install "extraargs" /installsource taggedmi TEST_F(CommandLineTest, ParseCommandLine_InstallWithSource) { const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG - _T(" /installsource oneclick"); + _T(" /installsource taggedmi"); EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); expected_.mode = COMMANDLINE_MODE_INSTALL; - expected_.install_source = _T("oneclick"); + expected_.install_source = _T("taggedmi"); VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } @@ -523,15 +536,15 @@ TEST_F(CommandLineTest, ParseCommandLine_InstallSilentWithOem) { VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } -// Parse: /install "extraargs" /installsource oneclick /silent +// Parse: /install "extraargs" /installsource taggedmi /silent TEST_F(CommandLineTest, ParseCommandLine_InstallSilentWithSource) { const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG - _T(" /installsource oneclick") + _T(" /installsource taggedmi") _T(" /silent"); EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); expected_.mode = COMMANDLINE_MODE_INSTALL; - expected_.install_source = _T("oneclick"); + expected_.install_source = _T("taggedmi"); expected_.is_silent_set = true; VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } @@ -547,15 +560,15 @@ TEST_F(CommandLineTest, ParseCommandLine_InstallElevated) { VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } -// Parse: /install "extraargs" /installelevated /installsource oneclick +// Parse: /install "extraargs" /installelevated /installsource taggedmi TEST_F(CommandLineTest, ParseCommandLine_InstallElevatedWithSource) { const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG - _T(" /installelevated /installsource oneclick"); + _T(" /installelevated /installsource taggedmi"); EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); expected_.mode = COMMANDLINE_MODE_INSTALL; expected_.is_install_elevated = true; - expected_.install_source = _T("oneclick"); + expected_.install_source = _T("taggedmi"); VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } @@ -589,11 +602,11 @@ TEST_F(CommandLineTest, ParseCommandLine_Handoff) { // Parse: /handoff "extraargs" /installsource "asd" TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSource) { const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG - _T(" /installsource oneclick"); + _T(" /installsource taggedmi"); EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL; - expected_.install_source = _T("oneclick"); + expected_.install_source = _T("taggedmi"); VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } @@ -672,6 +685,18 @@ TEST_F(CommandLineTest, ParseCommandLine_HandoffSilent) { VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } +// Parse: /handoff "extraargs" /silent /alwayslaunchcmd +TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentAlwaysLaunchCmd) { + const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG + _T(" /silent /alwayslaunchcmd"); + EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); + + expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL; + expected_.is_silent_set = true; + expected_.is_always_launch_cmd_set = true; + VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); +} + // Parse: /handoff "extraargs" /enterprise TEST_F(CommandLineTest, ParseCommandLine_HandoffEnterprise) { const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG @@ -700,12 +725,12 @@ TEST_F(CommandLineTest, // Parse: /handoff "extraargs" /installsource "asd" /silent TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithSource) { const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG - _T(" /installsource oneclick") + _T(" /installsource taggedmi") _T(" /silent"); EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL; - expected_.install_source = _T("oneclick"); + expected_.install_source = _T("taggedmi"); expected_.is_silent_set = true; VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true); } @@ -857,15 +882,6 @@ TEST_F(CommandLineTest, ParseCommandLine_UpdateSessionId) { VerifyCommandLineArgs(expected_, args_); } -// Parse: /netdiags -TEST_F(CommandLineTest, ParseCommandLine_NetDiags) { - const TCHAR* kCmdLine = _T("goopdate.exe /netdiags"); - EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); - - expected_.mode = COMMANDLINE_MODE_NETDIAGS; - VerifyCommandLineArgs(expected_, args_); -} - // Parse: /regserver TEST_F(CommandLineTest, ParseCommandLine_Regserver) { const TCHAR* kCmdLine = _T("goopdate.exe /regserver"); @@ -1025,43 +1041,6 @@ TEST_F(CommandLineTest, ParseCommandLine_CodeRedCheck) { VerifyCommandLineArgs(expected_, args_); } -TEST_F(CommandLineTest, ParseCommandLine_WebPlugin) { - const TCHAR* kCmdLine = _T("goopdate.exe /pi \"http://gears.google.com/\" ") - _T("\"/install foo\" /installsource oneclick "); - EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); - - expected_.mode = COMMANDLINE_MODE_WEBPLUGIN; - expected_.webplugin_urldomain = _T("http://gears.google.com/"); - expected_.webplugin_args = _T("/install foo"); - expected_.install_source = _T("oneclick"); - VerifyCommandLineArgs(expected_, args_); -} - -TEST_F(CommandLineTest, ParseCommandLine_WebPluginUrlEscaped) { - const TCHAR* kCmdLine = _T("goopdate.exe /pi \"http://gears.google.com/\" ") - _T("\"/install%20foo\" /installsource oneclick "); - EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); - - expected_.mode = COMMANDLINE_MODE_WEBPLUGIN; - expected_.webplugin_urldomain = _T("http://gears.google.com/"); - expected_.webplugin_args = _T("/install foo"); - expected_.install_source = _T("oneclick"); - VerifyCommandLineArgs(expected_, args_); -} - -TEST_F(CommandLineTest, ParseCommandLine_WebPluginTestStringTrim) { - const TCHAR* kCmdLine = _T("goopdate.exe /pi ") - _T("\" http://gears.google.com/ \" ") - _T("\"/install foo\" /installsource oneclick "); - EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); - - expected_.mode = COMMANDLINE_MODE_WEBPLUGIN; - expected_.webplugin_urldomain = _T("http://gears.google.com/"); - expected_.webplugin_args = _T("/install foo"); - expected_.install_source = _T("oneclick"); - VerifyCommandLineArgs(expected_, args_); -} - TEST_F(CommandLineTest, ParseCommandLine_LegacyOmaha1UiNoLanguage) { const TCHAR* kCmdLine = _T("goopdate.exe /ui \"manifestfilename.xml\""); EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); @@ -1174,18 +1153,6 @@ TEST_F(CommandLineTest, UiWithLangNoLanguage) { EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); } -TEST_F(CommandLineTest, WebPluginInstallSourceInvalid_IncorrectValue) { - const TCHAR* kCmdLine = _T("goopdate.exe /installsource invalid /pi ") - _T("\" http://gears.google.com/ \" "); - EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); -} - -TEST_F(CommandLineTest, WebPluginInstallSourceInvalid_Empty) { - const TCHAR* kCmdLine = _T("goopdate.exe /installsource /pi ") - _T("\" http://gears.google.com/ \" "); - EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); -} - // Parse: /handoff "extraargs" /lang "en" TEST_F(CommandLineTest, ParseCommandLine_HandoffLegacyOmaha1ToOmaha2) { const TCHAR* kCmdLine = @@ -1203,18 +1170,18 @@ TEST_F(CommandLineTest, _T("goopdate.exe /handoff ") _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") _T("appname=YouTubeUploader&needsadmin=False\"") - _T(" /installsource oneclick /lang en"); + _T(" /installsource taggedmi /lang en"); EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); } -// Parse: /handoff "extraargs" /installsource "oneclick" /lang "en" +// Parse: /handoff "extraargs" /installsource "taggedmi" /lang "en" TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSourceLegacyOmaha1ToOmaha2Both) { const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") _T("appname=YouTubeUploader&needsadmin=False&lang=en\"") - _T(" /installsource oneclick /lang en"); + _T(" /installsource taggedmi /lang en"); EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); } @@ -1229,13 +1196,13 @@ TEST_F(CommandLineTest, ParseCommandLine_InstallLegacyOmaha1) { EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); } -// Parse: /install "extraargs" /installsource oneclick /lang en +// Parse: /install "extraargs" /installsource taggedmi /lang en TEST_F(CommandLineTest, ParseCommandLine_InstallWithSourceLegacyOmaha1) { const TCHAR* kCmdLine = _T("goopdate.exe /install ") _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") _T("appname=YouTubeUploader&needsadmin=False\"") - _T(" /installsource oneclick /lang en"); + _T(" /installsource taggedmi /lang en"); EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_)); } @@ -1274,13 +1241,4 @@ TEST_F(CommandLineTest, ParseCommandLine_HealthCheck) { VerifyCommandLineArgs(expected_, args_); } -// Parse: /registermsihelper -TEST_F(CommandLineTest, ParseCommandLine_RegisterMsiHelper) { - const TCHAR* kCmdLine = _T("goopdate.exe /registermsihelper"); - EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_)); - - expected_.mode = COMMANDLINE_MODE_REGISTER_MSI_HELPER; - VerifyCommandLineArgs(expected_, args_); -} - } // namespace omaha diff --git a/omaha/common/config_manager.cc b/omaha/common/config_manager.cc index 251d62d06..33be1f02a 100644 --- a/omaha/common/config_manager.cc +++ b/omaha/common/config_manager.cc @@ -30,6 +30,7 @@ #include "omaha/base/const_addresses.h" #include "omaha/base/debug.h" #include "omaha/base/error.h" +#include "omaha/base/file.h" #include "omaha/base/logging.h" #include "omaha/base/scope_guard.h" #include "omaha/base/string.h" @@ -40,187 +41,409 @@ #include "omaha/common/const_goopdate.h" #include "omaha/common/crash_utils.h" #include "omaha/common/oem_install_utils.h" +#include "omaha/goopdate/policy_status_value.h" #include "omaha/statsreport/metrics.h" namespace omaha { namespace { -int MapCSIDLFor64Bit(int csidl) { - // We assume, for now, that Omaha will always be deployed in a 32-bit form. - // If any 64-bit components (such as the crash handler) need to query paths, - // they need to be directed to the 32-bit equivalents. - - switch (csidl) { - case CSIDL_PROGRAM_FILES: - return CSIDL_PROGRAM_FILESX86; - case CSIDL_PROGRAM_FILES_COMMON: - return CSIDL_PROGRAM_FILES_COMMONX86; - case CSIDL_SYSTEM: - return CSIDL_SYSTEMX86; - default: - return csidl; - } -} - -// GetDir32 is named as such because it will always look for 32-bit versions -// of directories; i.e. on a 64-bit OS, CSIDL_PROGRAM_FILES will return -// Program Files (x86). If we need to genuinely find 64-bit locations on -// 64-bit code in the future, we need to add a GetDir64() which omits the call -// to MapCSIDLFor64Bit(). -HRESULT GetDir32(int csidl, - const CString& path_tail, - bool create_dir, - CString* dir) { - ASSERT1(dir); - -#ifdef _WIN64 - csidl = MapCSIDLFor64Bit(csidl); -#endif - - CString path; - HRESULT hr = GetFolderPath(csidl | CSIDL_FLAG_DONT_VERIFY, &path); - if (FAILED(hr)) { - CORE_LOG(LW, (_T("GetDir failed to find path][%d][0x%08x]"), csidl, hr)); - return hr; +// This class aggregates a source/value pair for a single policy value, as well +// as a conflict-source/conflict-value pair if a conflict exists. +template +class PolicyValue { + public: + PolicyValue() : has_conflict_(false) {} + + // The update method is called multiple times, once for each policy source. + // The values are stored in the order in which the Update() method is called, + // and |is_managed| sources have precedence. + void Update(bool is_managed, const CString& source, const T& value) { + if (is_managed && source_.IsEmpty()) { + source_ = source; + value_ = value; + } else if (conflict_source_.IsEmpty()) { + conflict_source_ = source; + conflict_value_ = value; + has_conflict_ = !!GetValueString().Compare(GetConflictValueString()); + } } - if (!::PathAppend(CStrBuf(path, MAX_PATH), path_tail)) { - CORE_LOG(LW, (_T("GetDir failed to append path][%s][%s]"), - path, path_tail)); - return GOOPDATE_E_PATH_APPEND_FAILED; + + // This method is called once at the conclusion of updates to a policy value, + // with a local (machine) source and value. + // This method constructs and returns a IPolicyStatusValue using the final + // aggregation of the PolicyValue, if the optional |policy_status_value| is + // provided. + void UpdateFinal(const T& value, IPolicyStatusValue** policy_status_value) { + if (source_.IsEmpty()) { + source_ = _T("Default"); + value_ = value; + } + + if (policy_status_value) { + VERIFY1(SUCCEEDED(PolicyStatusValue::Create(source_, + GetValueString(), + has_conflict_, + conflict_source_, + GetConflictValueString(), + policy_status_value))); + } } - dir->SetString(path); - // Try to create the directory. Continue if the directory can't be created. - if (create_dir) { - hr = CreateDir(path, NULL); - if (FAILED(hr)) { - CORE_LOG(LW, (_T("[GetDir failed to create dir][%s][0x%08x]"), path, hr)); + CString ToString() const { + CString result(_T("[PolicyValue]")); + SafeCStringAppendFormat(&result, _T("[source_][%s]"), source_); + SafeCStringAppendFormat(&result, _T("[value_][%s]"), GetValueString()); + + if (has_conflict_) { + SafeCStringAppendFormat(&result, _T("[conflict_source_][%s]"), + conflict_source_); + SafeCStringAppendFormat(&result, _T("[conflict_value_][%s]"), + GetConflictValueString()); } + + return result; + } + + CString source() const { return source_; } + T value() const { return value_; } + + // These are the values used in the PolicyStatusValue COM object creation. + // These values do not necessarily correspond to value() and conflict_value(). + CString GetValueString() const { return value_; } + CString GetConflictValueString() const { return conflict_value_; } + + private: + CString source_; + T value_; + bool has_conflict_; + CString conflict_source_; + T conflict_value_; + + DISALLOW_COPY_AND_ASSIGN(PolicyValue); +}; + +template <> +CString PolicyValue::GetValueString() const { + return itostr(static_cast(value_)); +} + +template <> +CString PolicyValue::GetConflictValueString() const { + return itostr(static_cast(conflict_value_)); +} + +template <> +CString PolicyValue::GetValueString() const { + return String_BoolToString(value_); +} + +template <> +CString PolicyValue::GetConflictValueString() const { + return String_BoolToString(conflict_value_); +} + +template <> +CString PolicyValue>::GetValueString() const { + if (value_.empty()) { + return CString(); + } + + CString result; + for (const auto& app_id : value_) { + result += app_id; + result += ";"; + } + result.Delete(result.GetLength() - 1); + + return result; +} + +template <> +CString PolicyValue>::GetConflictValueString() const { + if (conflict_value_.empty()) { + return CString(); + } + + CString result; + for (const auto& app_id : conflict_value_) { + result += app_id; + result += ";"; + } + result.Delete(result.GetLength() - 1); + + return result; +} + +template <> +CString PolicyValue::GetValueString() const { + CString result; + SafeCStringFormat(&result, _T("%d, %d, %d"), value_.start_hour, + value_.start_min, + value_.duration_min); + return result; +} + +template <> +CString PolicyValue::GetConflictValueString() const { + CString result; + SafeCStringFormat(&result, _T("%d, %d, %d"), conflict_value_.start_hour, + conflict_value_.start_min, + conflict_value_.duration_min); + return result; +} + +struct SecondsMinutes { + DWORD seconds = 0; +}; + +// These methods return the string value in minutes. This is because the +// LastCheckPeriodMinutes policy is in minutes, and therefore the external +// interface returns values in minutes. +template <> +CString PolicyValue::GetValueString() const { + return itostr(static_cast(value_.seconds / 60)); +} + +template <> +CString PolicyValue::GetConflictValueString() const { + return itostr(static_cast(conflict_value_.seconds / 60)); +} + +template +void GetPolicyDword(const TCHAR* policy_name, T* out) { + ASSERT1(out); + + DWORD value = 0; + if (SUCCEEDED( + RegKey::GetValue(kRegKeyGoopdateGroupPolicy, policy_name, &value))) { + *out = static_cast(value); } +} + +void GetPolicyString(const TCHAR* policy_name, CString* out) { + ASSERT1(out); + + CString value; + if (SUCCEEDED( + RegKey::GetValue(kRegKeyGoopdateGroupPolicy, policy_name, &value))) { + *out = value; + } +} + +} // namespace + +bool OmahaPolicyManager::IsManaged() { + OPT_LOG(L1, (_T("[IsManaged][%s][%d]"), source(), policy_.is_managed)); + return policy_.is_managed; +} + +HRESULT OmahaPolicyManager::GetLastCheckPeriodMinutes(DWORD* minutes) { + if (!policy_.is_initialized) { + return E_FAIL; + } + + if (policy_.auto_update_check_period_minutes == -1) { + return E_FAIL; + } + + *minutes = static_cast(policy_.auto_update_check_period_minutes); + OPT_LOG(L5, (_T("[GetLastCheckPeriodMinutes][%s][%d]"), source(), *minutes)); return S_OK; } -// The app-specific value overrides the disable all value so read the former -// first. If it doesn't exist, read the "disable all" value. -bool GetEffectivePolicyForApp(const TCHAR* apps_default_value_name, - const TCHAR* app_prefix_name, - const GUID& app_guid, - DWORD* effective_policy) { - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetEffectivePolicyForApp][Ignoring group policy for %s]") - _T("[machine is not part of a domain]"), - GuidToString(app_guid))); - return false; +HRESULT OmahaPolicyManager::GetUpdatesSuppressedTimes( + UpdatesSuppressedTimes* times) { + if (!policy_.is_initialized) { + return E_FAIL; } - ASSERT1(apps_default_value_name); - ASSERT1(app_prefix_name); - ASSERT1(effective_policy); + if (policy_.updates_suppressed.start_hour == -1 || + policy_.updates_suppressed.start_minute == -1 || + policy_.updates_suppressed.duration_min == -1) { + OPT_LOG(L5, + (_T("[GetUpdatesSuppressedTimes][%s][Missing time]"), source())); + return E_FAIL; + } - CString app_value_name(app_prefix_name); - app_value_name.Append(GuidToString(app_guid)); + times->start_hour = static_cast(policy_.updates_suppressed.start_hour); + times->start_min = + static_cast(policy_.updates_suppressed.start_minute); + times->duration_min = + static_cast(policy_.updates_suppressed.duration_min); - HRESULT hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - app_value_name, - effective_policy); - if (SUCCEEDED(hr)) { - return true; - } else { - CORE_LOG(L4, (_T("[Failed to read Group Policy value][%s]"), - app_value_name)); + return S_OK; +} + +HRESULT OmahaPolicyManager::GetDownloadPreferenceGroupPolicy( + CString* download_preference) { + if (!policy_.is_initialized || policy_.download_preference.IsEmpty()) { + return E_FAIL; } - hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - apps_default_value_name, - effective_policy); - if (SUCCEEDED(hr)) { - return true; - } else { - CORE_LOG(L4, (_T("[Failed to read Group Policy value][%s]"), - apps_default_value_name)); + *download_preference = policy_.download_preference; + return S_OK; +} + +HRESULT OmahaPolicyManager::GetPackageCacheSizeLimitMBytes( + DWORD* cache_size_limit) { + if (!policy_.is_initialized || policy_.cache_size_limit == -1) { + return E_FAIL; } - return false; + *cache_size_limit = static_cast(policy_.cache_size_limit); + return S_OK; } -// Gets the raw update check period override value in seconds from the registry. -// The value must be processed for limits and overflow before using. -// Checks UpdateDev and Group Policy. -// Returns true if either override was successefully read. -bool GetLastCheckPeriodSecFromRegistry(DWORD* period_sec) { - ASSERT1(period_sec); +HRESULT OmahaPolicyManager::GetPackageCacheExpirationTimeDays( + DWORD* cache_life_limit) { + if (!policy_.is_initialized || policy_.cache_life_limit == -1) { + return E_FAIL; + } - DWORD update_dev_sec = 0; - if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, - kRegValueLastCheckPeriodSec, - &update_dev_sec))) { - CORE_LOG(L5, (_T("['LastCheckPeriodSec' override %d]"), update_dev_sec)); - *period_sec = update_dev_sec; - return true; + *cache_life_limit = static_cast(policy_.cache_life_limit); + return S_OK; +} + +HRESULT OmahaPolicyManager::GetProxyMode(CString* proxy_mode) { + if (!policy_.is_initialized || policy_.proxy_mode.IsEmpty()) { + return E_FAIL; } - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetLastCheckPeriodSecFromRegistry]") - _T("[Ignoring group policy]") - _T("[machine is not part of a domain]"))); - return false; + *proxy_mode = policy_.proxy_mode; + return S_OK; +} + +HRESULT OmahaPolicyManager::GetProxyPacUrl(CString* proxy_pac_url) { + if (!policy_.is_initialized || + policy_.proxy_mode.CompareNoCase(kProxyModePacScript) != 0 || + policy_.proxy_pac_url.IsEmpty()) { + return E_FAIL; } - DWORD group_policy_minutes = 0; - if (SUCCEEDED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueAutoUpdateCheckPeriodOverrideMinutes, - &group_policy_minutes))) { - CORE_LOG(L5, (_T("[Group Policy check period override %d]"), - group_policy_minutes)); + *proxy_pac_url = policy_.proxy_pac_url; + return S_OK; +} - *period_sec = (group_policy_minutes > UINT_MAX / 60) ? - UINT_MAX : - group_policy_minutes * 60; - CORE_LOG(L5, (_T("[GetLastCheckPeriodSecFromRegistry][%d]"), *period_sec)); +HRESULT OmahaPolicyManager::GetProxyServer(CString* proxy_server) { + if (!policy_.is_initialized || + policy_.proxy_mode.CompareNoCase(kProxyModeFixedServers) != 0 || + policy_.proxy_server.IsEmpty()) { + return E_FAIL; + } - return true; + *proxy_server = policy_.proxy_server; + return S_OK; +} + +HRESULT OmahaPolicyManager::GetForceInstallApps(bool is_machine, + std::vector* app_ids) { + ASSERT1(app_ids); + ASSERT1(app_ids->empty()); + + if (!policy_.is_initialized) { + return E_FAIL; } - return false; + for (const auto& app_settings : policy_.application_settings) { + const DWORD expected = is_machine ? kPolicyForceInstallMachine : + kPolicyForceInstallUser; + if (static_cast(app_settings.second.install) != expected) { + continue; + } + + CString app_id_string = GuidToString(app_settings.first); + app_ids->push_back(app_id_string); + } + + return !app_ids->empty() ? S_OK : E_FAIL; } -bool GetUpdatesSuppressedTimes(DWORD* start_hour, - DWORD* start_min, - DWORD* duration_min) { - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetUpdatesSuppressedTimes][Ignoring group policy]") - _T("[machine is not part of a domain]"))); - return false; +HRESULT OmahaPolicyManager::GetEffectivePolicyForAppInstalls( + const GUID& app_guid, DWORD* install_policy) { + if (!policy_.is_initialized) { + return E_FAIL; } - if (FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartHour, - start_hour)) || - FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedStartMin, - start_min)) || - FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueUpdatesSuppressedDurationMin, - duration_min))) { - OPT_LOG(L5, (_T("[GetUpdatesSuppressedTimes][Missing time][%x][%x][%x]"), - *start_hour, *start_min, *duration_min)); - return false; + if (policy_.application_settings.count(app_guid) && + policy_.application_settings.at(app_guid).install != -1) { + *install_policy = policy_.application_settings.at(app_guid).install; + return S_OK; } - // UpdatesSuppressedDurationMin is limited to 16 hours. - if (*start_hour > 23 || *start_min > 59 || *duration_min > 16 * kMinPerHour) { - OPT_LOG(L5, (_T("[GetUpdatesSuppressedTimes][Out of bounds][%x][%x][%x]"), - *start_hour, *start_min, *duration_min)); - return false; + if (policy_.install_default == -1) { + return E_FAIL; } - CORE_LOG(L5, (_T("[GetUpdatesSuppressedTimes][%x][%x][%x]"), - *start_hour, *start_min, *duration_min)); - return true; + *install_policy = policy_.install_default; + return S_OK; } -} // namespace +HRESULT OmahaPolicyManager::GetEffectivePolicyForAppUpdates( + const GUID& app_guid, DWORD* update_policy) { + if (!policy_.is_initialized) { + return E_FAIL; + } + + if (policy_.application_settings.count(app_guid) && + policy_.application_settings.at(app_guid).update != -1) { + *update_policy = policy_.application_settings.at(app_guid).update; + return S_OK; + } + + if (policy_.update_default == -1) { + return E_FAIL; + } + + *update_policy = policy_.update_default; + return S_OK; +} + +HRESULT OmahaPolicyManager::GetTargetChannel(const GUID& app_guid, + CString* target_channel) { + if (!policy_.is_initialized || + !policy_.application_settings.count(app_guid) || + policy_.application_settings.at(app_guid).target_channel.IsEmpty()) { + return E_FAIL; + } + + *target_channel = policy_.application_settings.at(app_guid).target_channel; + return S_OK; +} + +HRESULT OmahaPolicyManager::GetTargetVersionPrefix( + const GUID& app_guid, CString* target_version_prefix) { + if (!policy_.is_initialized || + !policy_.application_settings.count(app_guid) || + policy_.application_settings.at(app_guid) + .target_version_prefix.IsEmpty()) { + return E_FAIL; + } + + *target_version_prefix = + policy_.application_settings.at(app_guid).target_version_prefix; + return S_OK; +} + +HRESULT OmahaPolicyManager::IsRollbackToTargetVersionAllowed( + const GUID& app_guid, bool* rollback_allowed) { + if (!policy_.is_initialized || + !policy_.application_settings.count(app_guid) || + policy_.application_settings.at(app_guid).rollback_to_target_version == + -1) { + return E_FAIL; + } + + *rollback_allowed = + !!policy_.application_settings.at(app_guid).rollback_to_target_version; + return S_OK; +} + +void OmahaPolicyManager::set_policy(const CachedOmahaPolicy& policy) { + OPT_LOG(L1, (_T("[OmahaPolicyManager::set_policy][%s][%s]"), source(), + policy.ToString())); + policy_ = policy; +} LLock ConfigManager::lock_; ConfigManager* ConfigManager::config_manager_ = NULL; @@ -235,9 +458,13 @@ ConfigManager* ConfigManager::Instance() { void ConfigManager::DeleteInstance() { delete config_manager_; + config_manager_ = NULL; } -ConfigManager::ConfigManager() { +ConfigManager::ConfigManager() + : group_policy_manager_(new OmahaPolicyManager(_T("Group Policy"))), + dm_policy_manager_(new OmahaPolicyManager(_T("Device Management"))), + are_cloud_policies_preferred_(false) { CString current_module_directory(app_util::GetCurrentModuleDirectory()); CString path; @@ -264,50 +491,57 @@ ConfigManager::ConfigManager() { path.GetLength(), true) == 0) : false; + + // We initialize the policy managers without taking the Group Policy critical + // section. Later, once we know the exact scenario that we are operating + // under, we may reload the policies with the critical section lock. At the + // moment, we reload the policies with the critical section lock for all User + // installs and updates, as well as all Machine updates. + VERIFY1(SUCCEEDED(LoadPolicies(false))); } CString ConfigManager::GetUserDownloadStorageDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, + VERIFY_SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR), true, - &path))); + &path)); return path; } CString ConfigManager::GetUserInstallWorkingDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, + VERIFY_SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, CString(OMAHA_REL_INSTALL_WORKING_DIR), true, - &path))); + &path)); return path; } CString ConfigManager::GetUserOfflineStorageDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, + VERIFY_SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, CString(OMAHA_REL_OFFLINE_STORAGE_DIR), true, - &path))); + &path)); return path; } CString ConfigManager::GetUserGoopdateInstallDirNoCreate() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, + VERIFY_SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, CString(OMAHA_REL_GOOPDATE_INSTALL_DIR), false, - &path))); + &path)); return path; } CString ConfigManager::GetUserGoopdateInstallDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, + VERIFY_SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, CString(OMAHA_REL_GOOPDATE_INSTALL_DIR), true, - &path))); + &path)); return path; } @@ -316,53 +550,46 @@ bool ConfigManager::IsRunningFromUserGoopdateInstallDir() const { } CString ConfigManager::GetUserCrashReportsDir() const { - CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, - CString(OMAHA_REL_CRASH_DIR), - true, - &path))); - return path; + return app_util::GetTempDir(); } CString ConfigManager::GetMachineCrashReportsDir() const { - CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, - CString(OMAHA_REL_CRASH_DIR), - true, - &path))); - return path; + return GetSecureSystemTempDir(); } CString ConfigManager::GetMachineSecureDownloadStorageDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR), true, - &path))); + &path)); return path; } CString ConfigManager::GetMachineInstallWorkingDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, CString(OMAHA_REL_INSTALL_WORKING_DIR), true, - &path))); + &path)); return path; } CString ConfigManager::GetMachineSecureOfflineStorageDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, CString(OMAHA_REL_OFFLINE_STORAGE_DIR), true, - &path))); + &path)); return path; } CString ConfigManager::GetTempDownloadDir() const { CString temp_download_dir(app_util::GetTempDirForImpersonatedOrCurrentUser()); - ASSERT1(temp_download_dir); + if (temp_download_dir.IsEmpty()) { + return temp_download_dir; + } + HRESULT hr = CreateDir(temp_download_dir, NULL); if (FAILED(hr)) { CORE_LOG(LW, (_T("[CreateDir failed][%s][0x%08x]"), @@ -371,69 +598,221 @@ CString ConfigManager::GetTempDownloadDir() const { return temp_download_dir; } -int ConfigManager::GetPackageCacheSizeLimitMBytes() const { +int ConfigManager::GetPackageCacheSizeLimitMBytes( + IPolicyStatusValue** policy_status_value) const { DWORD kDefaultCacheStorageLimit = 500; // 500 MB DWORD kMaxCacheStorageLimit = 5000; // 5 GB - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetPackageCacheSizeLimitMBytes][Ignoring group policy]") - _T("[machine is not part of a domain]"))); - return kDefaultCacheStorageLimit; - } + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + DWORD cache_size_limit = 0; + HRESULT hr = policies_[i]->GetPackageCacheSizeLimitMBytes( + &cache_size_limit); - DWORD cache_size_limit = 0; - if (FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueCacheSizeLimitMBytes, - &cache_size_limit)) || - cache_size_limit > kMaxCacheStorageLimit || - cache_size_limit == 0) { - cache_size_limit = kDefaultCacheStorageLimit; + if (SUCCEEDED(hr)) { + if (cache_size_limit <= kMaxCacheStorageLimit && cache_size_limit > 0) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + cache_size_limit); + } + } } - return static_cast(cache_size_limit); + v.UpdateFinal(kDefaultCacheStorageLimit, policy_status_value); + + OPT_LOG(L5, (_T("[GetPackageCacheSizeLimitMBytes][%s]"), v.ToString())); + + return v.value(); } -int ConfigManager::GetPackageCacheExpirationTimeDays() const { +int ConfigManager::GetPackageCacheExpirationTimeDays( + IPolicyStatusValue** policy_status_value) const { DWORD kDefaultCacheLifeTimeInDays = 180; // 180 days. DWORD kMaxCacheLifeTimeInDays = 1800; // Roughly 5 years. - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetPackageCacheExpirationTimeDays]") - _T("[Ignoring group policy]") - _T("[machine is not part of a domain]"))); - return kDefaultCacheLifeTimeInDays; + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + DWORD cache_life_limit = 0; + HRESULT hr = policies_[i]->GetPackageCacheExpirationTimeDays( + &cache_life_limit); + + if (SUCCEEDED(hr)) { + if (cache_life_limit <= kMaxCacheLifeTimeInDays && cache_life_limit > 0) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + cache_life_limit); + } + } } - DWORD cache_life_limit = 0; - if (FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueCacheLifeLimitDays, - &cache_life_limit)) || - cache_life_limit > kMaxCacheLifeTimeInDays || - cache_life_limit == 0) { - cache_life_limit = kDefaultCacheLifeTimeInDays; + v.UpdateFinal(kDefaultCacheLifeTimeInDays, policy_status_value); + + OPT_LOG(L5, (_T("[GetPackageCacheExpirationTimeDays][%s]"), v.ToString())); + + return v.value(); +} + +HRESULT ConfigManager::GetProxyMode( + CString* proxy_mode, + IPolicyStatusValue** policy_status_value) const { + ASSERT1(proxy_mode); + + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + CString mode; + HRESULT hr = policies_[i]->GetProxyMode(&mode); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), policies_[i]->source(), mode); + } + } + + if (v.source().IsEmpty()) { + // No managed source had a value set for this policy. There is no local + // default value for this policy. So we return failure. + return E_FAIL; } - return static_cast(cache_life_limit); + v.UpdateFinal(CString(), policy_status_value); + + OPT_LOG(L5, (_T("[GetProxyMode][%s]"), v.ToString())); + + *proxy_mode = v.value(); + return S_OK; +} + +HRESULT ConfigManager::GetProxyPacUrl( + CString* proxy_pac_url, + IPolicyStatusValue** policy_status_value) const { + ASSERT1(proxy_pac_url); + + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + CString pac_url; + HRESULT hr = policies_[i]->GetProxyPacUrl(&pac_url); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), policies_[i]->source(), pac_url); + } + } + + if (v.source().IsEmpty()) { + // No managed source had a value set for this policy. There is no local + // default value for this policy. So we return failure. + return E_FAIL; + } + + v.UpdateFinal(CString(), policy_status_value); + + OPT_LOG(L5, (_T("[GetProxyPacUrl][%s]"), v.ToString())); + + *proxy_pac_url = v.value(); + return S_OK; +} + +HRESULT ConfigManager::GetProxyServer( + CString* proxy_server, + IPolicyStatusValue** policy_status_value) const { + ASSERT1(proxy_server); + + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + CString server; + HRESULT hr = policies_[i]->GetProxyServer(&server); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), policies_[i]->source(), server); + } + } + + if (v.source().IsEmpty()) { + // No managed source had a value set for this policy. There is no local + // default value for this policy. So we return failure. + return E_FAIL; + } + + v.UpdateFinal(CString(), policy_status_value); + + OPT_LOG(L5, (_T("[GetProxyServer][%s]"), v.ToString())); + + *proxy_server = v.value(); + return S_OK; +} + +HRESULT ConfigManager::GetForceInstallApps( + bool is_machine, + std::vector* app_ids, + IPolicyStatusValue** policy_status_value) const { + ASSERT1(app_ids); + + PolicyValue> v; + + for (size_t i = 0; i != policies_.size(); ++i) { + std::vector t; + HRESULT hr = policies_[i]->GetForceInstallApps(is_machine, &t); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), policies_[i]->source(), t); + } + } + + if (v.source().IsEmpty()) { + // No managed source had a value set for this policy. There is no local + // default value for this policy. So we return failure. + return E_FAIL; + } + + v.UpdateFinal(std::vector(), policy_status_value); + + OPT_LOG(L5, (_T("[GetForceInstallApps][is_machine][%d][%s]"), is_machine, + v.ToString())); + + *app_ids = v.value(); + return S_OK; } CString ConfigManager::GetMachineGoopdateInstallDirNoCreate() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, CString(OMAHA_REL_GOOPDATE_INSTALL_DIR), false, - &path))); + &path)); return path; } CString ConfigManager::GetMachineGoopdateInstallDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, CString(OMAHA_REL_GOOPDATE_INSTALL_DIR), true, - &path))); + &path)); + return path; +} + +CString ConfigManager::GetUserCompanyDir() const { + CString path; + VERIFY_SUCCEEDED(GetDir32(CSIDL_LOCAL_APPDATA, + CString(OMAHA_REL_COMPANY_DIR), + false, + &path)); + return path; +} + +CString ConfigManager::GetMachineCompanyDir() const { + CString path; + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + CString(OMAHA_REL_COMPANY_DIR), + false, + &path)); return path; } +CString ConfigManager::GetTempDir() const { + return ::IsUserAnAdmin() ? GetSecureSystemTempDir() : + app_util::GetTempDirForImpersonatedOrCurrentUser(); +} + bool ConfigManager::IsRunningFromMachineGoopdateInstallDir() const { return is_running_from_official_machine_dir_; } @@ -508,6 +887,20 @@ HRESULT ConfigManager::GetUsageStatsReportUrl(CString* url) const { return S_OK; } +HRESULT ConfigManager::GetAppLogoUrl(CString* url) const { + ASSERT1(url); + + if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, + kRegValueNameAppLogoUrl, + url))) { + CORE_LOG(L5, (_T("['app logo url' override %s]"), *url)); + return S_OK; + } + + *url = kUrlAppLogo; + return S_OK; +} + #if defined(HAS_DEVICE_MANAGEMENT) HRESULT ConfigManager::GetDeviceManagementUrl(CString* url) const { @@ -526,15 +919,202 @@ HRESULT ConfigManager::GetDeviceManagementUrl(CString* url) const { CPath ConfigManager::GetPolicyResponsesDir() const { CString path; - VERIFY1(SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, + VERIFY_SUCCEEDED(GetDir32(CSIDL_PROGRAM_FILES, CString(OMAHA_REL_POLICY_RESPONSES_DIR), true, - &path))); + &path)); return CPath(path); } #endif // defined(HAS_DEVICE_MANAGEMENT) +HRESULT ConfigManager::LoadPolicies(bool should_acquire_critical_section) { + HRESULT hr = LoadGroupPolicies(should_acquire_critical_section); + if (FAILED(hr)) { + return hr; + } + + policies_.clear(); + if (are_cloud_policies_preferred_) { + policies_.push_back(dm_policy_manager_); + policies_.push_back(group_policy_manager_); + } else { + policies_.push_back(group_policy_manager_); + policies_.push_back(dm_policy_manager_); + } + + return hr; +} + +HRESULT ConfigManager::LoadGroupPolicies(bool should_acquire_critical_section) { + CachedOmahaPolicy group_policies; + ON_SCOPE_EXIT_OBJ(*group_policy_manager_, &OmahaPolicyManager::set_policy, + ByRef(group_policies)); + + HANDLE policy_critical_section = NULL; + ScopeGuard guard_policy_critical_section = + MakeGuard(::LeaveCriticalPolicySection, ByRef(policy_critical_section)); + + // The Policy critical section will only be taken if the machine is + // Enterprise-joined and `should_acquire_critical_section` is true. + if (IsEnterpriseManaged()) { + group_policies.is_managed = true; + + if (should_acquire_critical_section) { + policy_critical_section = ::EnterCriticalPolicySection(TRUE); + // Not getting the policy critical section is a fatal error. So we will + // crash here if `policy_critical_section` is NULL. + if (!policy_critical_section) { + __debugbreak(); + } + } else { + guard_policy_critical_section.Dismiss(); + // Fall through to read the policies. + } + + } else { + guard_policy_critical_section.Dismiss(); + OPT_LOG(L1, (_T("[ConfigManager::LoadGroupPolicies][Machine is not ") + _T("Enterprise Managed]"))); + + // We still fall through to read the policies in order to store them as + // a conflict source in chrome://policy. The policies will not be respected + // however. + } + + if (!RegKey::HasKey(kRegKeyGoopdateGroupPolicy)) { + group_policies.is_managed = false; + + OPT_LOG(L1, (_T("[ConfigManager::LoadGroupPolicies][No Group Policies ") + _T("found under key][%s]"), + kRegKeyGoopdateGroupPolicy)); + + return S_FALSE; + } + + RegKey group_policy_key; + HRESULT hr = group_policy_key.Open(kRegKeyGoopdateGroupPolicy, KEY_READ); + if (FAILED(hr)) { + if (group_policies.is_managed) { + // Not getting the Group Policies is a fatal error if this is a managed + // machine. So we will crash here. + __debugbreak(); + } + + return hr; + } + + group_policies.is_initialized = true; + + GetPolicyDword(kRegValueCloudPolicyOverridesPlatformPolicy, + &are_cloud_policies_preferred_); + + GetPolicyDword(kRegValueAutoUpdateCheckPeriodOverrideMinutes, + &group_policies.auto_update_check_period_minutes); + GetPolicyString(kRegValueDownloadPreference, + &group_policies.download_preference); + GetPolicyDword(kRegValueCacheSizeLimitMBytes, + &group_policies.cache_size_limit); + GetPolicyDword(kRegValueCacheLifeLimitDays, &group_policies.cache_life_limit); + + GetPolicyDword(kRegValueUpdatesSuppressedStartHour, + &group_policies.updates_suppressed.start_hour); + GetPolicyDword(kRegValueUpdatesSuppressedStartMin, + &group_policies.updates_suppressed.start_minute); + GetPolicyDword(kRegValueUpdatesSuppressedDurationMin, + &group_policies.updates_suppressed.duration_min); + + GetPolicyString(kRegValueProxyMode, &group_policies.proxy_mode); + GetPolicyString(kRegValueProxyServer, &group_policies.proxy_server); + GetPolicyString(kRegValueProxyPacUrl, &group_policies.proxy_pac_url); + + GetPolicyDword(kRegValueInstallAppsDefault, &group_policies.install_default); + GetPolicyDword(kRegValueUpdateAppsDefault, &group_policies.update_default); + + static const int kGuidLen = 38; + int value_count = group_policy_key.GetValueCount(); + for (int i = 0; i < value_count; ++i) { + CString value_name; + DWORD type = 0; + if (FAILED(group_policy_key.GetValueNameAt(i, &value_name, &type))) { + continue; + } + + int app_id_start = value_name.Find(_T('{')); + if (app_id_start <= 0) { + continue; + } + + CString app_id_string = value_name.Mid(app_id_start); + GUID app_id = {}; + if (app_id_string.GetLength() != kGuidLen || + FAILED(StringToGuidSafe(app_id_string, &app_id)) || + GUID_NULL == app_id) { + continue; + } + + CString policy_name = value_name.Left(app_id_start); + + switch (type) { + case REG_DWORD: { + DWORD dword_policy_value = 0; + if (SUCCEEDED( + group_policy_key.GetValue(value_name, &dword_policy_value))) { + if (!policy_name.CompareNoCase(kRegValueInstallAppPrefix)) { + group_policies.application_settings[app_id].install = + dword_policy_value; + } else if (!policy_name.CompareNoCase(kRegValueUpdateAppPrefix)) { + group_policies.application_settings[app_id].update = + dword_policy_value; + } else if (!policy_name.CompareNoCase( + kRegValueRollbackToTargetVersion)) { + group_policies.application_settings[app_id] + .rollback_to_target_version = dword_policy_value; + } else { + OPT_LOG(LW, (_T("[ConfigManager::LoadGroupPolicies][Unexpected ") + _T("DWORD policy prefix encountered][%s][%d]"), + value_name, dword_policy_value)); + } + } + break; + } + + case REG_SZ: { + CString string_policy_value; + if (SUCCEEDED( + group_policy_key.GetValue(value_name, &string_policy_value))) { + if (!policy_name.CompareNoCase(kRegValueTargetChannel)) { + group_policies.application_settings[app_id].target_channel = + string_policy_value; + } else if (!policy_name.CompareNoCase(kRegValueTargetVersionPrefix)) { + group_policies.application_settings[app_id].target_version_prefix = + string_policy_value; + } else { + OPT_LOG(LW, (_T("[ConfigManager::LoadGroupPolicies][Unexpected ") + _T("String policy prefix encountered][%s][%s]"), + value_name, string_policy_value)); + } + } + break; + } + + default: + OPT_LOG(LW, (_T("[ConfigManager::LoadGroupPolicies][Unexpected Type ") + _T("for policy prefix encountered][%s][%d]"), + value_name, type)); + break; + } + } + + return S_OK; +} + +void ConfigManager::SetOmahaDMPolicies(const CachedOmahaPolicy& dm_policy) { + dm_policy_manager_->set_policy(dm_policy); + REPORT_LOG(L1, (_T("[ConfigManager::SetOmahaDMPolicies][%s]"), + dm_policy.ToString())); +} + // Returns the override from the registry locations if present. Otherwise, // returns the default value. // Default value is different value for internal users to make update checks @@ -543,30 +1123,48 @@ CPath ConfigManager::GetPolicyResponsesDir() const { // when the override is 0, which indicates updates are disabled. int ConfigManager::GetLastCheckPeriodSec(bool* is_overridden) const { ASSERT1(is_overridden); - DWORD registry_period_sec = 0; - *is_overridden = GetLastCheckPeriodSecFromRegistry(®istry_period_sec); - if (*is_overridden) { - if (0 == registry_period_sec) { - CORE_LOG(L5, (_T("[GetLastCheckPeriodSec][0 == registry_period_sec]"))); - return 0; - } - const int period_sec = registry_period_sec > INT_MAX ? - INT_MAX : - static_cast(registry_period_sec); - if (period_sec < kMinLastCheckPeriodSec) { - return kMinLastCheckPeriodSec; + return GetLastCheckPeriodSec(is_overridden, NULL); +} + +int ConfigManager::GetLastCheckPeriodSec( + bool* is_overridden, IPolicyStatusValue** status_value_minutes) const { + ASSERT1(is_overridden); + + PolicyValue v; + + DWORD policy_period_sec = 0; + if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, + kRegValueLastCheckPeriodSec, + &policy_period_sec))) { + if (policy_period_sec > 0 && policy_period_sec < kMinLastCheckPeriodSec) { + policy_period_sec = kMinLastCheckPeriodSec; + } else if (policy_period_sec > INT_MAX) { + policy_period_sec = INT_MAX; + } + v.Update(true, _T("UpdateDev"), {policy_period_sec}); + } else { + DWORD minutes = 0; + HRESULT hr = E_FAIL; + for (size_t i = 0; i != policies_.size(); ++i) { + hr = policies_[i]->GetLastCheckPeriodMinutes(&minutes); + if (SUCCEEDED(hr)) { + policy_period_sec = minutes * 60ULL > INT_MAX ? INT_MAX : minutes * 60; + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + {policy_period_sec}); + } } - CORE_LOG(L5, (_T("[GetLastCheckPeriodSec][period_sec][%d]"), period_sec)); - return period_sec; } - // Returns a lower value for internal users. - if (IsInternalUser()) { - return kLastCheckPeriodInternalUserSec; - } + *is_overridden = !v.source().IsEmpty(); + policy_period_sec = IsInternalUser() ? kLastCheckPeriodInternalUserSec : + kLastCheckPeriodSec; + v.UpdateFinal({policy_period_sec}, status_value_minutes); + + OPT_LOG(L5, (_T("[GetLastCheckPeriodMinutes][%s]"), v.ToString())); - return kLastCheckPeriodSec; + return v.value().seconds; } // All time values are in seconds. @@ -790,36 +1388,6 @@ int ConfigManager::GetAutoUpdateJitterMs() const { return (random_delay % kMaxJitterMs); } -time64 ConfigManager::GetTimeSinceLastCoreRunMs(bool is_machine) const { - const time64 now = GetCurrentMsTime(); - const time64 last_core_run = GetLastCoreRunTimeMs(is_machine); - - if (now < last_core_run) { - return ULONG64_MAX; - } - - return now - last_core_run; -} - -time64 ConfigManager::GetLastCoreRunTimeMs(bool is_machine) const { - const TCHAR* reg_update_key(is_machine ? MACHINE_REG_UPDATE : - USER_REG_UPDATE); - time64 last_core_run_time = 0; - if (SUCCEEDED(RegKey::GetValue(reg_update_key, - kRegValueLastCoreRun, - &last_core_run_time))) { - return last_core_run_time; - } - - return 0; -} - -HRESULT ConfigManager::SetLastCoreRunTimeMs(bool is_machine, time64 time) { - const TCHAR* reg_update_key(is_machine ? MACHINE_REG_UPDATE : - USER_REG_UPDATE); - return RegKey::SetValue(reg_update_key, kRegValueLastCoreRun, time); -} - // Overrides CodeRedCheckPeriodMs. Implements a lower bound value. Returns // INT_MAX if the registry value exceeds INT_MAX. int ConfigManager::GetCodeRedTimerIntervalMs() const { @@ -1002,97 +1570,198 @@ bool ConfigManager::IsInternalUser() const { return false; } -DWORD ConfigManager::GetEffectivePolicyForAppInstalls(const GUID& app_guid) { - DWORD effective_policy = kPolicyDisabled; - if (!GetEffectivePolicyForApp(kRegValueInstallAppsDefault, - kRegValueInstallAppPrefix, - app_guid, - &effective_policy)) { - return kInstallPolicyDefault; +DWORD ConfigManager::GetEffectivePolicyForAppInstalls( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const { + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + DWORD effective_policy = kPolicyDisabled; + HRESULT hr = policies_[i]->GetEffectivePolicyForAppInstalls( + app_guid, + &effective_policy); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + effective_policy); + } } - return effective_policy; + v.UpdateFinal(kInstallPolicyDefault, policy_status_value); + + OPT_LOG(L5, (_T("[GetEffectivePolicyForAppInstalls][%s][%s]"), + GuidToString(app_guid), v.ToString())); + + return v.value(); } -DWORD ConfigManager::GetEffectivePolicyForAppUpdates(const GUID& app_guid) { - DWORD effective_policy = kPolicyDisabled; - if (!GetEffectivePolicyForApp(kRegValueUpdateAppsDefault, - kRegValueUpdateAppPrefix, - app_guid, - &effective_policy)) { - return kUpdatePolicyDefault; +DWORD ConfigManager::GetEffectivePolicyForAppUpdates( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const { + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + DWORD effective_policy = kPolicyDisabled; + HRESULT hr = policies_[i]->GetEffectivePolicyForAppUpdates( + app_guid, + &effective_policy); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + effective_policy); + } } - return effective_policy; + v.UpdateFinal(kUpdatePolicyDefault, policy_status_value); + + OPT_LOG(L5, (_T("[GetEffectivePolicyForAppUpdates][%s][%s]"), + GuidToString(app_guid), v.ToString())); + + return v.value(); } -CString ConfigManager::GetTargetVersionPrefix(const GUID& app_guid) { - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetTargetVersionPrefix][Ignoring group policy for %s]") - _T("[machine is not part of a domain]"), - GuidToString(app_guid))); - return CString(); +CString ConfigManager::GetTargetChannel( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const { + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + CString target_channel; + HRESULT hr = policies_[i]->GetTargetChannel(app_guid, &target_channel); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + target_channel); + } } - CString app_value_name(kRegValueTargetVersionPrefix); - app_value_name.Append(GuidToString(app_guid)); + v.UpdateFinal(CString(), policy_status_value); - CString target_version_prefix; - RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - app_value_name, - &target_version_prefix); - return target_version_prefix; + OPT_LOG(L5, (_T("[GetTargetChannel][%s][%s]"), + GuidToString(app_guid), v.ToString())); + return v.value(); } -bool ConfigManager::IsRollbackToTargetVersionAllowed(const GUID& app_guid) { - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[IsRollbackToTargetVersionAllowed][false][%s]") - _T("[machine is not part of a domain]"), - GuidToString(app_guid))); - return false; +CString ConfigManager::GetTargetVersionPrefix( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const { + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + CString target_version_prefix; + HRESULT hr = policies_[i]->GetTargetVersionPrefix(app_guid, + &target_version_prefix); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + target_version_prefix); + } } - CString app_value_name(kRegValueRollbackToTargetVersion); - app_value_name.Append(GuidToString(app_guid)); + v.UpdateFinal(CString(), policy_status_value); - DWORD is_rollback_allowed = 0; - RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - app_value_name, - &is_rollback_allowed); - return !!is_rollback_allowed; + OPT_LOG(L5, (_T("[GetTargetVersionPrefix][%s][%s]"), + GuidToString(app_guid), v.ToString())); + + return v.value(); } -bool ConfigManager::AreUpdatesSuppressedNow() { - DWORD start_hour = 0; - DWORD start_min = 0; - DWORD duration_min = 0; - if (!GetUpdatesSuppressedTimes(&start_hour, &start_min, &duration_min)) { - return false; +bool ConfigManager::IsRollbackToTargetVersionAllowed( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const { + PolicyValue v; + + for (size_t i = 0; i != policies_.size(); ++i) { + bool rollback_allowed = false; + HRESULT hr = policies_[i]->IsRollbackToTargetVersionAllowed( + app_guid, + &rollback_allowed); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + rollback_allowed); + } + } + + v.UpdateFinal(false, policy_status_value); + + OPT_LOG(L5, (_T("[IsRollbackToTargetVersionAllowed][%s][%s]"), + GuidToString(app_guid), v.ToString())); + + return v.value(); +} + +HRESULT ConfigManager::GetUpdatesSuppressedTimes( + const CTime& time, + UpdatesSuppressedTimes* times, + bool* are_updates_suppressed, + IPolicyStatusValue** policy_status_value) const { + ASSERT1(times); + ASSERT1(are_updates_suppressed); + + PolicyValue v; + + HRESULT hr = E_FAIL; + for (size_t i = 0; i != policies_.size(); ++i) { + UpdatesSuppressedTimes t; + hr = policies_[i]->GetUpdatesSuppressedTimes(&t); + if (SUCCEEDED(hr)) { + v.Update(policies_[i]->IsManaged(), policies_[i]->source(), t); + } } - CTime now(CTime::GetCurrentTime()); - tm local = {}; - now.GetLocalTm(&local); + if (v.source().IsEmpty()) { + // No managed source had a value set. + return E_FAIL; + } + + // UpdatesSuppressedDurationMin is limited to 16 hours. + if (v.value().start_hour > 23 || + v.value().start_min > 59 || + v.value().duration_min > 16 * kMinPerHour) { + OPT_LOG(L5, (_T("[GetUpdatesSuppressedTimes][Out of bounds][%x][%x][%x]"), + v.value().start_hour, + v.value().start_min, + v.value().duration_min)); + return E_UNEXPECTED; + } + + v.UpdateFinal(UpdatesSuppressedTimes(), policy_status_value); + *times = v.value(); + + tm local_time = {}; + time.GetLocalTm(&local_time); + int time_diff_minutes = + (local_time.tm_hour - v.value().start_hour) * kMinPerHour + + (local_time.tm_min - v.value().start_min); + + // Add 24 hours if `time_diff_minutes` is negative. + if (time_diff_minutes < 0) { + time_diff_minutes += 24 * kMinPerHour; + } + + *are_updates_suppressed = + time_diff_minutes < static_cast(v.value().duration_min); + OPT_LOG(L5, (_T("[GetUpdatesSuppressedTimes][v=%s][time=%s]") + _T("[time_diff_minutes=%d][are_updates_suppressed=%d]"), + v.ToString(), time.Format(L"%#c"), + time_diff_minutes, *are_updates_suppressed)); + return S_OK; +} + +bool ConfigManager::AreUpdatesSuppressedNow(const CTime& now) const { + UpdatesSuppressedTimes times; + bool are_updates_suppressed = false; - // tm_year is relative to 1900. tm_mon is 0-based. - CTime start_updates_suppressed(local.tm_year + 1900, - local.tm_mon + 1, - local.tm_mday, - start_hour, - start_min, - local.tm_sec, - local.tm_isdst); - CTimeSpan duration_updates_suppressed(0, 0, duration_min, 0); - CTime end_updates_suppressed = - start_updates_suppressed + duration_updates_suppressed; - return now >= start_updates_suppressed && now <= end_updates_suppressed; + HRESULT hr = GetUpdatesSuppressedTimes(now, + ×, + &are_updates_suppressed, + NULL); + return SUCCEEDED(hr) && are_updates_suppressed; } -bool ConfigManager::CanInstallApp(const GUID& app_guid) const { +bool ConfigManager::CanInstallApp(const GUID& app_guid, bool is_machine) const { // Google Update should never be checking whether it can install itself. ASSERT1(!::IsEqualGUID(kGoopdateGuid, app_guid)); - return kPolicyDisabled != GetEffectivePolicyForAppInstalls(app_guid); + auto policy = GetEffectivePolicyForAppInstalls(app_guid, NULL); + return kPolicyDisabled != policy && + (is_machine || kPolicyEnabledMachineOnly != policy); } // Self-updates cannot be disabled. @@ -1101,8 +1770,8 @@ bool ConfigManager::CanUpdateApp(const GUID& app_guid, bool is_manual) const { return true; } - const DWORD effective_policy = GetEffectivePolicyForAppUpdates(app_guid); - + const DWORD effective_policy = GetEffectivePolicyForAppUpdates(app_guid, + NULL); if (kPolicyDisabled == effective_policy) { return false; } @@ -1124,6 +1793,18 @@ bool ConfigManager::AlwaysAllowCrashUploads() const { return always_allow_crash_uploads != 0; } +bool ConfigManager::ShouldVerifyPayloadAuthenticodeSignature() const { +#ifdef VERIFY_PAYLOAD_AUTHENTICODE_SIGNATURE + DWORD disabled_in_registry = 0; + RegKey::GetValue(MACHINE_REG_UPDATE_DEV, + kRegValueDisablePayloadAuthenticodeVerification, + &disabled_in_registry); + return disabled_in_registry == 0; +#else + return false; +#endif // VERIFY_PAYLOAD_AUTHENTICODE_SIGNATURE +} + int ConfigManager::MaxCrashUploadsPerDay() const { DWORD num_uploads = 0; if (FAILED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, @@ -1139,24 +1820,26 @@ int ConfigManager::MaxCrashUploadsPerDay() const { return static_cast(num_uploads); } -CString ConfigManager::GetDownloadPreferenceGroupPolicy() const { - CString download_preference; +CString ConfigManager::GetDownloadPreferenceGroupPolicy( + IPolicyStatusValue** policy_status_value) const { + PolicyValue v; - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GetDownloadPreferenceGroupPolicy]") - _T("[Ignoring group policy]") - _T("[machine is not part of a domain]"))); - return CString(); + for (size_t i = 0; i != policies_.size(); ++i) { + CString download_preference; + HRESULT hr = policies_[i]->GetDownloadPreferenceGroupPolicy( + &download_preference); + if (SUCCEEDED(hr) && download_preference == kDownloadPreferenceCacheable) { + v.Update(policies_[i]->IsManaged(), + policies_[i]->source(), + download_preference); + } } - HRESULT hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueDownloadPreference, - &download_preference); - if (SUCCEEDED(hr) && download_preference == kDownloadPreferenceCacheable) { - return download_preference; - } + v.UpdateFinal(CString(), policy_status_value); + + OPT_LOG(L5, (_T("[GetDownloadPreferenceGroupPolicy][%s]"), v.ToString())); - return CString(); + return v.value(); } #if defined(HAS_DEVICE_MANAGEMENT) @@ -1165,7 +1848,8 @@ CString ConfigManager::GetCloudManagementEnrollmentToken() const { CString enrollment_token; HRESULT hr = RegKey::GetValue(kRegKeyCloudManagementGroupPolicy, kRegValueEnrollmentToken, &enrollment_token); - return SUCCEEDED(hr) ? enrollment_token : CString(); + return (SUCCEEDED(hr) && IsUuid(enrollment_token)) ? enrollment_token : + CString(); } bool ConfigManager::IsCloudManagementEnrollmentMandatory() const { diff --git a/omaha/common/config_manager.h b/omaha/common/config_manager.h index 96e45e550..a3f5ba09b 100644 --- a/omaha/common/config_manager.h +++ b/omaha/common/config_manager.h @@ -26,13 +26,98 @@ #include #include #include +#include +#include +#include #include "base/basictypes.h" #include "omaha/base/constants.h" #include "omaha/base/synchronized.h" #include "omaha/base/time.h" +#include "omaha/goopdate/dm_storage.h" +#include "goopdate/omaha3_idl.h" namespace omaha { +struct UpdatesSuppressedTimes { + DWORD start_hour = 0; + DWORD start_min = 0; + DWORD duration_min = 0; +}; + +class PolicyManagerInterface { + public: + virtual ~PolicyManagerInterface() {} + + virtual CString source() = 0; + + virtual bool IsManaged() = 0; + + virtual HRESULT GetLastCheckPeriodMinutes(DWORD* minutes) = 0; + virtual HRESULT GetUpdatesSuppressedTimes(UpdatesSuppressedTimes* times) = 0; + virtual HRESULT GetDownloadPreferenceGroupPolicy( + CString* download_preference) = 0; + virtual HRESULT GetPackageCacheSizeLimitMBytes(DWORD* cache_size_limit) = 0; + virtual HRESULT GetPackageCacheExpirationTimeDays( + DWORD* cache_life_limit) = 0; + virtual HRESULT GetProxyMode(CString* proxy_mode) = 0; + virtual HRESULT GetProxyPacUrl(CString* proxy_pac_url) = 0; + virtual HRESULT GetProxyServer(CString* proxy_server) = 0; + virtual HRESULT GetForceInstallApps(bool is_machine, + std::vector* app_ids) = 0; + + virtual HRESULT GetEffectivePolicyForAppInstalls(const GUID& app_guid, + DWORD* install_policy) = 0; + virtual HRESULT GetEffectivePolicyForAppUpdates(const GUID& app_guid, + DWORD* update_policy) = 0; + virtual HRESULT GetTargetChannel(const GUID& app_guid, + CString* target_channel) = 0; + virtual HRESULT GetTargetVersionPrefix(const GUID& app_guid, + CString* target_version_prefix) = 0; + virtual HRESULT IsRollbackToTargetVersionAllowed(const GUID& app_guid, + bool* rollback_allowed) = 0; +}; + +class OmahaPolicyManager : public PolicyManagerInterface { + public: + explicit OmahaPolicyManager(const CString& source) : source_(source) {} + + CString source() override { return source_; } + + bool IsManaged() override; + + HRESULT GetLastCheckPeriodMinutes(DWORD* minutes) override; + HRESULT GetUpdatesSuppressedTimes(UpdatesSuppressedTimes* times) override; + HRESULT GetDownloadPreferenceGroupPolicy( + CString* download_preference) override; + HRESULT GetPackageCacheSizeLimitMBytes(DWORD* cache_size_limit) override; + HRESULT GetPackageCacheExpirationTimeDays(DWORD* cache_life_limit) override; + HRESULT GetProxyMode(CString* proxy_mode) override; + HRESULT GetProxyPacUrl(CString* proxy_pac_url) override; + HRESULT GetProxyServer(CString* proxy_server) override; + HRESULT GetForceInstallApps(bool is_machine, + std::vector* app_ids) override; + + HRESULT GetEffectivePolicyForAppInstalls(const GUID& app_guid, + DWORD* install_policy) override; + HRESULT GetEffectivePolicyForAppUpdates(const GUID& app_guid, + DWORD* update_policy) override; + HRESULT GetTargetChannel(const GUID& app_guid, + CString* target_channel) override; + HRESULT GetTargetVersionPrefix(const GUID& app_guid, + CString* target_version_prefix) override; + HRESULT IsRollbackToTargetVersionAllowed(const GUID& app_guid, + bool* rollback_allowed) override; + + void set_policy(const CachedOmahaPolicy& policy); + CachedOmahaPolicy policy() { return policy_; } + + private: + CString source_; + CachedOmahaPolicy policy_; + + DISALLOW_COPY_AND_ASSIGN(OmahaPolicyManager); +}; + class ConfigManager { public: const TCHAR* user_registry_clients() const { return USER_REG_CLIENTS; } @@ -92,11 +177,24 @@ class ConfigManager { // Gets the total disk size limit for cached packages. When this limit is hit, // packages should be deleted from oldest until total size is below the limit. - int GetPackageCacheSizeLimitMBytes() const; + int GetPackageCacheSizeLimitMBytes( + IPolicyStatusValue** policy_status_value) const; // Gets the package cache life limit. If a cached package is older than this // limit, it should be removed. - int GetPackageCacheExpirationTimeDays() const; + int GetPackageCacheExpirationTimeDays( + IPolicyStatusValue** policy_status_value) const; + + // Gets the proxy policy values. + HRESULT GetProxyMode(CString* proxy_mode, + IPolicyStatusValue** policy_status_value) const; + HRESULT GetProxyPacUrl(CString* proxy_pac_url, + IPolicyStatusValue** policy_status_value) const; + HRESULT GetProxyServer(CString* proxy_server, + IPolicyStatusValue** policy_status_value) const; + HRESULT GetForceInstallApps(bool is_machine, + std::vector* app_ids, + IPolicyStatusValue** policy_status_value) const; // Creates download data dir: // %UserProfile%/Application Data/Google/Update/Download @@ -154,6 +252,17 @@ class ConfigManager { // %ProgramFiles%/Google/Update CString GetMachineGoopdateInstallDir() const; + // Gets the Google company directory. Does not create the directory if it does + // not already exist. + // `%LocalAppData%/Google` or `%ProgramFiles%/Google`. + CString GetUserCompanyDir() const; + CString GetMachineCompanyDir() 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; @@ -173,6 +282,9 @@ class ConfigManager { // Returns the service endpoint where the usage stats requests are sent. HRESULT GetUsageStatsReportUrl(CString* url) const; + // Returns the url base for the app logos. + HRESULT GetAppLogoUrl(CString* url) const; + #if defined(HAS_DEVICE_MANAGEMENT) // Returns the Device Management API url. HRESULT GetDeviceManagementUrl(CString* url) const; @@ -182,9 +294,32 @@ class ConfigManager { CPath GetPolicyResponsesDir() const; #endif + // Loads policies for all the policy managers and sets up the policy managers + // in order of priority on the ConfigManager instance, which is used by the + // ConfigManager for subsequent config queries. + // This function can be called multiple times to reload policies. + // `should_acquire_critical_section` indicates whether the Group Policy + // critical section should be acquired before initializing the Group Policy + // manager. The caller should only set `should_acquire_critical_section` when + // not running under GPO, because GPO takes the critical section itself, and + // this can therefore result in a deadlock when this function tries to take + // the critical section. + HRESULT LoadPolicies(bool should_acquire_critical_section); + + // Sets the DM policies on the ConfigManager instance, which is used by the + // ConfigManager for subsequent config queries. + void SetOmahaDMPolicies(const CachedOmahaPolicy& dm_policy); + + CachedOmahaPolicy dm_policy() { return dm_policy_manager_->policy(); } + // Returns the time interval between update checks in seconds. // 0 indicates updates are disabled. int GetLastCheckPeriodSec(bool* is_overridden) const; + // |policy_status_value| will be returned in minutes. This is because the + // LastCheckPeriodMinutes policy is in minutes, and therefore the + // IPolicyStatusValue interface needs to return values in minutes. + int GetLastCheckPeriodSec(bool* is_overridden, + IPolicyStatusValue** policy_status_value) const; // Returns the number of seconds since the last successful update check. int GetTimeSinceLastCheckedSec(bool is_machine) const; @@ -220,11 +355,6 @@ class ConfigManager { // by UpdateDev settings. int GetAutoUpdateJitterMs() const; - // Core interval between runs functions. - time64 GetTimeSinceLastCoreRunMs(bool is_machine) const; - time64 GetLastCoreRunTimeMs(bool is_machine) const; - HRESULT SetLastCoreRunTimeMs(bool is_machine, time64 time); - // Code Red check interval functions. int GetCodeRedTimerIntervalMs() const; time64 GetTimeSinceLastCodeRedCheckMs(bool is_machine) const; @@ -247,8 +377,70 @@ class ConfigManager { // Returns true if the user is considered an internal user. bool IsInternalUser() const; + // Returns kPolicyEnabled if installation of the specified app is allowed. + // Otherwise, returns kPolicyDisabled. + DWORD GetEffectivePolicyForAppInstalls( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const; + + // Returns kPolicyEnabled if updates of the specified app is allowed. + // Otherwise, returns one of kPolicyDisabled, kPolicyManualUpdatesOnly, or + // kPolicyAutomaticUpdatesOnly. + DWORD GetEffectivePolicyForAppUpdates( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const; + + // Returns the target channel for the app, if the machine is joined to a + // domain and has the corresponding policy set. + // + // TargetChannel specifies which channel the app should be updated to. + // + // When this policy is set, the binaries returned by Google Update are the + // binaries for the specified channel. If this policy is not set, the default + // channel is used. + // + // The possible values for the Chrome app are {dev|beta|stable}. + CString GetTargetChannel(const GUID& app_guid, + IPolicyStatusValue** policy_status_value) const; + + // Returns the target version prefix for the app, if the machine is joined to + // a domain and has the corresponding policy set. + // Examples: + // * "" (or not configured): update to latest version available. + // * "55.": update to any minor version of 55 (e.g. 55.24.34 or 55.60.2). + // * "55.2.": update to any minor version of 55.2 (e.g. 55.2.34 or 55.2.2). + // * "55.24.34": update to this specific version only. + CString GetTargetVersionPrefix( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const; + + // Returns whether the RollbackToTargetVersion policy has been set for the + // app. Setting RollbackToTargetVersion will result in a version downgrade if + // the app version on the client is higher than the version on the server. + // This could happen under circumstances such as: + // - TargetVersionPrefix is used to pick an older version on the channel. + // - TargetChannel is used to move the client to a channel with a lower + // version (e.g., Dev/Beta to Beta/Stable). + // - A user somehow installed a newer version on the client. + // When not set, a client will not receive updates until the app version on + // the server passes the version on the client. + bool IsRollbackToTargetVersionAllowed( + const GUID& app_guid, IPolicyStatusValue** policy_status_value) const; + + // For domain-joined machines, checks the given `time` against the times that + // updates are suppressed. Updates are suppressed if the given `time` falls + // between the start time and the duration. + // The duration does not account for daylight savings time. For instance, if + // the start time is 22:00 hours, and with a duration of 8 hours, the updates + // will be suppressed for 8 hours regardless of whether daylight savings time + // changes happen in between. + HRESULT GetUpdatesSuppressedTimes( + const CTime& time, + UpdatesSuppressedTimes* times, + bool* are_updates_suppressed, + IPolicyStatusValue** policy_status_value) const; + bool AreUpdatesSuppressedNow( + const CTime& now = CTime::GetCurrentTime()) const; + // Returns true if installation of the specified app is allowed. - bool CanInstallApp(const GUID& app_guid) const; + bool CanInstallApp(const GUID& app_guid, bool is_machine) const; // Returns true if updates are allowed for the specified app. The 'is_manual' // parameter is needed for context, because the update policy can be one of @@ -259,13 +451,18 @@ class ConfigManager { // build flavor or other configuration parameters. bool AlwaysAllowCrashUploads() const; + // Returns whether the Authenticode signature of update payloads should be + // verified. + bool ShouldVerifyPayloadAuthenticodeSignature() const; + // Returns the number of crashes to upload per day. int MaxCrashUploadsPerDay() const; // Returns the value of the "DownloadPreference" group policy or an // empty string if the group policy does not exist, the policy is unknown, or // an error happened. - CString GetDownloadPreferenceGroupPolicy() const; + CString GetDownloadPreferenceGroupPolicy( + IPolicyStatusValue** policy_status_value) const; #if defined(HAS_DEVICE_MANAGEMENT) @@ -289,42 +486,15 @@ class ConfigManager { // or installed. static bool Is24HoursSinceLastUpdate(bool is_machine); - // Returns kPolicyEnabled if installation of the specified app is allowed. - // Otherwise, returns kPolicyDisabled. - static DWORD GetEffectivePolicyForAppInstalls(const GUID& app_guid); - - // Returns kPolicyEnabled if updates of the specified app is allowed. - // Otherwise, returns one of kPolicyDisabled, kPolicyManualUpdatesOnly, or - // kPolicyAutomaticUpdatesOnly. - static DWORD GetEffectivePolicyForAppUpdates(const GUID& app_guid); - - // Returns the target version prefix for the app, if the machine is joined to - // a domain and has the corresponding group policy set. - // Examples: - // * "" (or not configured): update to latest version available. - // * "55.": update to any minor version of 55 (e.g. 55.24.34 or 55.60.2). - // * "55.2.": update to any minor version of 55.2 (e.g. 55.2.34 or 55.2.2). - // * "55.24.34": update to this specific version only. - static CString GetTargetVersionPrefix(const GUID& app_guid); - - // Returns whether the RollbackToTargetVersion policy has been set for the - // app. If RollbackToTargetVersion is set, the TargetVersionPrefix policy - // governs the version to rollback clients with higher versions to. - static bool IsRollbackToTargetVersionAllowed(const GUID& app_guid); - - // For domain-joined machines, checks the current time against the times that - // updates are suppressed. Returns true if the current time falls between the - // start time and the duration. - // The duration does not account for daylight savings time. For instance, if - // the start time is 22:00 hours, and with a duration of 8 hours, the updates - // will be suppressed for 8 hours regardless of whether daylight savings time - // changes happen in between. - static bool AreUpdatesSuppressedNow(); - static ConfigManager* Instance(); static void DeleteInstance(); private: + // Loads the Group policies from the registry and sets it up on the + // ConfigManager instance, which is used by the ConfigManager for subsequent + // config queries. + HRESULT LoadGroupPolicies(bool should_acquire_critical_section); + static LLock lock_; static ConfigManager* config_manager_; @@ -332,6 +502,10 @@ class ConfigManager { bool is_running_from_official_user_dir_; bool is_running_from_official_machine_dir_; + std::vector> policies_; // NOLINT + std::shared_ptr group_policy_manager_; // NOLINT + std::shared_ptr dm_policy_manager_; // NOLINT + bool are_cloud_policies_preferred_; DISALLOW_COPY_AND_ASSIGN(ConfigManager); }; diff --git a/omaha/common/config_manager_unittest.cc b/omaha/common/config_manager_unittest.cc index fdccb2ad7..54f7daf92 100644 --- a/omaha/common/config_manager_unittest.cc +++ b/omaha/common/config_manager_unittest.cc @@ -13,7 +13,9 @@ // limitations under the License. // ======================================================================== +#include #include +#include #include "omaha/base/app_util.h" #include "omaha/base/const_addresses.h" #include "omaha/base/constants.h" @@ -35,7 +37,7 @@ namespace { // OMAHA_KEY_REL == "Software\Google\Update" #define OMAHA_KEY_REL \ - _T("Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME #define APP_GUID1 _T("{6762F466-8863-424f-817C-5757931F346E}") @@ -54,26 +56,15 @@ const TCHAR* const kAppMachineClientStatePath2 = const TCHAR* const kAppUserClientStatePath2 = _T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\") APP_GUID2; -const TCHAR* const kPolicyKey = - _T("HKLM\\Software\\Policies\\") - SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\"); const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_GUID1; const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_GUID2; const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_GUID1; const TCHAR* const kUpdatePolicyApp2 = _T("Update") APP_GUID2; -HRESULT SetPolicy(const TCHAR* policy_name, DWORD value) { - return RegKey::SetValue(kPolicyKey, policy_name, value); -} - -HRESULT SetPolicyString(const TCHAR* policy_name, const CString& value) { - return RegKey::SetValue(kPolicyKey, policy_name, value); -} - #if defined(HAS_DEVICE_MANAGEMENT) const TCHAR* const kCloudManagementPolicyKey = - _T("HKLM\\Software\\Policies\\") SHORT_COMPANY_NAME + _T("HKLM\\Software\\Policies\\") PATH_COMPANY_NAME _T("\\CloudManagement\\"); HRESULT SetCloudManagementPolicy(const TCHAR* policy_name, DWORD value) { @@ -105,29 +96,53 @@ class ConfigManagerNoOverrideTest : public testing::Test { : cm_(ConfigManager::Instance()) { } - bool CanInstallApp(const TCHAR* guid) { - return cm_->CanInstallApp(StringToGuid(guid)); + bool CanInstallApp(const TCHAR* guid, bool is_machine) { + return cm_->CanInstallApp(StringToGuid(guid), is_machine); } bool CanUpdateApp(const TCHAR* guid, bool is_manual) { return cm_->CanUpdateApp(StringToGuid(guid), is_manual); } - static DWORD GetEffectivePolicyForAppInstalls(const TCHAR* guid) { - return ConfigManager::GetEffectivePolicyForAppInstalls(StringToGuid(guid)); + DWORD GetEffectivePolicyForAppInstalls(const TCHAR* guid) { + return cm_->GetEffectivePolicyForAppInstalls(StringToGuid(guid), NULL); + } + + DWORD GetEffectivePolicyForAppUpdates(const TCHAR* guid) { + return cm_->GetEffectivePolicyForAppUpdates(StringToGuid(guid), NULL); + } + + CString GetTargetChannel(const TCHAR* guid) { + return cm_->GetTargetChannel(StringToGuid(guid), NULL); + } + + CString GetTargetVersionPrefix(const TCHAR* guid) { + return cm_->GetTargetVersionPrefix(StringToGuid(guid), NULL); + } + + bool IsRollbackToTargetVersionAllowed(const TCHAR* guid) { + return cm_->IsRollbackToTargetVersionAllowed(StringToGuid(guid), NULL); } - static DWORD GetEffectivePolicyForAppUpdates(const TCHAR* guid) { - return ConfigManager::GetEffectivePolicyForAppUpdates(StringToGuid(guid)); + bool AreUpdatesSuppressedNow(const CTime& now = CTime::GetCurrentTime()) { + return cm_->AreUpdatesSuppressedNow(now); + } + + DWORD GetForceInstallApps(bool is_machine, std::vector* app_ids) { + return cm_->GetForceInstallApps(is_machine, app_ids, NULL); } ConfigManager* cm_; }; -// This class is parameterized for Domain/Non-Domain using -// ::testing::WithParamInterface. -class ConfigManagerTest : public ConfigManagerNoOverrideTest, - public ::testing::WithParamInterface { +// This class is parameterized for Domain, Device Management, and +// CloudPolicyOverridesPlatformPolicy using +// ::testing::WithParamInterface>. The first +// parameter is the bool for Domain, the second the bool for DM (Device +// Management), and the third the bool for CloudPolicyOverridesPlatformPolicy. +class ConfigManagerTest + : public ConfigManagerNoOverrideTest, + public ::testing::WithParamInterface> { protected: ConfigManagerTest() : hive_override_key_name_(kRegistryHiveOverrideRoot) { @@ -139,9 +154,33 @@ class ConfigManagerTest : public ConfigManagerNoOverrideTest, EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain, IsDomain() ? 1UL : 0UL)); + if (IsDomain()) { + RegKey::CreateKey(kRegKeyGoopdateGroupPolicy); + } else { + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + } + + if (IsCloudPolicyOverridesPlatformPolicy()) { + RegKey::SetValue(kRegKeyGoopdateGroupPolicy, + kRegValueCloudPolicyOverridesPlatformPolicy, + 1UL); + } + + // Re-create the ConfigManager instance, since the registry entries above + // need to be accounted for within the ConfigManager constructor. + ConfigManager::DeleteInstance(); + cm_ = ConfigManager::Instance(); + + if (IsDM()) { + SetCannedCachedOmahaPolicy(); + } } virtual void TearDown() { + if (IsDM()) { + ResetCachedOmahaPolicy(); + } + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain)); RestoreRegistryHives(); @@ -149,12 +188,26 @@ class ConfigManagerTest : public ConfigManagerNoOverrideTest, } bool IsDomain() { - return GetParam(); + return std::get<0>(GetParam()); + } + + bool IsDM() { + return std::get<1>(GetParam()); + } + + bool IsCloudPolicyOverridesPlatformPolicy() { + return IsDomain() && std::get<2>(GetParam()); + } + + bool IsDomainPredominant() { + return IsDomain() && (!IsCloudPolicyOverridesPlatformPolicy() || !IsDM()); } void ExpectTrueOnlyIfDomain(bool condition) { - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_TRUE(condition); + } else if (IsDM()) { + return; } else { EXPECT_FALSE(condition); } @@ -164,6 +217,43 @@ class ConfigManagerTest : public ConfigManagerNoOverrideTest, ExpectTrueOnlyIfDomain(!condition); } + void SetCannedCachedOmahaPolicy() { + CachedOmahaPolicy info; + info.is_managed = true; + info.is_initialized = true; + info.auto_update_check_period_minutes = 111; + info.download_preference = kDownloadPreferenceCacheable; + CTime now(CTime::GetCurrentTime()); + info.updates_suppressed.start_hour = now.GetHour(); + info.updates_suppressed.start_minute = now.GetMinute(); + info.updates_suppressed.duration_min = 180; + info.install_default = kPolicyEnabled; + info.update_default = kPolicyEnabled; + + ApplicationSettings chrome_app; + chrome_app.install = kPolicyDisabled; + chrome_app.update = kPolicyAutomaticUpdatesOnly; + chrome_app.target_channel = _T("dev"); + chrome_app.target_version_prefix = _T("3.6.55"); + chrome_app.rollback_to_target_version = true; + info.application_settings.insert(std::make_pair(StringToGuid(kChromeAppId), + chrome_app)); + ApplicationSettings app1; + app1.install = kPolicyForceInstallMachine; + info.application_settings.insert(std::make_pair(StringToGuid(kAppGuid1), + app1)); + + ApplicationSettings app2; + app2.install = kPolicyForceInstallUser; + info.application_settings.insert(std::make_pair(StringToGuid(kAppGuid2), + app2)); + cm_->SetOmahaDMPolicies(info); + } + + void ResetCachedOmahaPolicy() { + cm_->SetOmahaDMPolicies(CachedOmahaPolicy()); + } + void CanCollectStatsHelper(bool is_machine); void CanCollectStatsIgnoresOppositeHiveHelper(bool is_machine); HRESULT SetFirstInstallTime(bool is_machine, DWORD time); @@ -313,19 +403,19 @@ TEST_F(ConfigManagerNoOverrideTest, RegistryKeys) { EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\"), cm_->registry_update(true)); - EXPECT_STREQ(_T("HKCU\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"), + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\"), cm_->user_registry_google()); - EXPECT_STREQ(_T("HKLM\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"), + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\"), cm_->machine_registry_google()); - EXPECT_STREQ(_T("HKCU\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"), + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\"), cm_->registry_google(false)); - EXPECT_STREQ(_T("HKLM\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"), + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\"), cm_->registry_google(true)); } TEST_F(ConfigManagerNoOverrideTest, GetUserCrashReportsDir) { - const CString expected_path = GetGoogleUserPath() + _T("CrashReports"); - EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path)); + const CString expected_path = app_util::GetTempDir(); + EXPECT_FALSE(expected_path.IsEmpty()); EXPECT_STREQ(expected_path, cm_->GetUserCrashReportsDir()); EXPECT_TRUE(File::Exists(expected_path)); } @@ -370,11 +460,18 @@ TEST_F(ConfigManagerNoOverrideTest, GetTempDownloadDir) { } TEST_F(ConfigManagerNoOverrideTest, GetMachineCrashReportsDir) { - CString program_files; - EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files)); - const CString expected_path = - program_files + _T("\\") + SHORT_COMPANY_NAME + _T("\\CrashReports"); - EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path)); + CString windir; + EXPECT_SUCCEEDED(GetFolderPath(CSIDL_WINDOWS, &windir)); + CString expected_path = windir + _T("\\SystemTemp"); + + if (!File::IsDirectory(expected_path)) { + 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)); + } + EXPECT_STREQ(expected_path, cm_->GetMachineCrashReportsDir()); EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin()); } @@ -407,11 +504,40 @@ TEST_F(ConfigManagerNoOverrideTest, GetMachineSecureOfflineStorageDir) { EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin()); } +TEST_F(ConfigManagerNoOverrideTest, GetTempDir) { + CString expected_path; + + if (::IsUserAnAdmin()) { + CString windir; + EXPECT_SUCCEEDED(GetFolderPath(CSIDL_WINDOWS, &windir)); + expected_path = windir + _T("\\SystemTemp"); + + if (!File::IsDirectory(expected_path)) { + 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()); } -INSTANTIATE_TEST_CASE_P(IsDomain, ConfigManagerTest, ::testing::Bool()); +INSTANTIATE_TEST_CASE_P(IsDomainIsDMIsCloudPolicyOverridesPlatformPolicy, + ConfigManagerTest, + ::testing::Combine(::testing::Bool(), + ::testing::Bool(), + ::testing::Bool())); // Tests the GetUpdateCheckUrl override. TEST_P(ConfigManagerTest, GetUpdateCheckUrl) { @@ -483,8 +609,26 @@ TEST_P(ConfigManagerTest, GetUsageStatsReportUrl) { EXPECT_STREQ(url, _T("http://usagestatsreport/")); } +// Tests the `GetAppLogoUrl` override. +TEST_P(ConfigManagerTest, GetAppLogoUrl) { + CString url; + EXPECT_SUCCEEDED(cm_->GetAppLogoUrl(&url)); + EXPECT_STREQ(url, kUrlAppLogo); + + EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, + kRegValueNameAppLogoUrl, + _T("http://applogo/"))); + url.Empty(); + EXPECT_SUCCEEDED(cm_->GetAppLogoUrl(&url)); + EXPECT_STREQ(url, _T("http://applogo/")); +} + // Tests LastCheckPeriodSec override. TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_Default) { + if (IsDM()) { + return; + } + bool is_overridden = true; if (cm_->IsInternalUser()) { EXPECT_EQ(kLastCheckPeriodInternalUserSec, @@ -531,6 +675,11 @@ TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_UpdateDevOverride) { EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueLastCheckPeriodSec)); + + if (IsDM()) { + return; + } + is_overridden = true; if (cm_->IsInternalUser()) { EXPECT_EQ(kLastCheckPeriodInternalUserSec, @@ -541,18 +690,22 @@ TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_UpdateDevOverride) { EXPECT_FALSE(is_overridden); } -TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride) { +TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_PolicyOverride) { const DWORD kOverrideMinutes = 4000; const DWORD kExpectedSeconds = kOverrideMinutes * 60; EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"), kOverrideMinutes)); bool is_overridden = false; - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(kExpectedSeconds, cm_->GetLastCheckPeriodSec(&is_overridden)); + EXPECT_TRUE(is_overridden); + } else if (IsDM()) { + EXPECT_EQ(111 * 60, cm_->GetLastCheckPeriodSec(&is_overridden)); + EXPECT_TRUE(is_overridden); + } else { + EXPECT_FALSE(is_overridden); } - - ExpectTrueOnlyIfDomain(is_overridden); } TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride_TooLow) { @@ -563,7 +716,7 @@ TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride_TooLow) { bool is_overridden = false; const int check_period(cm_->GetLastCheckPeriodSec(&is_overridden)); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(kMinLastCheckPeriodSec, check_period); } @@ -580,7 +733,7 @@ TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_GPO_Zero_Domain_NonDomain) { bool is_overridden = false; const int check_period(cm_->GetLastCheckPeriodSec(&is_overridden)); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(kExpectedSecondsDomain, check_period); } @@ -597,7 +750,7 @@ TEST_P(ConfigManagerTest, GetLastCheckPeriodSec_GPO_High_Domain_NonDomain) { bool is_overridden = false; const int check_period(cm_->GetLastCheckPeriodSec(&is_overridden)); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(kExpectedSecondsDomain, check_period); } @@ -611,7 +764,7 @@ TEST_P(ConfigManagerTest, kOverrideMinutes)); bool is_overridden = false; int check_period(cm_->GetLastCheckPeriodSec(&is_overridden)); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(INT_MAX, check_period); } @@ -622,7 +775,7 @@ TEST_P(ConfigManagerTest, kOverrideMinutes2)); is_overridden = false; check_period = cm_->GetLastCheckPeriodSec(&is_overridden); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(INT_MAX, check_period); } @@ -633,7 +786,7 @@ TEST_P(ConfigManagerTest, kOverrideMinutes3)); is_overridden = false; check_period = cm_->GetLastCheckPeriodSec(&is_overridden); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(INT_MAX, check_period); } @@ -650,7 +803,7 @@ TEST_P(ConfigManagerTest, kOverrideMinutes)); bool is_overridden = false; const int check_period(cm_->GetLastCheckPeriodSec(&is_overridden)); - if (IsDomain()) { + if (IsDomainPredominant()) { EXPECT_EQ(INT_MAX, check_period); } @@ -1127,39 +1280,80 @@ TEST_P(ConfigManagerTest, IsWindowsInstalling_Installing_Vista_ValidStates) { EXPECT_TRUE(cm_->IsWindowsInstalling()); } +TEST_P(ConfigManagerTest, GetForceInstallApps_NoGroupPolicy) { + std::vector app_ids; + EXPECT_EQ(IsDM() ? S_OK : E_FAIL, GetForceInstallApps(true, &app_ids)); + EXPECT_EQ(IsDM() ? S_OK : E_FAIL, GetForceInstallApps(false, &app_ids)); +} + +TEST_P(ConfigManagerTest, GetForceInstallApps_GroupPolicy) { + if (!IsDomainPredominant()) { + return; + } + + EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, kPolicyForceInstallMachine)); + EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, kPolicyForceInstallUser)); + + std::vector app_ids_machine; + EXPECT_SUCCEEDED(GetForceInstallApps(true, &app_ids_machine)); + EXPECT_EQ(1, app_ids_machine.size()); + + std::vector app_ids_user; + EXPECT_SUCCEEDED(GetForceInstallApps(false, &app_ids_user)); + EXPECT_EQ(1, app_ids_user.size()); +} + +TEST_P(ConfigManagerTest, GetForceInstallApps_DMPolicy) { + if (!IsDM()) { + return; + } + + std::vector app_ids_machine; + EXPECT_SUCCEEDED(GetForceInstallApps(true, &app_ids_machine)); + EXPECT_EQ(1, app_ids_machine.size()); + + std::vector app_ids_user; + EXPECT_SUCCEEDED(GetForceInstallApps(false, &app_ids_user)); + EXPECT_EQ(1, app_ids_user.size()); +} + TEST_P(ConfigManagerTest, CanInstallApp_NoGroupPolicy) { - EXPECT_TRUE(CanInstallApp(kAppGuid1)); - EXPECT_EQ(kPolicyEnabled, GetEffectivePolicyForAppInstalls(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + EXPECT_EQ(IsDM() ? kPolicyForceInstallMachine : kPolicyEnabled, + GetEffectivePolicyForAppInstalls(kAppGuid1)); } TEST_P(ConfigManagerTest, CanInstallApp_DifferentAppDisabled) { EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); - EXPECT_EQ(kPolicyEnabled, GetEffectivePolicyForAppInstalls(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + EXPECT_EQ(IsDM() ? kPolicyForceInstallMachine : kPolicyEnabled, + GetEffectivePolicyForAppInstalls(kAppGuid1)); } TEST_P(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppDisabled) { EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0)); - ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1)); + ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == kPolicyDisabled); } TEST_P(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppEnabled) { EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); - EXPECT_EQ(kPolicyEnabled, GetEffectivePolicyForAppInstalls(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + EXPECT_EQ(IsDomainPredominant() || !IsDM() ? kPolicyEnabled + : kPolicyForceInstallMachine, + GetEffectivePolicyForAppInstalls(kAppGuid1)); } TEST_P(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppInvalid) { EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == 2); } TEST_P(ConfigManagerTest, CanInstallApp_DefaultDisabled_NoAppValue) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0)); - ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1)); + ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == kPolicyDisabled); } @@ -1167,7 +1361,7 @@ TEST_P(ConfigManagerTest, CanInstallApp_DefaultDisabled_NoAppValue) { TEST_P(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppDisabled) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0)); EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0)); - ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1)); + ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == kPolicyDisabled); } @@ -1175,28 +1369,32 @@ TEST_P(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppDisabled) { TEST_P(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppEnabled) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0)); EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); - EXPECT_EQ(kPolicyEnabled, GetEffectivePolicyForAppInstalls(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + EXPECT_EQ(IsDomainPredominant() || !IsDM() ? kPolicyEnabled + : kPolicyForceInstallMachine, + GetEffectivePolicyForAppInstalls(kAppGuid1)); } // Invalid value defaulting to true overrides the InstallDefault disable. TEST_P(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppInvalid) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0)); EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == 2); } TEST_P(ConfigManagerTest, CanInstallApp_DefaultEnabled_NoAppValue) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); - EXPECT_EQ(kPolicyEnabled, GetEffectivePolicyForAppInstalls(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + EXPECT_EQ(IsDomainPredominant() || !IsDM() ? kPolicyEnabled + : kPolicyForceInstallMachine, + GetEffectivePolicyForAppInstalls(kAppGuid1)); } TEST_P(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppDisabled) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1)); EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0)); - ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1)); + ExpectFalseOnlyIfDomain(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == kPolicyDisabled); } @@ -1204,17 +1402,39 @@ TEST_P(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppDisabled) { TEST_P(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppEnabled) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1)); EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); - EXPECT_EQ(kPolicyEnabled, GetEffectivePolicyForAppInstalls(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, false)); + EXPECT_EQ(IsDomainPredominant() || !IsDM() ? kPolicyEnabled + : kPolicyForceInstallMachine, + GetEffectivePolicyForAppInstalls(kAppGuid1)); } TEST_P(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppInvalid) { EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1)); EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2)); - EXPECT_TRUE(CanInstallApp(kAppGuid1)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); ExpectTrueOnlyIfDomain(GetEffectivePolicyForAppInstalls(kAppGuid1) == 2); } +TEST_P(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppEnabledMachineOnly) { + EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1)); + EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 4)); + EXPECT_TRUE(CanInstallApp(kAppGuid1, true)); + ExpectTrueOnlyIfDomain(!CanInstallApp(kAppGuid1, false)); + ExpectTrueOnlyIfDomain(kPolicyEnabledMachineOnly == + GetEffectivePolicyForAppInstalls(kAppGuid1)); +} + +TEST_P(ConfigManagerTest, CanInstallApp_DMPolicy) { + if (IsDomainPredominant()) { + return; + } + + EXPECT_EQ(!IsDM(), CanInstallApp(kChromeAppId, true)); + EXPECT_EQ(IsDM() ? kPolicyDisabled : kPolicyEnabled, + GetEffectivePolicyForAppInstalls(kChromeAppId)); +} + TEST_P(ConfigManagerTest, CanUpdateApp_Auto_NoGroupPolicy) { EXPECT_TRUE(CanUpdateApp(kAppGuid1, false)); } @@ -1702,42 +1922,127 @@ TEST_P(ConfigManagerTest, CanUpdateApp_Manual_Omaha_AppDisabled) { EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, true)); } +TEST_P(ConfigManagerTest, GetEffectivePolicyForAppUpdates_DMPolicy) { + if (IsDomainPredominant()) { + return; + } + + EXPECT_EQ(IsDM() ? kPolicyAutomaticUpdatesOnly : kPolicyEnabled, + GetEffectivePolicyForAppUpdates(kChromeAppId)); +} + +TEST_P(ConfigManagerTest, GetTargetChannel) { + EXPECT_SUCCEEDED(SetPolicyString(_T("TargetChannel") CHROME_APP_ID, + _T("beta"))); + EXPECT_STREQ(IsDomainPredominant() ? _T("beta") : IsDM() ? _T("dev") : + _T(""), + GetTargetChannel(kChromeAppId)); +} + +TEST_P(ConfigManagerTest, GetTargetVersionPrefix) { + EXPECT_SUCCEEDED(SetPolicyString(_T("TargetVersionPrefix") CHROME_APP_ID, + _T("4.67.5"))); + EXPECT_STREQ(IsDomainPredominant() ? _T("4.67.5") : IsDM() ? _T("3.6.55") : + _T(""), + GetTargetVersionPrefix(kChromeAppId)); +} + +TEST_P(ConfigManagerTest, IsRollbackToTargetVersionAllowed) { + EXPECT_SUCCEEDED(SetPolicy(_T("RollbackToTargetVersion") CHROME_APP_ID, 1)); + EXPECT_EQ(IsDomain() || IsDM(), + IsRollbackToTargetVersionAllowed(kChromeAppId)); +} + +TEST_P(ConfigManagerTest, AreUpdatesSuppressedNow) { + CTime now(CTime::GetCurrentTime()); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartHour, + now.GetHour())); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartMin, + now.GetMinute())); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedDurationMin, 180)); + EXPECT_EQ(IsDomain() || IsDM(), AreUpdatesSuppressedNow()); +} + +TEST_P(ConfigManagerTest, AreUpdatesSuppressedNow_MultipleValues) { + const struct { + const DWORD suppress_start_hour; + const DWORD suppress_start_minute; + const DWORD suppress_duration_minutes; + const CTime now; + bool expect_updates_suppressed; + } test_cases[] = { + // Suppress starting 12:00 for 959 minutes. `now` is July 1, 2023, 01:15. + {12, 00, 959, {2023, 7, 01, 01, 15, 00}, IsDomainPredominant()}, + + // Suppress starting 12:00 for 959 minutes. `now` is July 1, 2023, 04:15. + {12, 00, 959, {2023, 7, 01, 04, 15, 00}, false}, + + // Suppress starting 00:00 for 959 minutes. `now` is July 1, 2023, 04:15. + {00, 00, 959, {2023, 7, 01, 04, 15, 00}, IsDomainPredominant()}, + + // Suppress starting 00:00 for 959 minutes. `now` is July 1, 2023, 16:15. + {00, 00, 959, {2023, 7, 01, 16, 15, 00}, false}, + + // Suppress starting 18:00 for 12 hours. `now` is July 1, 2023, 05:15. + { + 18, 00, 12 * kMinPerHour, + {2023, 7, 01, 5, 15, 00}, + IsDomainPredominant() + }, + + // Suppress starting 18:00 for 12 hours. `now` is July 1, 2023, 06:15. + {18, 00, 12 * kMinPerHour, {2023, 7, 01, 6, 15, 00}, false}, + }; + + for (const auto& test_case : test_cases) { + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartHour, + test_case.suppress_start_hour)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedStartMin, + test_case.suppress_start_minute)); + EXPECT_SUCCEEDED(SetPolicy(kRegValueUpdatesSuppressedDurationMin, + test_case.suppress_duration_minutes)); + EXPECT_EQ(test_case.expect_updates_suppressed && (IsDomain() || IsDM()), + AreUpdatesSuppressedNow(test_case.now)); + } +} + TEST_P(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Default) { - EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes()); + EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Override_TooBig) { EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheSizeLimitMBytes, 8192)); - EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes()); + EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Override_TooSmall) { EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheSizeLimitMBytes, 0)); - EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes()); + EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Override_Valid) { EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheSizeLimitMBytes, 250)); - EXPECT_EQ(IsDomain() ? 250 : 500, cm_->GetPackageCacheSizeLimitMBytes()); + EXPECT_EQ(IsDomain() ? 250 : 500, cm_->GetPackageCacheSizeLimitMBytes(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Default) { - EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays()); + EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Override_TooBig) { EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheLifeLimitDays, 3600)); - EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays()); + EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Override_TooSmall) { EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheLifeLimitDays, 0)); - EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays()); + EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays(NULL)); } TEST_P(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Override_Valid) { EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheLifeLimitDays, 60)); - EXPECT_EQ(IsDomain() ? 60 : 180, cm_->GetPackageCacheExpirationTimeDays()); + EXPECT_EQ(IsDomain() ? 60 : 180, + cm_->GetPackageCacheExpirationTimeDays(NULL)); } TEST_P(ConfigManagerTest, LastCheckedTime) { @@ -2006,16 +2311,18 @@ TEST_P(ConfigManagerTest, GetAutoUpdateJitterMs) { } TEST_P(ConfigManagerTest, GetDownloadPreferenceGroupPolicy) { - EXPECT_STREQ(_T(""), cm_->GetDownloadPreferenceGroupPolicy()); + EXPECT_STREQ(IsDM() ? kDownloadPreferenceCacheable : _T(""), + cm_->GetDownloadPreferenceGroupPolicy(NULL)); EXPECT_SUCCEEDED(SetPolicyString(kRegValueDownloadPreference, _T("unknown"))); - EXPECT_STREQ(_T(""), cm_->GetDownloadPreferenceGroupPolicy()); + EXPECT_STREQ(IsDM() ? kDownloadPreferenceCacheable : _T(""), + cm_->GetDownloadPreferenceGroupPolicy(NULL)); EXPECT_SUCCEEDED(SetPolicyString(kRegValueDownloadPreference, kDownloadPreferenceCacheable)); - EXPECT_STREQ(IsDomain() ? kDownloadPreferenceCacheable : _T(""), - cm_->GetDownloadPreferenceGroupPolicy()); + EXPECT_STREQ(IsDomain() || IsDM() ? kDownloadPreferenceCacheable : _T(""), + cm_->GetDownloadPreferenceGroupPolicy(NULL)); } #if defined(HAS_DEVICE_MANAGEMENT) diff --git a/omaha/common/const_cmd_line.h b/omaha/common/const_cmd_line.h index c45730c57..440631bc1 100644 --- a/omaha/common/const_cmd_line.h +++ b/omaha/common/const_cmd_line.h @@ -20,10 +20,7 @@ namespace omaha { -// -// Externally initiated modes. -// These modes are invoked by or on metainstallers or by the OneClick plugin . -// +// Externally initiated modes. These modes are invoked by metainstallers. // The "install" switch indicates installing Omaha and the app. const TCHAR* const kCmdLineInstall = _T("install"); @@ -38,12 +35,6 @@ const TCHAR* const kCmdLineUpdate = _T("update"); // Code Red scenario. const TCHAR* const kCmdLineRecover = _T("recover"); -// The "pi" switch indicates that this came from a webplugin. -// Requires two subarguments "siteurl" and "{args}" where -// siteurl is the base URL where the plugin ran from and {args} -// are the args to pass on once validation is complete. -const TCHAR* const kCmdLineWebPlugin = _T("pi"); - // // Main operating modes // @@ -158,9 +149,6 @@ const TCHAR* const kCmdLinePing = _T("ping"); // These are used for debug, testing, etc. // -// Run network diagnostics. -const TCHAR* const kCmdLineNetDiags = _T("netdiags"); - // The "crash" switch indicates that Omaha should crash upon startup. // This option is used to test the crash reporting system. const TCHAR* const kCmdLineCrash = _T("crash"); @@ -173,6 +161,10 @@ const TCHAR* const kCmdLineCrash = _T("crash"); // silently. const TCHAR* const kCmdLineSilent = _T("silent"); +// The "alwayslaunchcmd" switch specifies that the launch command is to be +// executed unconditionally, even for silent modes. +const TCHAR* const kCmdLineAlwaysLaunchCmd = _T("alwayslaunchcmd"); + const TCHAR* const kCmdLineLegacyOfflineInstall = _T("offlineinstall"); const TCHAR* const kCmdLineOfflineDir = _T("offlinedir"); @@ -204,13 +196,12 @@ const TCHAR* const kCmdLineInteractive = _T("i"); const TCHAR* const kCmdLineSessionId = _T("sessionid"); // The "installsource" switch that is used to pass the source of installation -// for ping tracking. For example: "/installsource OneClick". +// for ping tracking. For example: "/installsource taggedmi". const TCHAR* const kCmdLineInstallSource = _T("installsource"); // "installsource" values generated internally by Omaha. The server code needs // to be updated when these values change or new values are defined. const TCHAR* const kCmdLineInstallSource_TaggedMetainstaller = _T("taggedmi"); -const TCHAR* const kCmdLineInstallSource_OneClick = _T("oneclick"); const TCHAR* const kCmdLineInstallSource_ClickOnce = _T("clickonce"); const TCHAR* const kCmdLineInstallSource_Offline = _T("offline"); const TCHAR* const kCmdLineInstallSource_InstallDefault = _T("otherinstallcmd"); @@ -297,11 +288,22 @@ const TCHAR* const kExtraArgTTToken = _T("tttoken"); // successful install. const TCHAR* const kExtraArgBrowserType = _T("browser"); -// "runtime" extra argument tells Omaha to only install itself, staying on -// the system without any associated application for at least 24 hours. +// Runtime Mode: +// * "runtime" extra argument of "true" tells Omaha to only install itself, +// staying on the system without any associated application for at least 24 +// hours. // This is used to expose our COM API to a process that will install // applications via that API after the meta-installer exits. -const TCHAR* const kExtraArgRuntime = _T("runtime"); +// +// * "runtime" extra argument of "persist" tells Omaha to only install itself, +// staying persisted indefinitely on the system without any associated +// application. +// This is used to allow Enterprises to Push application installs to individual +// machines using Policy. +// +// * "runtime" extra argument of "false" tells Omaha that it can uninstall +// itself if there are no registered apps. +const TCHAR* const kExtraArgRuntimeMode = _T("runtime"); #if defined(HAS_DEVICE_MANAGEMENT) diff --git a/omaha/common/const_goopdate.h b/omaha/common/const_goopdate.h index eac72b8ef..c6c54597a 100644 --- a/omaha/common/const_goopdate.h +++ b/omaha/common/const_goopdate.h @@ -88,6 +88,15 @@ enum NeedsAdmin { // permissions allow, else will install per-user. }; +// Represents the values that are used by the application to indicate the +// Runtime Mode. +enum RuntimeMode { + RUNTIME_MODE_NOT_SET = -1, + RUNTIME_MODE_FALSE = 0, // Omaha will uninstall if no registered apps. + RUNTIME_MODE_TRUE = 1, // Omaha will remain around for 24 hours. + RUNTIME_MODE_PERSIST = 2, // Omaha will remain around indefinitely. +}; + // Using extern or intern linkage for these strings yields the same code size // for the executable DLL. @@ -218,14 +227,19 @@ const TCHAR* const kRegValueCohortHint = _T("hint"); const TCHAR* const kRegValueCohortName = _T("name"); // Registry values stored in the Update key. -const TCHAR* const kRegValueDelayOmahaUninstall = _T("DelayUninstall"); +const TCHAR* const kRegValueRuntimeMode = _T("RuntimeMode"); const TCHAR* const kRegValueOmahaEulaAccepted = _T("eulaaccepted"); // TODO(omaha3): Consider renaming these if there is not a upgrade problem. // If we can't consider moving all "gupdate" values to the customization file. -const TCHAR* const kRegValueServiceName = _T("omaha_service_name"); -const TCHAR* const kRegValueMediumServiceName = _T("omaham_service_name"); -const TCHAR* const kRegValueTaskNameC = _T("omaha_task_name_c"); -const TCHAR* const kRegValueTaskNameUA = _T("omaha_task_name_ua"); +// Use a non-gupdate name for the new medium service. +#define SERVICE_PREFIX _T("omaha") +#define MEDIUM_SERVICE_PREFIX _T("omaham") +const TCHAR* const kServicePrefix = SERVICE_PREFIX; +const TCHAR* const kMediumServicePrefix = MEDIUM_SERVICE_PREFIX; +const TCHAR* const kRegValueServiceName = SERVICE_PREFIX _T("_service_name"); +const TCHAR* const kRegValueMediumServiceName = MEDIUM_SERVICE_PREFIX _T("_service_name"); +const TCHAR* const kRegValueTaskNameC = SERVICE_PREFIX _T("_task_name_c"); +const TCHAR* const kRegValueTaskNameUA = SERVICE_PREFIX _T("_task_name_ua"); const TCHAR* const kRegValueLastStartedAU = _T("LastStartedAU"); const TCHAR* const kRegValueLastChecked = _T("LastChecked"); const TCHAR* const kRegValueLastCoreRun = _T("LastCoreRun"); @@ -239,7 +253,6 @@ const TCHAR* const kRegValueSelfUpdateExtraCode1 = _T("UpdateCode1"); const TCHAR* const kRegValueSelfUpdateErrorCode = _T("UpdateError"); const TCHAR* const kRegValueSelfUpdateVersion = _T("UpdateVersion"); const TCHAR* const kRegValueInstalledVersion = _T("version"); -const TCHAR* const kRegValueIsMSIHelperRegistered = _T("IsMSIHelperRegistered"); const TCHAR* const kRegValueLastOSVersion = _T("LastOSVersion"); // Indicates the time when it is safe for the client to connect to the server @@ -293,11 +306,6 @@ const TCHAR kRegValueCloudManagementEnrollmentToken[] = #endif // defined(HAS_DEVICE_MANAGEMENT) -// TODO(omaha3): Consider moving all "gupdate" values to the customization file. -// Use a non-gupdate name for the new medium service. -const TCHAR* const kServicePrefix = _T("omaha"); -const TCHAR* const kMediumServicePrefix = _T("omaham"); - const TCHAR* const kScheduledTaskNameUserPrefix = APP_NAME_IDENTIFIER _T("TaskUser"); const TCHAR* const kScheduledTaskNameMachinePrefix = @@ -393,6 +401,15 @@ const TCHAR* const kProgIDCredentialDialogUser = const TCHAR* const kProgIDCredentialDialogMachine = APP_NAME_IDENTIFIER _T(".CredentialDialogMachine"); +const TCHAR* const kProgIDPolicyStatusUser = + APP_NAME_IDENTIFIER _T(".PolicyStatusUser"); +#define kProgIDPolicyStatusMachine \ + APP_NAME_IDENTIFIER _T(".PolicyStatusMachine") +const TCHAR* const kProgIDPolicyStatusMachineFallback = + APP_NAME_IDENTIFIER _T(".PolicyStatusMachineFallback"); +const TCHAR* const kProgIDPolicyStatusSvc = + APP_NAME_IDENTIFIER _T(".PolicyStatusSvc"); + // Offline v3 manifest name. const TCHAR* const kOfflineManifestFileName = _T("OfflineManifest.gup"); diff --git a/omaha/common/const_group_policy.h b/omaha/common/const_group_policy.h index 715c060b8..a03b42820 100644 --- a/omaha/common/const_group_policy.h +++ b/omaha/common/const_group_policy.h @@ -50,6 +50,14 @@ const TCHAR* const kRegValueUpdatesSuppressedStartMin = _T("UpdatesSuppressedStartMin"); const TCHAR* const kRegValueUpdatesSuppressedDurationMin = _T("UpdatesSuppressedDurationMin"); +const TCHAR* const kRegValueCloudPolicyOverridesPlatformPolicy = + _T("CloudPolicyOverridesPlatformPolicy"); + +// The maximum value allowed for policy AutoUpdateCheckPeriodMinutes. +const int kMaxAutoUpdateCheckPeriodMinutes = 43200; + +// The maximum value allowed for policy UpdatesSuppressedDurationMin. +const int kMaxUpdatesSuppressedDurationMin = 960; // This policy specifies what kind of download URLs could be returned to the // client in the update response and in which order of priority. The client @@ -107,14 +115,18 @@ const TCHAR* const kRegValueInstallAppsDefault = _T("InstallDefault"); const TCHAR* const kRegValueInstallAppPrefix = _T("Install"); const TCHAR* const kRegValueUpdateAppsDefault = _T("UpdateDefault"); const TCHAR* const kRegValueUpdateAppPrefix = _T("Update"); +const TCHAR* const kRegValueTargetChannel = _T("TargetChannel"); const TCHAR* const kRegValueTargetVersionPrefix = _T("TargetVersionPrefix"); const TCHAR* const kRegValueRollbackToTargetVersion = _T("RollbackToTargetVersion"); const int kPolicyDisabled = 0; const int kPolicyEnabled = 1; +const int kPolicyEnabledMachineOnly = 4; const int kPolicyManualUpdatesOnly = 2; const int kPolicyAutomaticUpdatesOnly = 3; +const int kPolicyForceInstallMachine = 5; +const int kPolicyForceInstallUser = 6; const bool kInstallPolicyDefault = kPolicyEnabled; const bool kUpdatePolicyDefault = kPolicyEnabled; diff --git a/omaha/common/crash_utils.cc b/omaha/common/crash_utils.cc index 125b55864..6250e20cf 100644 --- a/omaha/common/crash_utils.cc +++ b/omaha/common/crash_utils.cc @@ -90,14 +90,6 @@ HRESULT InitializeCrashDir(bool is_machine, CString *crash_dir_out) { CString dir = is_machine ? cm->GetMachineCrashReportsDir() : cm->GetUserCrashReportsDir(); - if (is_machine && !dir.IsEmpty()) { - HRESULT hr = InitializeCrashDirSecurity(&dir); - if (FAILED(hr)) { - CORE_LOG(LW, (_T("[failed to initialize crash dir security][0x%x]"), hr)); - ::RemoveDirectory(dir); - } - } - // Use the temporary directory of the process if the crash directory can't be // initialized for any reason. Users can't read files in other users' temp // directories, so the temp dir is a good option to still have crash handling. diff --git a/omaha/common/crash_utils_unittest.cc b/omaha/common/crash_utils_unittest.cc index fbeec5caa..62e95e1e7 100644 --- a/omaha/common/crash_utils_unittest.cc +++ b/omaha/common/crash_utils_unittest.cc @@ -16,7 +16,6 @@ #include #include #include "omaha/base/app_util.h" -#include "omaha/base/atl_regexp.h" #include "omaha/base/constants.h" #include "omaha/base/const_addresses.h" #include "omaha/base/const_object_names.h" diff --git a/omaha/common/event_logger.cc b/omaha/common/event_logger.cc index 17e7b4d7f..7dd0ae6c1 100644 --- a/omaha/common/event_logger.cc +++ b/omaha/common/event_logger.cc @@ -33,9 +33,6 @@ namespace omaha { void LogEventHelper(WORD type, DWORD id, size_t count, const TCHAR** strings, const TCHAR* ctx) { ASSERT1(count <= static_cast(std::numeric_limits::max())); - if (!ConfigManager::Instance()->CanLogEvents(type)) { - return; - } // Include the circular logging buffer in the event log if the type is a // warning or an error. @@ -45,6 +42,16 @@ void LogEventHelper(WORD type, DWORD id, size_t count, const TCHAR** strings, SafeCStringAAppendFormat(&data, "\n[More context: %S]", context); } + CString joined_strings; + for (size_t i = 0; i < count; ++i) { + SafeCStringAppendFormat(&joined_strings, L"[%s]", strings[i]); + } + OPT_LOG(L1, (_T("[LogEventHelper][%d][%d][%d][%s][%S]"), type, id, count, + joined_strings, data)); + if (!ConfigManager::Instance()->CanLogEvents(type)) { + return; + } + HRESULT hr = EventLogger::ReportEvent(EventLogger::kSourceName, type, EventLogger::kDefaultCategory, @@ -141,7 +148,7 @@ HRESULT EventLogger::ReportEvent(const TCHAR* src_name, // logging provides for logging the sid at no cost so that the user shows up // in the event log. CString sid_string; - VERIFY1(SUCCEEDED(user_info::GetEffectiveUserSid(&sid_string))); + VERIFY_SUCCEEDED(user_info::GetEffectiveUserSid(&sid_string)); PSID psid = NULL; if (!sid_string.IsEmpty()) { VERIFY1(::ConvertStringSidToSid(sid_string, &psid)); diff --git a/omaha/common/event_logger_unittest.cc b/omaha/common/event_logger_unittest.cc index 175a0e152..3a5102282 100644 --- a/omaha/common/event_logger_unittest.cc +++ b/omaha/common/event_logger_unittest.cc @@ -99,7 +99,7 @@ TEST_F(EventLoggerTest, ReportEvent) { strings, arraysize(buf), buf)); - // Read the record at the top to do a brief sanity check. + // Read the record at the top to do a brief check. const size_t kBufferSize = 1024 * 64; byte buffer[kBufferSize] = {0}; EVENTLOGRECORD* rec = reinterpret_cast(buffer); diff --git a/omaha/common/exception_handler.cc b/omaha/common/exception_handler.cc index ad0813fa6..653ead58f 100644 --- a/omaha/common/exception_handler.cc +++ b/omaha/common/exception_handler.cc @@ -116,7 +116,7 @@ HRESULT OmahaExceptionHandler::Initialize( // for OOP, otherwise Omaha crashes will be handled in-process. HRESULT OmahaExceptionHandler::InstallHandler() { CString pipe_name; - VERIFY1(SUCCEEDED(crash_utils::GetCrashPipeName(&pipe_name))); + VERIFY_SUCCEEDED(crash_utils::GetCrashPipeName(&pipe_name)); UTIL_LOG(L6, (_T("[crash pipe][%s]"), pipe_name)); // If the machine is internal to Google, include full memory in the minidump; diff --git a/omaha/common/experiment_labels.cc b/omaha/common/experiment_labels.cc index a8db118b6..446f00cc9 100644 --- a/omaha/common/experiment_labels.cc +++ b/omaha/common/experiment_labels.cc @@ -182,8 +182,8 @@ HRESULT ExperimentLabels::ReadFromRegistry(bool is_machine, const CString state_key( app_registry_utils::GetAppClientStateKey(is_machine, app_id)); if (RegKey::HasValue(state_key, kRegValueExperimentLabels)) { - VERIFY1(SUCCEEDED(RegKey::GetValue( - state_key, kRegValueExperimentLabels, &label_list))); + VERIFY_SUCCEEDED(RegKey::GetValue( + state_key, kRegValueExperimentLabels, &label_list)); } if (!Deserialize(label_list)) { @@ -199,8 +199,8 @@ HRESULT ExperimentLabels::ReadFromRegistry(bool is_machine, const CString med_state_key( app_registry_utils::GetAppClientStateMediumKey(true, app_id)); if (RegKey::HasValue(med_state_key, kRegValueExperimentLabels)) { - VERIFY1(SUCCEEDED(RegKey::GetValue( - med_state_key, kRegValueExperimentLabels, &label_list))); + VERIFY_SUCCEEDED(RegKey::GetValue( + med_state_key, kRegValueExperimentLabels, &label_list)); } if (!label_list.IsEmpty()) { @@ -344,7 +344,7 @@ bool ExperimentLabels::MergeLabelSets(const CString& old_label_list, CString ExperimentLabels::ReadRegistry(bool is_machine, const CString& app_id) { ExperimentLabels stored_labels; - VERIFY1(SUCCEEDED(stored_labels.ReadFromRegistry(is_machine, app_id))); + VERIFY_SUCCEEDED(stored_labels.ReadFromRegistry(is_machine, app_id)); return stored_labels.Serialize(SerializeOptions::INCLUDE_TIMESTAMPS); } @@ -377,8 +377,10 @@ HRESULT ExperimentLabels::WriteRegistry(bool is_machine, if (is_machine) { const CString med_state_key( app_registry_utils::GetAppClientStateMediumKey(is_machine, app_id)); - VERIFY1(SUCCEEDED(RegKey::DeleteValue(med_state_key, - kRegValueExperimentLabels))); + if (RegKey::HasValue(med_state_key, kRegValueExperimentLabels)) { + VERIFY_SUCCEEDED( + RegKey::DeleteValue(med_state_key, kRegValueExperimentLabels)); + } } return hr; @@ -386,7 +388,7 @@ HRESULT ExperimentLabels::WriteRegistry(bool is_machine, CString ExperimentLabels::RemoveTimestamps(const CString& labels) { ExperimentLabels stored_labels; - VERIFY1(SUCCEEDED(stored_labels.Deserialize(labels))); + VERIFY_SUCCEEDED(stored_labels.Deserialize(labels)); return stored_labels.Serialize(SerializeOptions::EXCLUDE_TIMESTAMPS); } diff --git a/omaha/common/experiment_labels_unittest.cc b/omaha/common/experiment_labels_unittest.cc index 312cb30ea..ef706f626 100644 --- a/omaha/common/experiment_labels_unittest.cc +++ b/omaha/common/experiment_labels_unittest.cc @@ -62,19 +62,16 @@ namespace { const TCHAR* const kLabelOneKey = LABELONE_KEY; const TCHAR* const kLabelOneValue = LABELONE_VALUE; -const TCHAR* const kLabelOneExpStr = LABELONE_EXP_STR; const time64 kLabelOneExpInt = LABELONE_EXP_INT; const TCHAR* const kLabelOneCombined = LABELONE_COMBINED; const TCHAR* const kLabelTwoKey = LABELTWO_KEY; const TCHAR* const kLabelTwoValue = LABELTWO_VALUE; -const TCHAR* const kLabelTwoExpStr = LABELTWO_EXP_STR; const time64 kLabelTwoExpInt = LABELTWO_EXP_INT; const TCHAR* const kLabelTwoCombined = LABELTWO_COMBINED; const TCHAR* const kLabelOldKey = LABELOLD_KEY; const TCHAR* const kLabelOldValue = LABELOLD_VALUE; -const TCHAR* const kLabelOldExpStr = LABELOLD_EXP_STR; const time64 kLabelOldExpInt = LABELOLD_EXP_INT; const TCHAR* const kLabelOldCombined = LABELOLD_COMBINED; diff --git a/omaha/common/extra_args_parser.cc b/omaha/common/extra_args_parser.cc index ee03a78a9..a0e4cbb8e 100644 --- a/omaha/common/extra_args_parser.cc +++ b/omaha/common/extra_args_parser.cc @@ -152,6 +152,26 @@ HRESULT StringToNeedsAdmin(const TCHAR* str, NeedsAdmin* value) { return S_OK; } +HRESULT StringToRuntimeMode(const TCHAR* str, RuntimeMode* value) { + ASSERT1(str); + ASSERT1(value); + + const TCHAR* const kFalse = _T("false"); + const TCHAR* const kTrue = _T("true"); + const TCHAR* const kPersist = _T("persist"); + + if (!_tcsicmp(kFalse, str)) { + *value = RUNTIME_MODE_FALSE; + } else if (!_tcsicmp(kTrue, str)) { + *value = RUNTIME_MODE_TRUE; + } else if (!_tcsicmp(kPersist, str)) { + *value = RUNTIME_MODE_PERSIST; + } else { + return E_INVALIDARG; + } + return S_OK; +} + } // namespace // If no bundle name is specified, the first app's name is used. @@ -200,7 +220,8 @@ HRESULT ExtraArgsParser::ParseExtraArgs(const TCHAR* extra_args, // Save the arguments for the last application. args->apps.push_back(cur_extra_app_args_); - ASSERT1(!(args->runtime_only && args->apps[0].app_guid != GUID_NULL)); + ASSERT1(args->runtime_mode == RUNTIME_MODE_NOT_SET || + args->apps[0].app_guid == GUID_NULL); if (args->bundle_name.IsEmpty()) { ASSERT1(!args->apps.empty()); @@ -266,14 +287,17 @@ HRESULT ExtraArgsParser::HandleToken(const CString& token, if (!String_StringToTristate(value, &args->usage_stats_enable)) { return E_INVALIDARG; } - } else if (name.CompareNoCase(kExtraArgRuntime) == 0) { + } else if (name.CompareNoCase(kExtraArgRuntimeMode) == 0) { if (!args->apps.empty() || cur_extra_app_args_.app_guid != GUID_NULL) { return E_INVALIDARG; } - args->runtime_only = true; + + if (FAILED(StringToRuntimeMode(value, &args->runtime_mode))) { + return E_INVALIDARG; + } #if defined(HAS_DEVICE_MANAGEMENT) } else if (name.CompareNoCase(kExtraArgEnrollmentToken) == 0) { - if (value.GetLength() > kEnrollmentTokenMaxLength) { + if (!IsUuid(value)) { return E_INVALIDARG; } args->enrollment_token = value; @@ -302,7 +326,8 @@ HRESULT ExtraArgsParser::HandleToken(const CString& token, if (FAILED(hr)) { return hr; } - if (cur_extra_app_args_.app_guid == GUID_NULL || args->runtime_only) { + if (cur_extra_app_args_.app_guid == GUID_NULL || + args->runtime_mode != RUNTIME_MODE_NOT_SET) { return E_INVALIDARG; } first_app_ = false; diff --git a/omaha/common/extra_args_parser_unittest.cc b/omaha/common/extra_args_parser_unittest.cc index c55c49fee..72186f6bd 100644 --- a/omaha/common/extra_args_parser_unittest.cc +++ b/omaha/common/extra_args_parser_unittest.cc @@ -51,7 +51,7 @@ void VerifyExtraArgsHaveSpecificValues( const CString& expected_untrusted_data, NeedsAdmin expected_needs_admin, Tristate expected_usage_stats_enable, - bool expected_runtime_only, + RuntimeMode expected_runtime_mode, const CString& expected_bundle_name, const GUID& expected_installation_id, const CString& expected_brand_code, @@ -82,7 +82,7 @@ void VerifyExtraArgsHaveSpecificValues( expected.experiment_labels = expected_omaha_experiment_labels; expected.referral_id = expected_referral_id; expected.usage_stats_enable = expected_usage_stats_enable; - expected.runtime_only = expected_runtime_only; + expected.runtime_mode = expected_runtime_mode; VerifyCommandLineExtraArgs(expected, args); } @@ -484,7 +484,7 @@ TEST(ExtraArgsParserTest, ExtraArgumentsHaveDoubleAmpersand) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -512,7 +512,7 @@ TEST(ExtraArgsParserTest, ExtraArgumentsAmpersandOnly) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -541,7 +541,7 @@ TEST(ExtraArgsParserTest, ExtraArgumentsBeginInAmpersand) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -570,7 +570,7 @@ TEST(ExtraArgsParserTest, ExtraArgumentsEndInAmpersand) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -645,7 +645,7 @@ TEST(ExtraArgsParserTest, ExtraArgumentsOneValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -673,7 +673,7 @@ TEST(ExtraArgsParserTest, ExtraArgumentsTwoValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -838,7 +838,7 @@ TEST(ExtraArgsParserTest, UsageStatsOn) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -866,7 +866,7 @@ TEST(ExtraArgsParserTest, UsageStatsOff) { _T(""), NEEDS_ADMIN_NO, TRISTATE_FALSE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -895,7 +895,7 @@ TEST(ExtraArgsParserTest, UsageStatsNone) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -947,7 +947,7 @@ TEST(ExtraArgsParserTest, BundleNameValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T("Google Bundle"), GUID_NULL, _T(""), @@ -975,7 +975,7 @@ TEST(ExtraArgsParserTest, BundleNameNotPresentButAppNameIs) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T("Google Chrome"), GUID_NULL, _T(""), @@ -1001,7 +1001,7 @@ TEST(ExtraArgsParserTest, BundleNameNorAppNamePresent) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1028,7 +1028,7 @@ TEST(ExtraArgsParserTest, BundleNameNotPresentAndNoApp) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1058,7 +1058,7 @@ TEST(ExtraArgsParserTest, InstallationGuidValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), expected_guid, _T(""), @@ -1118,7 +1118,7 @@ TEST(ExtraArgsParserTest, BrandCodeValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T("GOOG"), @@ -1155,7 +1155,7 @@ TEST(ExtraArgsParserTest, ClientIdValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1183,7 +1183,7 @@ TEST(ExtraArgsParserTest, OmahaExperimentIdValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1211,7 +1211,7 @@ TEST(ExtraArgsParserTest, AppExperimentIdValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1239,7 +1239,7 @@ TEST(ExtraArgsParserTest, ReferralIdValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1267,7 +1267,7 @@ TEST(ExtraArgsParserTest, ApValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1295,7 +1295,7 @@ TEST(ExtraArgsParserTest, TTValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1326,7 +1326,7 @@ TEST(ExtraArgsParserTest, AppArgsValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1389,7 +1389,7 @@ TEST(ExtraArgsParserTest, InstallDataIndexValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - false, + RUNTIME_MODE_NOT_SET, _T(""), GUID_NULL, _T(""), @@ -1529,7 +1529,7 @@ TEST(ExtraArgsParserTest, RuntimeValid) { _T(""), NEEDS_ADMIN_NO, TRISTATE_NONE, - true, + RUNTIME_MODE_TRUE, _T(""), GUID_NULL, _T(""), @@ -1559,7 +1559,7 @@ TEST(ExtraArgsParserTest, RuntimeWithExtraArgs) { _T(""), NEEDS_ADMIN_NO, TRISTATE_TRUE, - true, + RUNTIME_MODE_TRUE, _T("Google Bundle"), GUID_NULL, _T("GOOG"), @@ -1572,6 +1572,59 @@ TEST(ExtraArgsParserTest, RuntimeWithExtraArgs) { _T("")); } +TEST(ExtraArgsParserTest, RuntimePersist) { + CommandLineExtraArgs args; + ExtraArgsParser parser; + CString extra_args = _T("runtime=persist"); + + EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args)); + VerifyExtraArgsHaveSpecificValues( + args, + _T("{00000000-0000-0000-0000-000000000000}"), + _T(""), + _T(""), + _T(""), + NEEDS_ADMIN_NO, + TRISTATE_NONE, + RUNTIME_MODE_PERSIST, + _T(""), + GUID_NULL, + _T(""), + _T(""), + _T(""), + _T(""), + _T(""), + _T(""), + _T(""), + _T("")); +} + +TEST(ExtraArgsParserTest, RuntimeFalse) { + CommandLineExtraArgs args; + ExtraArgsParser parser; + CString extra_args = _T("runtime=false"); + + EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args)); + VerifyExtraArgsHaveSpecificValues( + args, + _T("{00000000-0000-0000-0000-000000000000}"), + _T(""), + _T(""), + _T(""), + NEEDS_ADMIN_NO, + TRISTATE_NONE, + RUNTIME_MODE_FALSE, + _T(""), + GUID_NULL, + _T(""), + _T(""), + _T(""), + _T(""), + _T(""), + _T(""), + _T(""), + _T("")); +} TEST(ExtraArgsParserTest, RuntimeBeforeAppGuid) { CommandLineExtraArgs args; diff --git a/omaha/common/google_signaturevalidator.cc b/omaha/common/google_signaturevalidator.cc index bd492883b..5173511dc 100644 --- a/omaha/common/google_signaturevalidator.cc +++ b/omaha/common/google_signaturevalidator.cc @@ -39,13 +39,12 @@ HRESULT VerifyGoogleAuthenticodeSignature(const CString& filename, std::vector subject; subject.push_back(kSha256CertificateSubjectName); - subject.push_back(kCertificateSubjectName); + subject.push_back(kSha1CertificateSubjectName); + subject.push_back(kLegacyCertificateSubjectName); - const bool allow_test_variant = false; const bool check_cert_is_valid_now = false; hr = VerifyCertificate(filename, subject, - allow_test_variant, check_cert_is_valid_now, expected_hashes.empty() ? NULL : &expected_hashes); if (FAILED(hr)) { diff --git a/omaha/common/goopdate_command_line_validator.cc b/omaha/common/goopdate_command_line_validator.cc index 80f17a6bd..f37cec738 100644 --- a/omaha/common/goopdate_command_line_validator.cc +++ b/omaha/common/goopdate_command_line_validator.cc @@ -77,10 +77,6 @@ HRESULT GoopdateCommandLineValidator::Setup() { SafeCStringFormat(&cmd_line, _T("/%s"), kCmdUnregServer); CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUnregServer); - // gu.exe /netdiags - SafeCStringFormat(&cmd_line, _T("/%s"), kCmdLineNetDiags); - CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnNetDiags); - // gu.exe /crash SafeCStringFormat(&cmd_line, _T("/%s"), kCmdLineCrash); CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCrash); @@ -96,15 +92,16 @@ HRESULT GoopdateCommandLineValidator::Setup() { CreateScenario(kCmdLineOnDemand, &GoopdateCommandLineValidator::OnDemand); // gu.exe /install [/appargs [/installsource source - // [/silent [/eularequired [/oem [/installelevated [/sessionid - // [/enterprise + // [/silent [/alwayslaunchcmd [/eularequired [/oem [/installelevated + // [/sessionid [/enterprise SafeCStringFormat( - &cmd_line, _T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s ") + &cmd_line, _T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s [/%s ") _T("[/%s sid [/%s"), kCmdLineInstall, kCmdLineAppArgs, kCmdLineInstallSource, kCmdLineSilent, + kCmdLineAlwaysLaunchCmd, kCmdLineEulaRequired, kCmdLineOem, kCmdLineInstallElevated, @@ -118,15 +115,16 @@ HRESULT GoopdateCommandLineValidator::Setup() { CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUpdate); // gu.exe /handoff [/appargs [/installsource source - // [/silent [/eularequired [/offlineinstall [/offlinedir

- // [/sessionid [/enterprise + // [/silent [/alwayslaunchcmd [/eularequired [/offlineinstall + // [/offlinedir [/sessionid [/enterprise SafeCStringFormat( - &cmd_line, _T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s dir ") - _T("[/%s sid [/%s"), + &cmd_line, _T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s ") + _T("[/%s dir [/%s sid [/%s"), kCmdLineAppHandoffInstall, kCmdLineAppArgs, kCmdLineInstallSource, kCmdLineSilent, + kCmdLineAlwaysLaunchCmd, kCmdLineEulaRequired, kCmdLineLegacyOfflineInstall, kCmdLineOfflineDir, @@ -157,12 +155,6 @@ HRESULT GoopdateCommandLineValidator::Setup() { CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnReportCrashInteractive); - // gu.exe /pi /installsource - SafeCStringFormat(&cmd_line, _T("/%s domainurl args /%s src"), - kCmdLineWebPlugin, - kCmdLineInstallSource); - CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnWebPlugin); - // gu.exe /cr SafeCStringFormat(&cmd_line, _T("/%s"), kCmdLineCodeRedCheck); CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCodeRed); @@ -198,10 +190,6 @@ HRESULT GoopdateCommandLineValidator::Setup() { SafeCStringFormat(&cmd_line, _T("/%s"), kCmdLineHealthCheck); CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnHealthCheck); - // gu.exe /registermsihelper - SafeCStringFormat(&cmd_line, _T("/%s"), kCmdLineRegisterMsiHelper); - CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRegisterMsiHelper); - return S_OK; } @@ -313,11 +301,6 @@ HRESULT GoopdateCommandLineValidator::OnUnregServer() { return S_OK; } -HRESULT GoopdateCommandLineValidator::OnNetDiags() { - args_->mode = COMMANDLINE_MODE_NETDIAGS; - return S_OK; -} - HRESULT GoopdateCommandLineValidator::OnCrash() { args_->mode = COMMANDLINE_MODE_CRASH; return S_OK; @@ -347,6 +330,7 @@ HRESULT GoopdateCommandLineValidator::OnInstall() { 0, &args_->session_id); args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent); + args_->is_always_launch_cmd_set = parser_->HasSwitch(kCmdLineAlwaysLaunchCmd); args_->is_enterprise_set = parser_->HasSwitch(kCmdLineEnterprise); args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired); args_->is_oem_set = parser_->HasSwitch(kCmdLineOem); @@ -371,6 +355,7 @@ HRESULT GoopdateCommandLineValidator::OnInstallHandoffWorker() { 0, &args_->session_id); args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent); + args_->is_always_launch_cmd_set = parser_->HasSwitch(kCmdLineAlwaysLaunchCmd); args_->is_enterprise_set = parser_->HasSwitch(kCmdLineEnterprise); args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired); args_->is_offline_set = parser_->HasSwitch(kCmdLineLegacyOfflineInstall) || @@ -420,45 +405,6 @@ HRESULT GoopdateCommandLineValidator::OnReportCrashInteractive() { &args_->crash_filename); } -HRESULT GoopdateCommandLineValidator::OnWebPlugin() { - HRESULT hr = parser_->GetSwitchArgumentValue(kCmdLineInstallSource, - 0, - &args_->install_source); - if (FAILED(hr)) { - return hr; - } - // Validate install_source value. - args_->install_source.MakeLower(); - if ((args_->install_source.Compare(kCmdLineInstallSource_OneClick) != 0) && - (args_->install_source.Compare(kCmdLineInstallSource_Update3Web) != 0)) { - args_->install_source.Empty(); - return E_INVALIDARG; - } - - args_->mode = COMMANDLINE_MODE_WEBPLUGIN; - - CString urldomain; - hr = parser_->GetSwitchArgumentValue(kCmdLineWebPlugin, - 0, - &urldomain); - if (FAILED(hr)) { - return hr; - } - hr = StringUnescape(urldomain, &args_->webplugin_urldomain); - if (FAILED(hr)) { - return hr; - } - - CString webplugin_args; - hr = parser_->GetSwitchArgumentValue(kCmdLineWebPlugin, - 1, - &webplugin_args); - if (FAILED(hr)) { - return hr; - } - return StringUnescape(webplugin_args, &args_->webplugin_args); -} - HRESULT GoopdateCommandLineValidator::OnCodeRed() { args_->mode = COMMANDLINE_MODE_CODE_RED_CHECK; return S_OK; @@ -511,10 +457,5 @@ HRESULT GoopdateCommandLineValidator::OnHealthCheck() { return S_OK; } -HRESULT GoopdateCommandLineValidator::OnRegisterMsiHelper() { - args_->mode = COMMANDLINE_MODE_REGISTER_MSI_HELPER; - return S_OK; -} - } // namespace omaha diff --git a/omaha/common/goopdate_command_line_validator.h b/omaha/common/goopdate_command_line_validator.h index 50bfb7c5d..e9578cdcc 100644 --- a/omaha/common/goopdate_command_line_validator.h +++ b/omaha/common/goopdate_command_line_validator.h @@ -57,7 +57,6 @@ class GoopdateCommandLineValidator { HRESULT OnServiceUnregister(); HRESULT OnRegServer(); HRESULT OnUnregServer(); - HRESULT OnNetDiags(); HRESULT OnCrash(); HRESULT OnComServer(); HRESULT OnComBroker(); @@ -68,7 +67,6 @@ class GoopdateCommandLineValidator { HRESULT OnUpdateApps(); HRESULT OnReportCrash(); HRESULT OnReportCrashInteractive(); - HRESULT OnWebPlugin(); HRESULT OnCodeRed(); HRESULT OnRecover(); HRESULT OnRecoverMachine(); diff --git a/omaha/common/goopdate_utils.cc b/omaha/common/goopdate_utils.cc index a2f94027c..ee1eabcd0 100644 --- a/omaha/common/goopdate_utils.cc +++ b/omaha/common/goopdate_utils.cc @@ -17,7 +17,9 @@ #include #include +#include #include +#include #include "omaha/base/app_util.h" #include "omaha/base/const_addresses.h" @@ -37,7 +39,6 @@ #include "omaha/base/scope_guard.h" #include "omaha/base/scoped_impersonation.h" #include "omaha/base/service_utils.h" -#include "omaha/base/signatures.h" #include "omaha/base/string.h" #include "omaha/base/system.h" #include "omaha/base/system_info.h" @@ -359,20 +360,34 @@ CString GetInstalledShellVersion(bool is_machine) { } HRESULT StartGoogleUpdateWithArgs(bool is_machine, + StartMode start_mode, const TCHAR* args, HANDLE* process) { - CORE_LOG(L3, (_T("[StartGoogleUpdateWithArgs][%d][%s]"), - is_machine, args ? args : _T(""))); + CORE_LOG(L3, (_T("[StartGoogleUpdateWithArgs][%d][%d][%s]"), + is_machine, start_mode, args ? args : _T(""))); CString exe_path = BuildGoogleUpdateExePath(is_machine); CORE_LOG(L3, (_T("[command line][%s][%s]"), exe_path, args ? args : _T(""))); - HRESULT hr = System::ShellExecuteProcess(exe_path, args, NULL, process); + PROCESS_INFORMATION pi = {0}; + HRESULT hr = start_mode == StartMode::kForeground ? + System::ShellExecuteProcess(exe_path, args, NULL, process) : + System::StartProcessWithArgsAndInfo(exe_path, args, &pi); if (FAILED(hr)) { CORE_LOG(LE, (_T("[can't start process][%s][0x%08x]"), exe_path, hr)); return hr; } + + if (start_mode != StartMode::kForeground) { + if (process) { + *process = pi.hProcess; + } else { + ::CloseHandle(pi.hProcess); + } + ::CloseHandle(pi.hThread); + } + return S_OK; } @@ -682,7 +697,7 @@ HRESULT RegisterTypeLibForUser(ITypeLib* lib, // help_dir can be NULL. const TCHAR* library_name = _T("oleaut32.dll"); - scoped_library module(static_cast(::LoadLibrary(library_name))); + scoped_library module(LoadSystemLibrary(library_name)); if (!module) { HRESULT hr = HRESULTFromLastError(); CORE_LOG(LEVEL_ERROR, @@ -721,7 +736,7 @@ HRESULT UnRegisterTypeLibForUser(REFGUID lib_id, CORE_LOG(L3, (_T("[UnRegisterTypeLibForUser]"))); const TCHAR* library_name = _T("oleaut32.dll"); - scoped_library module(static_cast(::LoadLibrary(library_name))); + scoped_library module(LoadSystemLibrary(library_name)); if (!module) { HRESULT hr = HRESULTFromLastError(); CORE_LOG(LEVEL_ERROR, @@ -1271,7 +1286,7 @@ HRESULT OpenUniqueEventFromEnvironment(const CString& var_name, return S_OK; } -// The caller is responsible for reseting the event and closing the handle. +// The caller is responsible for resetting the event and closing the handle. HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle) { ASSERT1(event_handle); ASSERT1(event_attr); @@ -1376,22 +1391,25 @@ HRESULT WriteNameValuePairsToHandle(const HANDLE file_handle, bool IsAppInstallWorkerRunning(bool is_machine) { CORE_LOG(L3, (_T("[IsAppInstallWorkerRunning][%d]"), is_machine)); std::vector processes; - VERIFY1(SUCCEEDED(GetInstallWorkerProcesses(is_machine, &processes))); + VERIFY_SUCCEEDED(GetInstallWorkerProcesses(is_machine, &processes)); 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)); @@ -1434,14 +1452,15 @@ DEFINE_METRIC_integer(last_checked); HRESULT UpdateLastChecked(bool is_machine) { // Set the last check value to the current value. DWORD now = Time64ToInt32(GetCurrent100NSTime()); - CORE_LOG(L3, (_T("[UpdateLastChecked][now %d]"), now)); metric_last_checked = now; HRESULT hr = ConfigManager::Instance()->SetLastCheckedTime(is_machine, now); if (FAILED(hr)) { - CORE_LOG(LE, (_T("[SetLastCheckedTime failed][0x%08x]"), hr)); + OPT_LOG(LE, (_T("[SetLastCheckedTime failed][0x%08x]"), hr)); return hr; } + + OPT_LOG(L1, (_T("[UpdateLastChecked][now %d]"), now)); return S_OK; } @@ -1464,7 +1483,7 @@ HANDLE GetImpersonationTokenForMachineProcess(bool is_machine) { } bool is_local_system(false); - VERIFY1(SUCCEEDED(IsSystemProcess(&is_local_system))); + VERIFY_SUCCEEDED(IsSystemProcess(&is_local_system)); if (!is_local_system) { return NULL; } @@ -1554,7 +1573,7 @@ HRESULT GetMacHashesViaNDIS(std::vector* mac_hashes) { } DWORD query = OID_802_3_PERMANENT_ADDRESS; - BYTE mac_address[6]; + char mac_address[6]; DWORD mac_size = sizeof(mac_address); if (!::DeviceIoControl(get(file_handle), IOCTL_NDIS_QUERY_GLOBAL_STATS, @@ -1570,11 +1589,11 @@ HRESULT GetMacHashesViaNDIS(std::vector* mac_hashes) { continue; } - std::vector mac_address_buffer(arraysize(mac_address)); - ::memcpy(&mac_address_buffer.front(), mac_address, arraysize(mac_address)); - CStringA hashed_mac_address; - Base64::Encode(mac_address_buffer, &hashed_mac_address, false); + Base64Escape(mac_address, + arraysize(mac_address), + &hashed_mac_address, + true); mac_hashes->push_back(AnsiToWideString(hashed_mac_address, hashed_mac_address.GetLength())); } @@ -1604,7 +1623,7 @@ HRESULT ResetMacHashesInRegistry(bool is_machine, ASSERT1(!mac_hashes.empty()); GLock user_id_lock; - VERIFY1(SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock))); + VERIFY_SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock)); __mutexScope(user_id_lock); const ConfigManager& config_manager = *ConfigManager::Instance(); @@ -1613,7 +1632,7 @@ HRESULT ResetMacHashesInRegistry(bool is_machine, reg_path = AppendRegKeyPath(reg_path, kRegSubkeyUserId); // Delete any leftover/old MAC hashes. - VERIFY1(SUCCEEDED(RegKey::DeleteKey(reg_path))); + VERIFY_SUCCEEDED(RegKey::DeleteKey(reg_path)); RegKey reg_key_uid; DWORD disposition = 0; @@ -1671,23 +1690,23 @@ HRESULT ResetUserIdIfMacMismatch(bool is_machine) { HRESULT ResetUserId(bool is_machine, bool is_legacy) { GLock user_id_lock; - VERIFY1(SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock))); + VERIFY_SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock)); __mutexScope(user_id_lock); RegKey update_key; - VERIFY1(SUCCEEDED(update_key.Open( - ConfigManager::Instance()->registry_update(is_machine)))); + VERIFY_SUCCEEDED(update_key.Open( + ConfigManager::Instance()->registry_update(is_machine))); if (update_key.HasValue(kRegValueOldUserId)) { - VERIFY1(SUCCEEDED(update_key.DeleteValue(kRegValueUserId))); + VERIFY_SUCCEEDED(update_key.DeleteValue(kRegValueUserId)); } else { - VERIFY1(SUCCEEDED(update_key.RenameValue(kRegValueUserId, - kRegValueOldUserId))); + VERIFY_SUCCEEDED(update_key.RenameValue(kRegValueUserId, + kRegValueOldUserId)); if (is_legacy) { CString uid; - VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueOldUserId, &uid))); + VERIFY_SUCCEEDED(update_key.GetValue(kRegValueOldUserId, &uid)); uid.Append(kRegValueDataLegacyUserId); - VERIFY1(SUCCEEDED(update_key.SetValue(kRegValueOldUserId, uid))); + VERIFY_SUCCEEDED(update_key.SetValue(kRegValueOldUserId, uid)); } } @@ -1697,15 +1716,15 @@ HRESULT ResetUserId(bool is_machine, bool is_legacy) { void RecordNewUserIdCreated(const RegKey& update_key) { // UID creation timestamp. DWORD now = Time64ToInt32(GetCurrent100NSTime()); - VERIFY1(SUCCEEDED(update_key.SetValue(kRegValueUserIdCreateTime, now))); + VERIFY_SUCCEEDED(update_key.SetValue(kRegValueUserIdCreateTime, now)); CORE_LOG(L3, (_T("[New UID creation time: %d]"), now)); // Increment number of UID rotations. DWORD num_rotations(0); update_key.GetValue(kRegValueUserIdNumRotations, &num_rotations); ++num_rotations; - VERIFY1(SUCCEEDED(update_key.SetValue(kRegValueUserIdNumRotations, - num_rotations))); + VERIFY_SUCCEEDED(update_key.SetValue(kRegValueUserIdNumRotations, + num_rotations)); } DEFINE_METRIC_count(opt_in_uid_generated); @@ -1717,7 +1736,7 @@ HRESULT CreateUserId(bool is_machine) { } GLock user_id_lock; - VERIFY1(SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock))); + VERIFY_SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock)); __mutexScope(user_id_lock); RegKey update_key; @@ -1766,7 +1785,7 @@ void DeleteUserId(bool is_machine) { } GLock user_id_lock; - VERIFY1(SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock))); + VERIFY_SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock)); __mutexScope(user_id_lock); RegKey update_key; @@ -1776,9 +1795,9 @@ void DeleteUserId(bool is_machine) { return; } - VERIFY1(SUCCEEDED(update_key.DeleteValue(kRegValueUserId))); - VERIFY1(SUCCEEDED(update_key.DeleteValue(kRegValueOldUserId))); - VERIFY1(SUCCEEDED(update_key.DeleteSubKey(kRegSubkeyUserId))); + VERIFY_SUCCEEDED(update_key.DeleteValue(kRegValueUserId)); + VERIFY_SUCCEEDED(update_key.DeleteValue(kRegValueOldUserId)); + VERIFY_SUCCEEDED(update_key.DeleteSubKey(kRegSubkeyUserId)); } CString GetUserIdLazyInit(bool is_machine) { @@ -1805,7 +1824,7 @@ CString GetUserIdLazyInit(bool is_machine) { CString GetUserIdHistory(bool is_machine) { GLock user_id_lock; - VERIFY1(SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock))); + VERIFY_SUCCEEDED(InitializeUserIdLock(is_machine, &user_id_lock)); __mutexScope(user_id_lock); CString old_uid; diff --git a/omaha/common/goopdate_utils.h b/omaha/common/goopdate_utils.h index d138b5599..0e1d032ca 100644 --- a/omaha/common/goopdate_utils.h +++ b/omaha/common/goopdate_utils.h @@ -34,6 +34,10 @@ class NetworkRequest; class UpdateResponse; struct NamedObjectAttributes; +// The enum for the values of the |start_mode| parameter for the function +// StartGoogleUpdateWithArgs(). +enum class StartMode { kForeground, kBackground }; + // Represents the Result of an attempt to terminate the browser. struct TerminateBrowserResult { TerminateBrowserResult() @@ -85,10 +89,10 @@ CString GetHKRoot(); CString GetInstalledShellVersion(bool is_machine); // Starts an instance of the Google Update version found in the registry. -// Only use to start interactive processes because it uses ::ShellExecuteEx(). // args can be NULL. // process can be NULL. If not NULL, caller is responsible for closing handle. HRESULT StartGoogleUpdateWithArgs(bool is_machine, + StartMode start_mode, const TCHAR* args, HANDLE* process); @@ -243,10 +247,11 @@ HRESULT WriteNameValuePairsToHandle(const HANDLE file_handle, bool IsAppInstallWorkerRunning(bool is_machine); // Converts the installer_data value to UTF8. Then writes this UTF8 data -// prefixed with the UTF8 BOM of EF BB BF to a temp file. Returns the path to -// temp file that was created. The returned path will be quote-enclosed by -// EnclosePath(). -HRESULT WriteInstallerDataToTempFile(const CString& installer_data, +// prefixed with the UTF8 BOM of EF BB BF to a temp file in `directory`. Returns +// the path to temp file that was created. The returned path will be +// quote-enclosed by EnclosePath(). +HRESULT WriteInstallerDataToTempFile(const CPath& directory, + const CString& installer_data, CString* installer_data_file_path); // Updates LastChecked to now. Call after successful update check for all apps. diff --git a/omaha/common/goopdate_utils_unittest.cc b/omaha/common/goopdate_utils_unittest.cc index d9fd063de..e5d3ba854 100644 --- a/omaha/common/goopdate_utils_unittest.cc +++ b/omaha/common/goopdate_utils_unittest.cc @@ -20,11 +20,12 @@ #include #include #include + #include #include +#include // NOLINT #include "omaha/base/app_util.h" -#include "omaha/base/atl_regexp.h" #include "omaha/base/browser_utils.h" #include "omaha/base/constants.h" #include "omaha/base/const_utils.h" @@ -56,10 +57,10 @@ namespace omaha { namespace { -#define DUMMY_CLSID _T("{6FC94136-0D4C-450e-99C2-BCDA72A9C8F0}") -const TCHAR* hkcr_key_name = _T("HKCR\\CLSID\\") DUMMY_CLSID; -const TCHAR* hklm_key_name = _T("HKLM\\Software\\Classes\\CLSID\\") DUMMY_CLSID; -const TCHAR* hkcu_key_name = _T("HKCU\\Software\\Classes\\CLSID\\") DUMMY_CLSID; +#define TEST_CLSID _T("{6FC94136-0D4C-450e-99C2-BCDA72A9C8F0}") +const TCHAR* hkcr_key_name = _T("HKCR\\CLSID\\") TEST_CLSID; +const TCHAR* hklm_key_name = _T("HKLM\\Software\\Classes\\CLSID\\") TEST_CLSID; +const TCHAR* hkcu_key_name = _T("HKCU\\Software\\Classes\\CLSID\\") TEST_CLSID; const TCHAR* kAppId = _T("{3DAE8C13-C394-481E-8163-4E7A7699084F}"); @@ -896,17 +897,18 @@ TEST(GoopdateUtilsTest, GetOSInfo) { CString os_version = SystemInfo::GetKernel32OSVersion(); EXPECT_TRUE(!os_version.IsEmpty()); - const AtlRE major_minor_build = _T("{\\d+\\.\\d+\\.\\d+}"); + const std::wregex major_minor_build { _T("\\d+\\.\\d+\\.\\d+") }; - CString expected_os_version; - CString actual_os_version; - EXPECT_TRUE(AtlRE::PartialMatch(os_version, - major_minor_build, - &expected_os_version)); - EXPECT_TRUE(AtlRE::PartialMatch(os_version_getosinfo, - major_minor_build, - &actual_os_version)); - EXPECT_STREQ(expected_os_version, actual_os_version); + std::wcmatch expected_os_version; + EXPECT_TRUE(std::regex_search(os_version.GetString(), + expected_os_version, + major_minor_build)); + std::wcmatch actual_os_version; + EXPECT_TRUE(std::regex_search(os_version.GetString(), + actual_os_version, + major_minor_build)); + EXPECT_STREQ(expected_os_version.str(0).c_str(), + actual_os_version.str(0).c_str()); } class GoopdateUtilsRegistryProtectedTest : public testing::Test { @@ -1106,8 +1108,8 @@ TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest, CString path = BuildGoogleUpdateExePath(true); CString program_files_path; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path)); - EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME + - _T("\\") + PRODUCT_NAME + _T("\\GoogleUpdate.exe"), + EXPECT_STREQ(program_files_path + _T("\\") + PATH_COMPANY_NAME + + _T("\\") + PRODUCT_NAME + _T("\\") + MAIN_EXE_BASE_NAME + _T(".exe"), path); } @@ -1117,15 +1119,15 @@ TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest, CString path = BuildGoogleUpdateExePath(true); CString program_files_path; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path)); - EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME + - _T("\\") + PRODUCT_NAME + _T("\\GoogleUpdate.exe"), + EXPECT_STREQ(program_files_path + _T("\\") + PATH_COMPANY_NAME + + _T("\\") + PRODUCT_NAME + _T("\\") + MAIN_EXE_BASE_NAME + _T(".exe"), path); // Test when the key exists but the value doesn't. ASSERT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENTS_GOOPDATE)); path = BuildGoogleUpdateExePath(true); - EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME + - _T("\\") + PRODUCT_NAME + _T("\\GoogleUpdate.exe"), + EXPECT_STREQ(program_files_path + _T("\\") + PATH_COMPANY_NAME + + _T("\\") + PRODUCT_NAME + _T("\\") + MAIN_EXE_BASE_NAME + _T(".exe"), path); } @@ -1141,8 +1143,8 @@ TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest, CString user_appdata; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA, &user_appdata)); CString expected_path; - expected_path.Format(_T("%s\\") SHORT_COMPANY_NAME _T("\\") - PRODUCT_NAME _T("\\GoogleUpdate.exe"), + expected_path.Format(_T("%s\\") PATH_COMPANY_NAME _T("\\") + PRODUCT_NAME _T("\\") MAIN_EXE_BASE_NAME _T(".exe"), user_appdata); EXPECT_STREQ(expected_path, path); } @@ -1152,8 +1154,8 @@ TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest, CString user_appdata; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA, &user_appdata)); CString expected_path; - expected_path.Format(_T("%s\\") SHORT_COMPANY_NAME _T("\\") - PRODUCT_NAME _T("\\GoogleUpdate.exe"), + expected_path.Format(_T("%s\\") PATH_COMPANY_NAME _T("\\") + PRODUCT_NAME _T("\\") MAIN_EXE_BASE_NAME _T(".exe"), user_appdata); // Test when the key doesn't exist. @@ -1175,7 +1177,10 @@ TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest, _T("pv"), _T("1.2.3.4"))); const TCHAR* kArgs = _T("/cr"); - HRESULT hr = StartGoogleUpdateWithArgs(true, kArgs, NULL); + HRESULT hr = + StartGoogleUpdateWithArgs(true, StartMode::kForeground, kArgs, NULL); + EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr); + hr = StartGoogleUpdateWithArgs(true, StartMode::kBackground, kArgs, NULL); EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr); } @@ -1191,7 +1196,10 @@ TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest, _T("pv"), _T("1.2.3.4"))); const TCHAR* kArgs = _T("/cr"); - HRESULT hr = StartGoogleUpdateWithArgs(false, kArgs, NULL); + HRESULT hr = + StartGoogleUpdateWithArgs(false, StartMode::kForeground, kArgs, NULL); + EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr); + hr = StartGoogleUpdateWithArgs(false, StartMode::kBackground, kArgs, NULL); EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr); } @@ -1199,7 +1207,7 @@ TEST(GoopdateUtilsTest, BuildInstallDirectory_Machine) { const CPath dir = BuildInstallDirectory(true, _T("1.2.3.0")); CString program_files_path; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path)); - EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME + + EXPECT_STREQ(program_files_path + _T("\\") + PATH_COMPANY_NAME + _T("\\") + PRODUCT_NAME + _T("\\1.2.3.0"), dir); } @@ -1466,6 +1474,12 @@ TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadManyPairs) { } TEST(GoopdateUtilsTest, WriteInstallerDataToTempFile) { + CString file_path; + EXPECT_EQ(S_FALSE, WriteInstallerDataToTempFile( + CPath(_T("NonExistentDirectory")), + CString(_T("hello\n")), + &file_path)); + CStringA utf8_bom; utf8_bom.Format("%c%c%c", 0xEF, 0xBB, 0xBF); @@ -1491,12 +1505,14 @@ TEST(GoopdateUtilsTest, WriteInstallerDataToTempFile) { ASSERT_EQ(expected_installer_data.size(), list_installer_data.size()); + const CPath directory(app_util::GetCurrentModuleDirectory()); for (size_t i = 0; i < list_installer_data.size(); ++i) { CString installer_data = list_installer_data[i]; - SCOPED_TRACE(installer_data); + SCOPED_TRACE(installer_data.GetString()); - CString file_path; - HRESULT hr = WriteInstallerDataToTempFile(installer_data, &file_path); + HRESULT hr = WriteInstallerDataToTempFile(directory, + installer_data, + &file_path); ON_SCOPE_EXIT(::DeleteFile, file_path.GetString()); EXPECT_SUCCEEDED(hr); @@ -1829,12 +1845,14 @@ TEST(GoopdateUtilsTest, GetMacHashesViaNDIS) { for (size_t i = 0; i < mac_hashes.size(); ++i) { CStringA mac_hash(WideToUtf8(mac_hashes[i])); - std::vector mac; - EXPECT_SUCCEEDED(Base64::Decode(mac_hash, &mac)); + CStringA mac; + ASSERT_GE(Base64Unescape(mac_hash, &mac), 0); CString mac_string; - for (size_t j = 0; j < mac.size(); ++j) { - mac_string.AppendFormat(_T("%s%02X"), j == 0 ? _T("") : _T(":"), mac[j]); + for (int j = 0; j < mac.GetLength(); ++j) { + // The cast to uint8 is necessary to prevent sign extension. + const uint8 octet = static_cast(mac[j]); + mac_string.AppendFormat(_T("%s%02X"), j == 0 ? _T("") : _T(":"), octet); } ExpectMacMatchViaWMI(mac_string); diff --git a/omaha/common/omaha_customization_unittest.cc b/omaha/common/omaha_customization_unittest.cc index 3ab14f180..06a4377aa 100644 --- a/omaha/common/omaha_customization_unittest.cc +++ b/omaha/common/omaha_customization_unittest.cc @@ -65,26 +65,11 @@ TEST(OmahaCustomizationTest, Constants_BuildFiles) { EXPECT_TRUE(::IsEqualGUID(kProxyClsidIsUserGuid, kActualProxyClsidIsUserGuid)); - // VERSION file values. Only the relatively stable ones are tested. - // The versions may or may not match in non-Google Update builds. #ifdef GOOGLE_UPDATE_BUILD - EXPECT_STREQ("9", ONECLICK_PLUGIN_VERSION_ANSI); - // TODO(omaha): Change the name to ANSI. - EXPECT_STREQ("3", UPDATE_PLUGIN_VERSION_ANSI); -#else - std::wcout << _T("Did not test version values.") << std::endl; -#endif - // Primary omaha_version_utils values. EXPECT_STREQ(_T("npGoogleOneClick"), ONECLICK_PLUGIN_NAME); EXPECT_STREQ(_T("npGoogleUpdate"), UPDATE_PLUGIN_NAME); - - // Filenames from omaha_version_utils. - EXPECT_STREQ( - _T("npGoogleOneClick") _T(ONECLICK_PLUGIN_VERSION_ANSI) _T(".dll"), - ONECLICK_PLUGIN_FILENAME); - EXPECT_STREQ(_T("npGoogleUpdate") _T(UPDATE_PLUGIN_VERSION_ANSI) _T(".dll"), - UPDATE_PLUGIN_FILENAME); +#endif // GOOGLE_UPDATE_BUILD } TEST(OmahaCustomizationTest, Constants_Names) { @@ -112,25 +97,24 @@ TEST(OmahaCustomizationTest, Constants_Names) { // Other values based on the app name. EXPECT_STREQ(_T("_Google_Update_"), kLockPrefix); -#endif // GOOGLE_UPDATE_BUILD // Filename bases EXPECT_STREQ(_T("GoogleUpdate"), MAIN_EXE_BASE_NAME); +#endif // GOOGLE_UPDATE_BUILD EXPECT_STREQ(_T("goopdate"), MAIN_DLL_BASE_NAME); } TEST(OmahaCustomizationTest, Constants_Filenames) { - EXPECT_STREQ(_T("GoogleUpdate.exe"), kOmahaShellFileName); - EXPECT_STREQ(_T("GoogleCrashHandler.exe"), kCrashHandlerFileName); - EXPECT_STREQ(_T("GoogleCrashHandler64.exe"), kCrashHandler64FileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T(".exe"), kOmahaShellFileName); + EXPECT_STREQ(CRASH_HANDLER_NAME _T(".exe"), kCrashHandlerFileName); + EXPECT_STREQ(CRASH_HANDLER_NAME _T("64.exe"), kCrashHandler64FileName); EXPECT_STREQ(_T("goopdate.dll"), kOmahaDllName); EXPECT_STREQ(_T("goopdateres_%s.dll"), kOmahaResourceDllNameFormat); - EXPECT_STREQ(_T("GoogleUpdateBroker.exe"), kOmahaBrokerFileName); - EXPECT_STREQ(_T("GoogleUpdateCore.exe"), kOmahaCoreFileName); - EXPECT_STREQ(_T("GoogleUpdateOnDemand.exe"), kOmahaOnDemandFileName); - EXPECT_STREQ(_T("GoogleUpdateWebPlugin.exe"), kOmahaWebPluginFileName); - EXPECT_STREQ(_T("GoogleUpdateSetup.exe"), kOmahaMetainstallerFileName); - EXPECT_STREQ(_T("GoogleUpdateComRegisterShell64.exe"), + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T("Broker.exe"), kOmahaBrokerFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T("Core.exe"), kOmahaCoreFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T("OnDemand.exe"), kOmahaOnDemandFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T("Setup.exe"), kOmahaMetainstallerFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T("ComRegisterShell64.exe"), kOmahaCOMRegisterShell64); EXPECT_STREQ(_T("psmachine.dll"), kPSFileNameMachine); EXPECT_STREQ(_T("psmachine_64.dll"), kPSFileNameMachine64); @@ -139,22 +123,27 @@ TEST(OmahaCustomizationTest, Constants_Filenames) { } TEST(OmahaCustomizationTest, Constants_Certificate) { - EXPECT_STREQ(_T("Google Inc"), kCertificateSubjectName); + EXPECT_STREQ(_T("Google LLC"), kSha1CertificateSubjectName); + EXPECT_STREQ(_T("Google LLC"), kSha256CertificateSubjectName); } TEST(OmahaCustomizationTest, Constants_OmahaAppId_String) { +#ifdef GOOGLE_UPDATE_BUILD EXPECT_STREQ(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), GOOPDATE_APP_ID); EXPECT_STREQ(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), kGoogleUpdateAppId); +#endif } TEST(OmahaCustomizationTest, Constants_OmahaAppId_GUID) { +#ifdef GOOGLE_UPDATE_BUILD const GUID kExpectedGoogleUpdateGuid = {0x430FD4D0, 0xB729, 0x4F61, {0xAA, 0x34, 0x91, 0x52, 0x64, 0x81, 0x79, 0x9D}}; EXPECT_TRUE(::IsEqualGUID(kExpectedGoogleUpdateGuid, kGoopdateGuid)); EXPECT_STREQ(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), GuidToString(kGoopdateGuid)); +#endif } TEST(OmahaCustomizationTest, Constants_OmahaAppId_GUIDAndStringMatch) { @@ -163,15 +152,15 @@ TEST(OmahaCustomizationTest, Constants_OmahaAppId_GUIDAndStringMatch) { TEST(OmahaCustomizationTest, Constants_Directories) { EXPECT_STREQ(_T("Offline"), OFFLINE_DIR_NAME); - EXPECT_GU_STREQ(_T("Google"), OMAHA_REL_COMPANY_DIR); - EXPECT_GU_STREQ(_T("Google\\CrashReports"), OMAHA_REL_CRASH_DIR); - EXPECT_GU_STREQ(_T("Google\\Update"), OMAHA_REL_GOOPDATE_INSTALL_DIR); - EXPECT_GU_STREQ(_T("Google\\Update\\Log"), OMAHA_REL_LOG_DIR); - EXPECT_GU_STREQ(_T("Google\\Update\\Offline"), + EXPECT_STREQ(PATH_COMPANY_NAME, OMAHA_REL_COMPANY_DIR); + EXPECT_STREQ(PATH_COMPANY_NAME _T("\\CrashReports"), OMAHA_REL_CRASH_DIR); + EXPECT_STREQ(PATH_COMPANY_NAME _T("\\Update"), OMAHA_REL_GOOPDATE_INSTALL_DIR); + EXPECT_STREQ(PATH_COMPANY_NAME _T("\\Update\\Log"), OMAHA_REL_LOG_DIR); + EXPECT_STREQ(PATH_COMPANY_NAME _T("\\Update\\Offline"), OMAHA_REL_OFFLINE_STORAGE_DIR); - EXPECT_GU_STREQ(_T("Google\\Update\\Download"), + EXPECT_STREQ(PATH_COMPANY_NAME _T("\\Update\\Download"), OMAHA_REL_DOWNLOAD_STORAGE_DIR); - EXPECT_GU_STREQ(_T("Google\\Update\\Install"), + EXPECT_STREQ(PATH_COMPANY_NAME _T("\\Update\\Install"), OMAHA_REL_INSTALL_WORKING_DIR); } @@ -184,47 +173,44 @@ TEST(OmahaCustomizationTest, Constants_RegistryKeys_NotCustomized) { } TEST(OmahaCustomizationTest, Constants_RegistryKeys) { - EXPECT_GU_STREQ(_T("Software\\Google\\"), COMPANY_MAIN_KEY); - EXPECT_GU_STREQ(_T("Software\\Google\\Update\\"), GOOPDATE_MAIN_KEY); - EXPECT_GU_STREQ(_T("Software\\Google\\Update\\Clients\\"), GOOPDATE_REG_RELATIVE_CLIENTS); // NOLINT - EXPECT_GU_STREQ(_T("Software\\Google\\Update\\ClientState\\"), GOOPDATE_REG_RELATIVE_CLIENT_STATE); // NOLINT - EXPECT_GU_STREQ(_T("Software\\Google\\Update\\ClientStateMedium\\"), GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM); // NOLINT - EXPECT_GU_STREQ(_T("Software\\Policies\\Google\\"), COMPANY_POLICIES_MAIN_KEY); // NOLINT - EXPECT_GU_STREQ(_T("Software\\Policies\\Google\\Update\\"), GOOPDATE_POLICIES_RELATIVE); // NOLINT - - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\"), USER_REG_GOOGLE); - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\"), USER_REG_UPDATE); - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"), USER_REG_CLIENTS); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), USER_REG_CLIENTS_GOOPDATE); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"), USER_REG_CLIENT_STATE); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), USER_REG_CLIENT_STATE_GOOPDATE); // NOLINT - - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\"), MACHINE_REG_GOOGLE); - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\"), MACHINE_REG_UPDATE); - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"), MACHINE_REG_CLIENTS); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), MACHINE_REG_CLIENTS_GOOPDATE); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"), MACHINE_REG_CLIENT_STATE); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), MACHINE_REG_CLIENT_STATE_GOOPDATE); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"), MACHINE_REG_CLIENT_STATE_MEDIUM); // NOLINT - - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\UpdateDev\\"), MACHINE_REG_UPDATE_DEV); // NOLINT + EXPECT_STREQ(_T("Software\\") PATH_COMPANY_NAME _T("\\"), COMPANY_MAIN_KEY); + EXPECT_STREQ(_T("Software\\") PATH_COMPANY_NAME _T("\\Update\\"), GOOPDATE_MAIN_KEY); + EXPECT_STREQ(_T("Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), GOOPDATE_REG_RELATIVE_CLIENTS); // NOLINT + EXPECT_STREQ(_T("Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), GOOPDATE_REG_RELATIVE_CLIENT_STATE); // NOLINT + EXPECT_STREQ(_T("Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientStateMedium\\"), GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM); // NOLINT + EXPECT_STREQ(_T("Software\\Policies\\") PATH_COMPANY_NAME _T("\\"), COMPANY_POLICIES_MAIN_KEY); // NOLINT + EXPECT_STREQ(_T("Software\\Policies\\") PATH_COMPANY_NAME _T("\\Update\\"), GOOPDATE_POLICIES_RELATIVE); // NOLINT + + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\"), USER_REG_GOOGLE); + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\"), USER_REG_UPDATE); + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), USER_REG_CLIENTS); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\" GOOPDATE_APP_ID), USER_REG_CLIENTS_GOOPDATE); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), USER_REG_CLIENT_STATE); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\" GOOPDATE_APP_ID), USER_REG_CLIENT_STATE_GOOPDATE); // NOLINT + + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\"), MACHINE_REG_GOOGLE); + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\"), MACHINE_REG_UPDATE); + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), MACHINE_REG_CLIENTS); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\" GOOPDATE_APP_ID), MACHINE_REG_CLIENTS_GOOPDATE); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), MACHINE_REG_CLIENT_STATE); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\" GOOPDATE_APP_ID), MACHINE_REG_CLIENT_STATE_GOOPDATE); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientStateMedium\\"), MACHINE_REG_CLIENT_STATE_MEDIUM); // NOLINT + + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\UpdateDev\\"), MACHINE_REG_UPDATE_DEV); // NOLINT } TEST(OmahaCustomizationTest, Constants_RegistryKeys_GroupPolicy) { - EXPECT_GU_STREQ(_T("Software\\Policies\\Google\\Update\\"), GOOPDATE_POLICIES_RELATIVE); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Policies\\Google\\Update\\"), kRegKeyGoopdateGroupPolicy); // NOLINT + EXPECT_STREQ(_T("Software\\Policies\\") PATH_COMPANY_NAME _T("\\Update\\"), GOOPDATE_POLICIES_RELATIVE); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\Policies\\") PATH_COMPANY_NAME _T("\\Update\\"), kRegKeyGoopdateGroupPolicy); // NOLINT } TEST(OmahaCustomizationTest, Constants_RegistryValues) { EXPECT_GU_STREQ(_T("Google Update"), kRunValueName); } -TEST(OmahaCustomizationTest, Constants_MsiMsp) { - EXPECT_STREQ(_T("GoogleUpdateHelper.msi"), kHelperInstallerName); +TEST(OmahaCustomizationTest, Constants_LegacyMsi) { EXPECT_STREQ(_T("{A92DAB39-4E2C-4304-9AB6-BC44E68B55E2}"), - kHelperInstallerProductGuid); - EXPECT_STREQ(_T("GoogleUpdateHelperPatch.msp"), kHelperPatchName); - EXPECT_STREQ(_T("{E0D0D2C9-5836-4023-AB1D-54EC3B90AD03}"), kHelperPatchGuid); + kLegacyHelperInstallerGuid); } TEST(OmahaCustomizationTest, Constants_CompatibleMinimumOlderShellVersion) { @@ -236,6 +222,7 @@ TEST(OmahaCustomizationTest, Constants_BrandCode) { } TEST(OmahaCustomizationTest, Constants_Addresses) { +#ifdef GOOGLE_UPDATE_BUILD EXPECT_STREQ(_T("www.google.com"), kGoogleHttpServer); EXPECT_STREQ(_T("tools.google.com"), kGoopdateServer); EXPECT_STREQ(_T("https://update.googleapis.com/service/update2"), @@ -247,10 +234,11 @@ TEST(OmahaCustomizationTest, Constants_Addresses) { kUrlCodeRedCheck); EXPECT_STREQ(_T("https://clients5.google.com/tbproxy/usagestats"), kUrlUsageStatsReport); +#endif } TEST(OmahaCustomizationTest, Constants_Config) { - EXPECT_GU_STREQ(_T("Software\\Google\\Update\\Shared"), kCiRegKeyShared); + EXPECT_STREQ(_T("Software\\") PATH_COMPANY_NAME _T("\\Update\\Shared"), kCiRegKeyShared); } TEST(OmahaCustomizationTest, Constants_Debug) { @@ -258,8 +246,8 @@ TEST(OmahaCustomizationTest, Constants_Debug) { } TEST(OmahaCustomizationTest, Constants_Logging) { - EXPECT_STREQ(_T("GoogleUpdate.ini"), kLogConfigFileName); - EXPECT_STREQ(_T("GoogleUpdate.log"), kDefaultLogFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T(".ini"), kLogConfigFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T(".log"), kDefaultLogFileName); } // These should not change during customization. @@ -272,6 +260,7 @@ TEST(OmahaCustomizationTest, Constants_ObjectNames_Pipes) { } TEST(OmahaCustomizationTest, Constants_ObjectNames_MutexesAndEvents) { +#ifdef GOOGLE_UPDATE_BUILD EXPECT_STREQ(_T("{A9A86B93-B54E-4570-BE89-42418507707B}"), kSetupMutex); EXPECT_STREQ(_T("{A0C1F415-D2CE-4ddc-9B48-14E56FD55162}"), kShutdownEvent); EXPECT_STREQ(_T("{B5665124-2B19-40e2-A7BC-B44321E72C4B}"), @@ -288,6 +277,7 @@ TEST(OmahaCustomizationTest, Constants_ObjectNames_MutexesAndEvents) { kMetricsSerializer); EXPECT_STREQ(_T("{66CC0160-ABB3-4066-AE47-1CA6AD5065C8}"), kRegistryAccessMutex); +#endif } TEST(OmahaCustomizationTest, Constants_ObjectNames_SharedMemory) { @@ -306,7 +296,7 @@ TEST(OmahaCustomizationTest, Constants_Services) { EXPECT_GU_STREQ(_T("gupdate"), kServicePrefix); EXPECT_GU_STREQ(_T("gupdatem"), kMediumServicePrefix); - EXPECT_STREQ(_T("GoogleUpdate.exe"), kServiceFileName); + EXPECT_STREQ(MAIN_EXE_BASE_NAME _T(".exe"), kServiceFileName); } TEST(OmahaCustomizationTest, Constants_ScheduledTasks) { @@ -314,23 +304,6 @@ TEST(OmahaCustomizationTest, Constants_ScheduledTasks) { EXPECT_GU_STREQ(_T("GoogleUpdateTaskMachine"), kScheduledTaskNameMachinePrefix); // NOLINT } -TEST(OmahaCustomizationTest, Constants_Plugins) { - EXPECT_GU_STREQ(_T("Google.OneClickCtrl.") _T(ONECLICK_PLUGIN_VERSION_ANSI), - kOneClickProgId); - EXPECT_STREQ( - "application/x-vnd.google.oneclickctrl." ONECLICK_PLUGIN_VERSION_ANSI, - kOneClickPluginMimeTypeAnsi); -} - -TEST(OmahaCustomizationTest, Constants_HostCheck) { - EXPECT_EQ(5, arraysize(kSiteLockPatternStrings)); - EXPECT_STREQ(_T("^(gears)|(mail)|(tools)|(www)|(desktop)|(pack)|(chrome)|(drive)\\.google\\.com$"), kSiteLockPatternStrings[0]); // NOLINT - EXPECT_STREQ(_T("^www\\.google\\.(ad)|(bg)|(ca)|(cn)|(cz)|(de)|(es)|(fi)|(fr)|(gr)|(hr)|(hu)|(it)|(ki)|(kr)|(lt)|(lv)|(nl)|(no)|(pl)|(pt)|(ro)|(ru)|(sk)|(sg)|(sl)|(sr)|(vn)$"), kSiteLockPatternStrings[1]); // NOLINT - EXPECT_STREQ(_T("^www\\.google\\.co\\.(hu)|(id)|(il)|(it)|(jp)|(kr)|(th)|(uk)$"), kSiteLockPatternStrings[2]); // NOLINT - EXPECT_STREQ(_T("^www\\.google\\.com\\.(ar)|(au)|(br)|(cn)|(et)|(gr)|(hr)|(ki)|(lv)|(om)|(pl)|(pt)|(ru)|(sg)|(sv)|(tr)|(vn)$"), kSiteLockPatternStrings[3]); // NOLINT - EXPECT_STREQ(_T("^(www\\.)?chrome\\.com$"), kSiteLockPatternStrings[4]); -} - // // ConfigManager keys. // @@ -338,37 +311,37 @@ TEST(OmahaCustomizationTest, Constants_HostCheck) { TEST(OmahaCustomizationTest, ConfigManager_RegistryKeys) { const ConfigManager& cm = *ConfigManager::Instance(); - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"), cm.user_registry_clients()); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"), cm.machine_registry_clients()); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"), cm.registry_clients(false)); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"), cm.registry_clients(true)); // NOLINT - - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.user_registry_clients_goopdate()); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.machine_registry_clients_goopdate()); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_clients_goopdate(false)); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_clients_goopdate(true)); // NOLINT - - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"), cm.user_registry_client_state()); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"), cm.machine_registry_client_state()); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"), cm.registry_client_state(false)); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"), cm.registry_client_state(true)); // NOLINT - - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.user_registry_client_state_goopdate()); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.machine_registry_client_state_goopdate()); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_client_state_goopdate(false)); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_client_state_goopdate(true)); // NOLINT - - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"), cm.machine_registry_client_state_medium()); // NOLINT - - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\"), cm.user_registry_update()); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\"), cm.machine_registry_update()); // NOLINT - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\"), cm.registry_update(false)); // NOLINT - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\"), cm.registry_update(true)); // NOLINT - - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\"), cm.user_registry_google()); - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\"), cm.machine_registry_google()); - EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\"), cm.registry_google(false)); - EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\"), cm.registry_google(true)); + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), cm.user_registry_clients()); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), cm.machine_registry_clients()); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), cm.registry_clients(false)); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\"), cm.registry_clients(true)); // NOLINT + + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\" GOOPDATE_APP_ID), cm.user_registry_clients_goopdate()); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\" GOOPDATE_APP_ID), cm.machine_registry_clients_goopdate()); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\" GOOPDATE_APP_ID), cm.registry_clients_goopdate(false)); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\" GOOPDATE_APP_ID), cm.registry_clients_goopdate(true)); // NOLINT + + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), cm.user_registry_client_state()); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), cm.machine_registry_client_state()); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), cm.registry_client_state(false)); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\"), cm.registry_client_state(true)); // NOLINT + + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\" GOOPDATE_APP_ID), cm.user_registry_client_state_goopdate()); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\" GOOPDATE_APP_ID), cm.machine_registry_client_state_goopdate()); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\" GOOPDATE_APP_ID), cm.registry_client_state_goopdate(false)); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientState\\" GOOPDATE_APP_ID), cm.registry_client_state_goopdate(true)); // NOLINT + + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\ClientStateMedium\\"), cm.machine_registry_client_state_medium()); // NOLINT + + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\"), cm.user_registry_update()); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\"), cm.machine_registry_update()); // NOLINT + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\"), cm.registry_update(false)); // NOLINT + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\"), cm.registry_update(true)); // NOLINT + + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\"), cm.user_registry_google()); + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\"), cm.machine_registry_google()); + EXPECT_STREQ(_T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\"), cm.registry_google(false)); + EXPECT_STREQ(_T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\"), cm.registry_google(true)); } TEST(OmahaCustomizationTest, IsInternalUser) { @@ -390,12 +363,12 @@ TEST(OmahaCustomizationTest, IsInternalUser) { // TEST(OmahaCustomizationTest, GetGoogleUserPath) { - EXPECT_STREQ(GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\"), + EXPECT_STREQ(GetLocalAppDataPath() + PATH_COMPANY_NAME + _T("\\"), GetGoogleUserPath()); } TEST(OmahaCustomizationTest, GetGoogleUpdateUserPath) { - EXPECT_STREQ(GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\") + EXPECT_STREQ(GetLocalAppDataPath() + PATH_COMPANY_NAME + _T("\\") + PRODUCT_NAME + _T("\\"), GetGoogleUpdateUserPath()); } @@ -405,7 +378,7 @@ TEST(OmahaCustomizationTest, GetGoogleUpdateMachinePath) { CString expected_machine_path; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES | CSIDL_FLAG_DONT_VERIFY, &expected_machine_path)); - expected_machine_path.Append(_T("\\") SHORT_COMPANY_NAME + expected_machine_path.Append(_T("\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME); EXPECT_STREQ(expected_machine_path, GetGoogleUpdateMachinePath()); } diff --git a/omaha/common/ping.cc b/omaha/common/ping.cc index b1e540984..e4b0c7ae3 100644 --- a/omaha/common/ping.cc +++ b/omaha/common/ping.cc @@ -62,7 +62,7 @@ Ping::Ping(bool is_machine, const CString& session_id, const CString& install_source) { CString request_id; - VERIFY1(SUCCEEDED(GetGuid(&request_id))); + VERIFY_SUCCEEDED(GetGuid(&request_id)); Initialize(is_machine, session_id, install_source, request_id); } @@ -88,8 +88,8 @@ void Ping::Initialize(bool is_machine, Ping::~Ping() { } -void Ping::BuildRequest(const App* app, bool is_update_check) { - update_request_utils::BuildRequest(app, is_update_check, ping_request_.get()); +void Ping::BuildRequest(const App* app) { + update_request_utils::BuildRequest(app, false, ping_request_.get()); } void Ping::LoadAppDataFromExtraArgs(const CommandLineExtraArgs& extra_args) { @@ -295,6 +295,7 @@ HRESULT Ping::SendUsingGoogleUpdate(const CString& request_string, scoped_process ping_process; HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_, + StartMode::kBackground, args, address(ping_process)); if (FAILED(hr)) { @@ -448,7 +449,7 @@ HRESULT Ping::DeletePersistedPing(bool is_machine, void Ping::DeletePersistedPingOnSuccess(const HRESULT& hr) { if (SUCCEEDED(hr)) { - VERIFY1(SUCCEEDED(DeletePersistedPing(is_machine_, request_id_))); + VERIFY_SUCCEEDED(DeletePersistedPing(is_machine_, request_id_)); } } @@ -516,8 +517,8 @@ HRESULT Ping::SendPersistedPings(bool is_machine) { if (SUCCEEDED(hr) || IsPingExpired(persisted_time)) { CORE_LOG(L3, (_T("[Deleting persisted ping][0x%x]"), hr)); - VERIFY1(SUCCEEDED(DeletePersistedPing(is_machine, - persisted_subkey_name))); + VERIFY_SUCCEEDED(DeletePersistedPing(is_machine, + persisted_subkey_name)); } } @@ -562,9 +563,9 @@ HRESULT Ping::SendString(bool is_machine, // Registry writes to HKLM need admin. scoped_revert_to_self revert_to_self; - VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( + VERIFY_SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( is_machine, - response.get()))); + response.get())); return S_OK; } @@ -599,7 +600,7 @@ HRESULT SendReliablePing(Ping* ping, bool is_fire_and_forget) { CORE_LOG(L6, (_T("[Ping::SendReliablePing]"))); ASSERT1(ping); - VERIFY1(SUCCEEDED(ping->PersistPing())); + VERIFY_SUCCEEDED(ping->PersistPing()); return ping->Send(is_fire_and_forget); } diff --git a/omaha/common/ping.h b/omaha/common/ping.h index ecd5f3d88..d68d3952d 100644 --- a/omaha/common/ping.h +++ b/omaha/common/ping.h @@ -103,7 +103,7 @@ class Ping { // TODO(omaha): Consider moving everything except the functionality that // actually sends the pings out of the Ping class into builder classes. A // dependency on the model App is not desirable here. - void BuildRequest(const App* app, bool is_update_check); + void BuildRequest(const App* app); // Loads app data from a location other than the Omaha state machine. void LoadAppDataFromExtraArgs(const CommandLineExtraArgs& extra_args); diff --git a/omaha/common/ping_event_download_metrics_unittest.cc b/omaha/common/ping_event_download_metrics_unittest.cc index 20c025efd..3e83dfe0d 100644 --- a/omaha/common/ping_event_download_metrics_unittest.cc +++ b/omaha/common/ping_event_download_metrics_unittest.cc @@ -35,7 +35,7 @@ class PingEventDownloadMetricsTest : public testing::Test { protected: void SetUpRegistry() { const TCHAR* const kOmahaUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") GOOPDATE_APP_ID; @@ -82,7 +82,7 @@ TEST_F(PingEventDownloadMetricsTest, BuildPing) { CString expected_ping_request_substring; expected_ping_request_substring = - _T("") diff --git a/omaha/common/ping_event_unittest.cc b/omaha/common/ping_event_unittest.cc index c50a6e01e..9b3517e78 100644 --- a/omaha/common/ping_event_unittest.cc +++ b/omaha/common/ping_event_unittest.cc @@ -38,7 +38,7 @@ class PingEventTest : public testing::Test { OverrideRegistryHives(kRegistryHiveOverrideRoot); const TCHAR* const kOmahaUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") GOOPDATE_APP_ID; diff --git a/omaha/common/ping_test.cc b/omaha/common/ping_test.cc index 04d1ecf11..78848cf8f 100644 --- a/omaha/common/ping_test.cc +++ b/omaha/common/ping_test.cc @@ -91,7 +91,7 @@ TEST_F(PingTest, BuildOmahaPing) { File::Remove(goopdate_utils::BuildGoogleUpdateExePath(false)); // User ping, missing shell. - Ping install_ping_no_shell(false, _T("session"), _T("oneclick")); + Ping install_ping_no_shell(false, _T("session"), _T("taggedmi")); install_ping_no_shell.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping_no_shell.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), @@ -102,7 +102,7 @@ TEST_F(PingTest, BuildOmahaPing) { expected_shell_version_substring.Format(_T(" shell_version=\"%s\" "), GetShellVersionString()); CString expected_ping_request_substring; - expected_ping_request_substring.Format(_T("")); // NOLINT + expected_ping_request_substring.Format(_T("")); // NOLINT CString actual_ping_request; install_ping_no_shell.BuildRequestString(&actual_ping_request); @@ -121,7 +121,7 @@ TEST_F(PingTest, BuildOmahaPing) { true)); // User ping, 1.2.183.21 shell. - Ping install_ping_1_2_183_21(false, _T("session"), _T("oneclick")); + Ping install_ping_1_2_183_21(false, _T("session"), _T("taggedmi")); install_ping_1_2_183_21.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping_1_2_183_21.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), @@ -135,11 +135,15 @@ TEST_F(PingTest, BuildOmahaPing) { EXPECT_NE(-1, actual_ping_request.Find(expected_shell_version_substring)); EXPECT_NE(-1, actual_ping_request.Find(expected_ping_request_substring)); + + // Clean up after ourselves, since we copied the test exe into this path + // earlier in this test. + EXPECT_SUCCEEDED(File::Remove(goopdate_utils::BuildGoogleUpdateExePath(false))); } TEST_F(PingTest, BuildAppsPing) { const TCHAR* const kOmahaUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") GOOPDATE_APP_ID; const CString expected_pv = _T("1.3.99.0"); @@ -195,7 +199,7 @@ TEST_F(PingTest, BuildAppsPing) { apps_ping.BuildAppsPing(ping_event); CString expected_ping_request_substring; - expected_ping_request_substring.Format(_T("")); // NOLINT + expected_ping_request_substring.Format(_T("")); // NOLINT CString actual_ping_request; apps_ping.BuildRequestString(&actual_ping_request); @@ -205,7 +209,7 @@ TEST_F(PingTest, BuildAppsPing) { } TEST_F(PingTest, DISABLED_SendString) { - CString request_string = _T(""); // NOLINT + CString request_string = _T(""); // NOLINT EXPECT_HRESULT_SUCCEEDED(Ping::SendString(false, HeadersVector(), request_string)); @@ -215,7 +219,7 @@ TEST_F(PingTest, DISABLED_SendString) { } TEST_F(PingTest, DISABLED_HandlePing) { - CString request_string = _T(""); // NOLINT + CString request_string = _T(""); // NOLINT CStringA request_string_utf8(WideToUtf8(request_string)); CStringA ping_string_utf8; @@ -244,7 +248,7 @@ TEST_F(PingTest, SendInProcess) { command_line_extra_args.language = _T("en"); // User ping. - Ping install_ping(false, _T("unittest"), _T("oneclick")); + Ping install_ping(false, _T("unittest"), _T("taggedmi")); install_ping.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event); @@ -325,7 +329,7 @@ TEST_F(PingTest, PersistAndSendPersistedPings) { command_line_extra_args.language = _T("en"); // User ping. - Ping install_ping(false, _T("unittest"), _T("oneclick")); + Ping install_ping(false, _T("unittest"), _T("taggedmi")); install_ping.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event); @@ -350,7 +354,7 @@ TEST_F(PingTest, PersistAndSendPersistedPings) { Ping::kRegValuePersistedPingString, &persisted_ping)); EXPECT_NE(-1, persisted_ping.Find(_T("sessionid=\"unittest\""))); - EXPECT_NE(-1, persisted_ping.Find(_T(""))); // NOLINT + EXPECT_NE(-1, persisted_ping.Find(_T(""))); // NOLINT EXPECT_HRESULT_SUCCEEDED(Ping::SendPersistedPings(false)); @@ -376,7 +380,7 @@ TEST_F(PingTest, DISABLED_SendUsingGoogleUpdate) { command_line_extra_args.language = _T("en"); // User ping and wait for completion. - Ping install_ping(false, _T("unittest"), _T("oneclick")); + Ping install_ping(false, _T("unittest"), _T("taggedmi")); install_ping.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event); @@ -389,7 +393,7 @@ TEST_F(PingTest, DISABLED_SendUsingGoogleUpdate) { TEST_F(PingTest, Send_Empty) { CommandLineExtraArgs command_line_extra_args; - Ping install_ping(false, _T("unittest"), _T("oneclick")); + Ping install_ping(false, _T("unittest"), _T("taggedmi")); EXPECT_EQ(S_FALSE, install_ping.Send(false)); } @@ -408,7 +412,7 @@ TEST_F(PingTest, DISABLED_Send) { command_line_extra_args.language = _T("en"); // User ping and wait for completion. - Ping install_ping(false, _T("unittest"), _T("oneclick")); + Ping install_ping(false, _T("unittest"), _T("taggedmi")); install_ping.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event); @@ -431,7 +435,7 @@ TEST_F(PingTest, DISABLED_SendFireAndForget) { command_line_extra_args.language = _T("en"); // User ping and do not wait for completion. - Ping install_ping(false, _T("unittest"), _T("oneclick")); + Ping install_ping(false, _T("unittest"), _T("taggedmi")); install_ping.LoadAppDataFromExtraArgs(command_line_extra_args); install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event); diff --git a/omaha/common/protocol_definition.h b/omaha/common/protocol_definition.h index 9a5689e7b..9f5390bbf 100644 --- a/omaha/common/protocol_definition.h +++ b/omaha/common/protocol_definition.h @@ -68,11 +68,14 @@ struct OS { CString platform; // "win". CString version; // major.minor. CString service_pack; - CString arch; // "x86", "x64", or "unknown". + CString arch; // "x86", "x64", "ARM64", etc, or "unknown". }; struct UpdateCheck { - UpdateCheck() : is_valid(false), is_update_disabled(false) {} + UpdateCheck() + : is_valid(false), + is_update_disabled(false), + is_rollback_allowed(false) {} // TODO(omaha): this member is not serialized. Use pointers to indicate // optional elements instead of is_valid. @@ -85,6 +88,8 @@ struct UpdateCheck { bool is_rollback_allowed; CString target_version_prefix; + + CString target_channel; }; @@ -295,7 +300,19 @@ struct DayStart { struct SystemRequirements { CString platform; // "win". - CString arch; // "x86", "x64", or "unknown". + + // Expected host processor architecture that the app is compatible with. + // `arch` can be a single entry, or multiple entries separated with `,`. + // Entries prefixed with a `-` (negative entries) indicate non-compatible + // hosts. + // + // Examples: + // * `arch` == "x86". + // * `arch` == "x64". + // * `arch` == "x86,x64,-arm64": the app will fail installation if the + // underlying host is arm64. + CString arch; + CString min_os_version; // major.minor. }; diff --git a/omaha/common/scheduled_task_utils.cc b/omaha/common/scheduled_task_utils.cc index 79370e431..f10dab18b 100644 --- a/omaha/common/scheduled_task_utils.cc +++ b/omaha/common/scheduled_task_utils.cc @@ -14,14 +14,17 @@ #include "omaha/common/scheduled_task_utils.h" #include "omaha/common/scheduled_task_utils_internal.h" -#include -#include + #include #include +#include +#include + #include "omaha/base/debug.h" #include "omaha/base/error.h" #include "omaha/base/logging.h" #include "omaha/base/omaha_version.h" +#include "omaha/base/path.h" #include "omaha/base/safe_format.h" #include "omaha/base/scoped_ptr_cotask.h" #include "omaha/base/service_utils.h" @@ -44,6 +47,21 @@ namespace scheduled_task_utils { namespace internal { +namespace { + +CString GenerateRandName(const TCHAR* name_prefix) { + CString guid; + if (FAILED(GetGuid(&guid))) { + return CString(); + } + + CString rand_name; + SafeCStringFormat(&rand_name, _T("%s%s"), name_prefix, guid); + return rand_name; +} + +} // namespace + V1ScheduledTasks::V1ScheduledTasks() { CORE_LOG(L1, (_T("[V1ScheduledTasks::V1ScheduledTasks]"))); } @@ -334,7 +352,10 @@ HRESULT V1ScheduledTasks::CreateScheduledTask(ITask* task, UTIL_LOG(L3, (_T("[CreateScheduledTask][%s][%s][%d]"), task_path, task_parameters, is_machine)); - HRESULT hr = task->SetApplicationName(task_path); + CString quoted_task_path(task_path); + EnclosePath("ed_task_path); + + HRESULT hr = task->SetApplicationName(quoted_task_path); if (FAILED(hr)) { UTIL_LOG(LE, (_T("[ITask.SetApplicationName failed][0x%x]"), hr)); return hr; @@ -515,7 +536,7 @@ HRESULT V1ScheduledTasks::UninstallScheduledTask(const CString& task_name) { } // Stop the task before deleting it. Ignore return value. - VERIFY1(SUCCEEDED(StopScheduledTask(task_name))); + VERIFY_SUCCEEDED(StopScheduledTask(task_name)); // delete the task. hr = scheduler->Delete(task_name); @@ -755,8 +776,12 @@ HRESULT V2ScheduledTasks::CreateScheduledTaskXml( principal_attributes = _T("InteractiveToken\n"); } + CString quoted_task_path(task_path); + EnclosePath("ed_task_path); + CString task_xml; - SafeCStringFormat(&task_xml, + SafeCStringFormat( + &task_xml, _T("\n") _T("\n") @@ -783,8 +808,12 @@ HRESULT V2ScheduledTasks::CreateScheduledTaskXml( _T(" \n") _T(" IgnoreNew\n") _T(" false\n") + _T(" false\n") _T(" true\n") _T(" false\n") + _T(" \n") + _T(" false\n") + _T(" \n") _T(" true\n") _T(" false\n") _T(" false\n") @@ -804,7 +833,7 @@ HRESULT V2ScheduledTasks::CreateScheduledTaskXml( hourly_trigger, user_id, principal_attributes, - task_path, + quoted_task_path, task_parameters); *scheduled_task_xml = task_xml; @@ -830,7 +859,7 @@ HRESULT V2ScheduledTasks::InstallScheduledTask(const CString& task_name, const CString start_time(plus_5min.Format(_T("%Y-%m-%dT%H:%M:%S"))); CString task_xml; - VERIFY1(SUCCEEDED(CreateScheduledTaskXml( + VERIFY_SUCCEEDED(CreateScheduledTaskXml( task_path, task_parameters, task_description, @@ -838,7 +867,7 @@ HRESULT V2ScheduledTasks::InstallScheduledTask(const CString& task_name, is_machine, create_logon_trigger, create_hourly_trigger, - &task_xml))); + &task_xml)); CComPtr registered_task; return task_folder->RegisterTask( @@ -1013,19 +1042,6 @@ CString GetCurrentTaskNameCore(bool is_machine) { default_name); } -HRESULT CreateAndSetVersionedTaskNameCoreInRegistry( - bool is_machine) { - UTIL_LOG(L3, (_T("[CreateAndSetVersionedTaskNameCoreInRegistry][%d]"), - is_machine)); - - CString default_name(GetDefaultGoopdateTaskName(is_machine, - COMMANDLINE_MODE_CORE)); - return goopdate_utils::CreateAndSetVersionedNameInRegistry( - is_machine, - default_name, - kRegValueTaskNameC); -} - CString GetCurrentTaskNameUA(bool is_machine) { UTIL_LOG(L3, (_T("[GetCurrentTaskNameUA][%d]"), is_machine)); @@ -1036,37 +1052,48 @@ CString GetCurrentTaskNameUA(bool is_machine) { default_name); } -HRESULT CreateAndSetVersionedTaskNameUAInRegistry(bool machine) { - UTIL_LOG(L3, (_T("[CreateAndSetVersionedTaskNameUAInRegistry][%d]"), - machine)); +CString CreateRandomTaskName(bool is_machine, CommandLineMode mode) { + UTIL_LOG(L3, (_T("[CreateRandomTaskName][%d][%d]"), is_machine, mode)); - CString default_name(GetDefaultGoopdateTaskName(machine, - COMMANDLINE_MODE_UA)); - return goopdate_utils::CreateAndSetVersionedNameInRegistry( - machine, - default_name, - kRegValueTaskNameUA); + CString prefix(GetDefaultGoopdateTaskName(is_machine, mode)); + CString name(GenerateRandName(prefix)); + UTIL_LOG(L3, (_T("[Random name][%s]"), name)); + + return name; +} + +HRESULT SetTaskNameInRegistry(bool is_machine, + CommandLineMode mode, + const CString& name) { + UTIL_LOG(L3, (_T("[SetTaskNameInRegistry][%d][%d][%s]"), + is_machine, mode, name)); + + const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE; + const TCHAR* key_value = mode == COMMANDLINE_MODE_CORE ? + kRegValueTaskNameC : + kRegValueTaskNameUA; + return RegKey::SetValue(key_name, key_value, name); } // Returns the task name Omaha used to install in Omaha 1.2.x. CString GetOmaha1LegacyTaskName(bool is_machine) { - const CString kLegacyOmaha1TaskNameMachine = _T("GoogleUpdateTask"); - const CString kLegacyOmaha1TaskNameUser = _T("GoogleUpdateTaskUser"); + const CString kLegacyOmaha1TaskNameMachine = MAIN_EXE_BASE_NAME _T("Task"); + const CString kLegacyOmaha1TaskNameUser = MAIN_EXE_BASE_NAME _T("TaskUser"); return is_machine ? kLegacyOmaha1TaskNameMachine : kLegacyOmaha1TaskNameUser; } // Returns the task name Omaha used to install in Omaha 2 before the // "GoogleUpdate.exe does not run all the time" refactoring. CString GetOmaha2LegacyTaskName(bool is_machine) { - const CString& kLegacyOmaha2TaskNameUserPrefix = _T("GoogleUpdateTaskUser"); - const CString& kLegacyOmaha2TaskNameMachine = _T("GoogleUpdateTaskMachine"); + const CString& kLegacyOmaha2TaskNameUserPrefix = MAIN_EXE_BASE_NAME _T("TaskUser"); + const CString& kLegacyOmaha2TaskNameMachine = MAIN_EXE_BASE_NAME _T("TaskMachine"); if (is_machine) { return kLegacyOmaha2TaskNameMachine; } CString task_name_user = kLegacyOmaha2TaskNameUserPrefix; CString user_sid; - VERIFY1(SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid))); + VERIFY_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid)); task_name_user += user_sid; return task_name_user; } @@ -1110,7 +1137,7 @@ CString GetDefaultGoopdateTaskName(bool is_machine, CommandLineMode mode) { } else { task_name = kScheduledTaskNameUserPrefix; CString user_sid; - VERIFY1(SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid))); + VERIFY_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid)); task_name += user_sid; } @@ -1138,7 +1165,10 @@ HRESULT InstallGoopdateTaskForMode(const CString& task_path, CString task_name(mode == COMMANDLINE_MODE_CORE ? internal::GetCurrentTaskNameCore(is_machine) : internal::GetCurrentTaskNameUA(is_machine)); - HRESULT hr = internal::Instance().InstallScheduledTask( + + if (internal::Instance().IsInstalledScheduledTask(task_name)) { + // Update the currently installed scheduled task. + HRESULT hr = internal::Instance().InstallScheduledTask( task_name, task_path, task_parameters, @@ -1147,32 +1177,35 @@ HRESULT InstallGoopdateTaskForMode(const CString& task_path, mode == COMMANDLINE_MODE_CORE && is_machine, mode == COMMANDLINE_MODE_UA); - - if (SUCCEEDED(hr)) { - return hr; + if (SUCCEEDED(hr)) { + return hr; + } } // Create a new task name and fall through to install that. - if (mode == COMMANDLINE_MODE_CORE) { - VERIFY1(SUCCEEDED( - internal::CreateAndSetVersionedTaskNameCoreInRegistry(is_machine))); - task_name = internal::GetCurrentTaskNameCore(is_machine); - } else { - VERIFY1(SUCCEEDED( - internal::CreateAndSetVersionedTaskNameUAInRegistry(is_machine))); - task_name = internal::GetCurrentTaskNameUA(is_machine); + task_name = internal::CreateRandomTaskName(is_machine, mode); + if (task_name.IsEmpty()) { + return E_UNEXPECTED; } + ASSERT1(!internal::Instance().IsInstalledScheduledTask(task_name)); - return internal::Instance().InstallScheduledTask( - task_name, - task_path, - task_parameters, - task_description, - is_machine, - mode == COMMANDLINE_MODE_CORE && - is_machine, - mode == COMMANDLINE_MODE_UA); + HRESULT hr = internal::Instance().InstallScheduledTask( + task_name, + task_path, + task_parameters, + task_description, + is_machine, + mode == COMMANDLINE_MODE_CORE && + is_machine, + mode == COMMANDLINE_MODE_UA); + if (SUCCEEDED(hr)) { + VERIFY_SUCCEEDED(internal::SetTaskNameInRegistry(is_machine, + mode, + task_name)); + } + + return hr; } HRESULT InstallGoopdateTasks(const CString& task_path, bool is_machine) { @@ -1187,32 +1220,32 @@ HRESULT InstallGoopdateTasks(const CString& task_path, bool is_machine) { } HRESULT UninstallGoopdateTasks(bool is_machine) { - VERIFY1(SUCCEEDED(internal::Instance().UninstallScheduledTask( - internal::GetCurrentTaskNameCore(is_machine)))); - VERIFY1(SUCCEEDED(internal::Instance().UninstallScheduledTask( - internal::GetCurrentTaskNameUA(is_machine)))); + VERIFY_SUCCEEDED(internal::Instance().UninstallScheduledTask( + internal::GetCurrentTaskNameCore(is_machine))); + VERIFY_SUCCEEDED(internal::Instance().UninstallScheduledTask( + internal::GetCurrentTaskNameUA(is_machine))); // Try to uninstall any tasks that we failed to update during a previous // overinstall. It is possible that we fail to uninstall these again here. - VERIFY1(SUCCEEDED(internal::Instance().UninstallScheduledTasks( + VERIFY_SUCCEEDED(internal::Instance().UninstallScheduledTasks( scheduled_task_utils::GetDefaultGoopdateTaskName( - is_machine, COMMANDLINE_MODE_CORE)))); - VERIFY1(SUCCEEDED(internal::Instance().UninstallScheduledTasks( + is_machine, COMMANDLINE_MODE_CORE))); + VERIFY_SUCCEEDED(internal::Instance().UninstallScheduledTasks( scheduled_task_utils::GetDefaultGoopdateTaskName( - is_machine, COMMANDLINE_MODE_UA)))); + is_machine, COMMANDLINE_MODE_UA))); return S_OK; } HRESULT UninstallLegacyGoopdateTasks(bool is_machine) { const CString& legacy_omaha1_task = internal::GetOmaha1LegacyTaskName(is_machine); - VERIFY1(SUCCEEDED( - internal::Instance().UninstallScheduledTask(legacy_omaha1_task))); + VERIFY_SUCCEEDED( + internal::Instance().UninstallScheduledTask(legacy_omaha1_task)); const CString& legacy_omaha2_task = internal::GetOmaha2LegacyTaskName(is_machine); - VERIFY1(SUCCEEDED( - internal::Instance().UninstallScheduledTask(legacy_omaha2_task))); + VERIFY_SUCCEEDED( + internal::Instance().UninstallScheduledTask(legacy_omaha2_task)); return S_OK; } @@ -1232,6 +1265,11 @@ bool IsDisabledGoopdateTaskUA(bool is_machine) { return internal::Instance().IsDisabledScheduledTask(task_name); } +bool HasGoopdateTaskEverRunUA(bool is_machine) { + const CString& task_name(internal::GetCurrentTaskNameUA(is_machine)); + return internal::Instance().HasScheduledTaskEverRun(task_name); +} + HRESULT GetExitCodeGoopdateTaskUA(bool is_machine) { const CString& task_name(internal::GetCurrentTaskNameUA(is_machine)); return internal::Instance().GetScheduledTaskExitCode(task_name); diff --git a/omaha/common/scheduled_task_utils.h b/omaha/common/scheduled_task_utils.h index 326f875e0..47a9b2546 100644 --- a/omaha/common/scheduled_task_utils.h +++ b/omaha/common/scheduled_task_utils.h @@ -41,6 +41,8 @@ HRESULT UninstallLegacyGoopdateTasks(bool is_machine); HRESULT StartGoopdateTaskCore(bool is_machine); bool IsInstalledGoopdateTaskUA(bool is_machine); bool IsDisabledGoopdateTaskUA(bool is_machine); +bool HasGoopdateTaskEverRunUA(bool is_machine); + HRESULT GetExitCodeGoopdateTaskUA(bool is_machine); bool IsUATaskHealthy(bool is_machine); diff --git a/omaha/common/scheduled_task_utils_internal.h b/omaha/common/scheduled_task_utils_internal.h index aa98801b1..fa0edc877 100644 --- a/omaha/common/scheduled_task_utils_internal.h +++ b/omaha/common/scheduled_task_utils_internal.h @@ -162,22 +162,21 @@ ScheduledTasksInterface& Instance(); // value if there is no registration. CString GetCurrentTaskNameCore(bool is_machine); -// Creates a unique name, say "GoogleUpdateTaskMachineCore1c9b3d6baf90df3", of -// the GoogleUpdateCore scheduled task, and stores it in the registry. -// Subsequent invocations of GetCurrentTaskNameCore() will return this new -// value. -HRESULT CreateAndSetVersionedTaskNameCoreInRegistry(bool machine); - // Gets the current name, say "GoogleUpdateTaskMachineUA", of the // GoogleUpdateUA scheduled task, either from the registry, or a default value // if there is no registration. CString GetCurrentTaskNameUA(bool is_machine); -// Creates a unique name, say "GoogleUpdateTaskMachineUA1c9b3d6baf90df3", of -// the GoogleUpdateUA scheduled task, and stores it in the registry. -// Subsequent invocations of GetCurrentTaskNameUA() will return this new -// value. -HRESULT CreateAndSetVersionedTaskNameUAInRegistry(bool machine); +// Creates a unique name, say "GoogleUpdateTaskMachineCore1c9b3d6baf90df3", or +// "GoogleUpdateTaskMachineUA1c9b3d6baf90df3", of the GoogleUpdateCore/UA +// scheduled task. +CString CreateRandomTaskName(bool is_machine, CommandLineMode mode); + +// Stores the name of the GoogleUpdateCore/UA scheduled task in the registry. +// Subsequent invocations of GetCurrentTaskNameCore/UA() will return this value. +HRESULT SetTaskNameInRegistry(bool is_machine, + CommandLineMode mode, + const CString& name); // Waits for the task to change its status to the specified status value and // returns the status of the task. If the status did not change within the diff --git a/omaha/common/scheduled_task_utils_unittest.cc b/omaha/common/scheduled_task_utils_unittest.cc index c8d05eb4b..ee6430cd2 100644 --- a/omaha/common/scheduled_task_utils_unittest.cc +++ b/omaha/common/scheduled_task_utils_unittest.cc @@ -39,7 +39,7 @@ namespace { const int kMaxWaitForProcessMs = 120000; const TCHAR kLongRunningProcessesRelativePath[] = - _T("unittest_support\\does_not_shutdown\\GoogleUpdate.exe"); + _T("unittest_support\\does_not_shutdown\\") MAIN_EXE_BASE_NAME _T(".exe"); CString GetLongRunningProcessPath() { const CString module_dir(app_util::GetCurrentModuleDirectory()); @@ -210,8 +210,12 @@ TEST_P(ScheduledTaskUtilsV2Test, CreateScheduledTaskXml) { principal_attributes = _T("InteractiveToken\n"); } + CString quoted_task_path(task_path); + EnclosePath("ed_task_path); + CString expected_task_xml; - SafeCStringFormat(&expected_task_xml, + SafeCStringFormat( + &expected_task_xml, _T("\n") _T("\n") @@ -238,8 +242,12 @@ TEST_P(ScheduledTaskUtilsV2Test, CreateScheduledTaskXml) { _T(" \n") _T(" IgnoreNew\n") _T(" false\n") + _T(" false\n") _T(" true\n") _T(" false\n") + _T(" \n") + _T(" false\n") + _T(" \n") _T(" true\n") _T(" false\n") _T(" false\n") @@ -259,7 +267,7 @@ TEST_P(ScheduledTaskUtilsV2Test, CreateScheduledTaskXml) { hourly_trigger, user_id, principal_attributes, - task_path, + quoted_task_path, kScheduledTaskParameters); CString task_xml; @@ -308,7 +316,6 @@ TEST_P(ScheduledTaskUtilsV2Test, InstallScheduledTask) { TEST(ScheduledTaskUtilsTest, GoopdateTasks) { - const CString task_name = GetCurrentTaskNameCore(IsUserAdmin()); const CString task_path = GetLongRunningProcessPath(); // Install/uninstall. @@ -316,6 +323,7 @@ TEST(ScheduledTaskUtilsTest, GoopdateTasks) { EXPECT_SUCCEEDED(UninstallGoopdateTasks(IsUserAdmin())); EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin())); + const CString task_name = GetCurrentTaskNameCore(IsUserAdmin()); EXPECT_FALSE(Instance().HasScheduledTaskEverRun(task_name)); // Start and stop. @@ -377,12 +385,12 @@ TEST(ScheduledTaskUtilsTest, V1OnlyGoopdateTaskInUseOverinstall) { } TEST(ScheduledTaskUtilsTest, GetExitCodeGoopdateTaskUA) { - const CString task_name = GetCurrentTaskNameUA(IsUserAdmin()); const CString task_path = ConcatenatePath( app_util::GetCurrentModuleDirectory(), _T("unittest_support\\SaveArguments.exe")); EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin())); + const CString task_name = GetCurrentTaskNameUA(IsUserAdmin()); EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN, GetExitCodeGoopdateTaskUA(IsUserAdmin())); EXPECT_FALSE(Instance().HasScheduledTaskEverRun(task_name)); diff --git a/omaha/common/stats_uploader.cc b/omaha/common/stats_uploader.cc index 0c44a0cd7..9b121fd5f 100644 --- a/omaha/common/stats_uploader.cc +++ b/omaha/common/stats_uploader.cc @@ -106,6 +106,17 @@ HRESULT UploadMetrics(bool is_machine, return GOOPDATE_E_CANNOT_USE_NETWORK; } +#if defined(GOOGLE_UPDATE_BUILD) + // The usagestats collection, aggregation, and reporting features are + // deprecated and may be removed from the code base in the future. + // Omaha uses the completion pings for telemetry and quality of service. + // There is no need to collect and report a different kind of usagestats. + UNREFERENCED_PARAMETER(is_machine); + UNREFERENCED_PARAMETER(extra_url_data); + UNREFERENCED_PARAMETER(content); + OPT_LOG(L3, (_T("[Stats not uploaded because the feature is deprecated.]"))); + return S_FALSE; +#else const TCHAR* version = GetVersionString(); CString test_source(ConfigManager::Instance()->GetTestSource()); @@ -139,6 +150,7 @@ HRESULT UploadMetrics(bool is_machine, std::vector response_buffer; return network_request.PostString(url, content, &response_buffer); +#endif // GOOGLE_UPDATE_BUILD } HRESULT ReportMetrics(bool is_machine, @@ -223,7 +235,7 @@ HRESULT DoAggregateAndReportMetrics(bool is_machine, bool force_report) { return hr; } - VERIFY1(SUCCEEDED(ResetPersistentMetrics(&key))); + VERIFY_SUCCEEDED(ResetPersistentMetrics(&key)); CORE_LOG(L3, (_T("[Stats upload successful]"))); return S_OK; } diff --git a/omaha/common/stats_uploader_unittest.cc b/omaha/common/stats_uploader_unittest.cc index a9b8059d0..4c1a7573e 100644 --- a/omaha/common/stats_uploader_unittest.cc +++ b/omaha/common/stats_uploader_unittest.cc @@ -125,7 +125,7 @@ class StatsUploaderTest : public testing::Test { }; const TCHAR StatsUploaderTest::key_name_[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\"); const TCHAR StatsUploaderTest::metric_name_[] = _T("test_bool"); @@ -204,10 +204,10 @@ TEST_F(StatsUploaderTest, AggregateAndReportMetrics) { TEST_F(StatsUploaderTest, ResetPersistentMetricsTest) { const TCHAR* keys[] = { - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Timings"), // NOLINT - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Counts"), // NOLINT - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Integers"), // NOLINT - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Booleans"), // NOLINT + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Timings"), // NOLINT + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Counts"), // NOLINT + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Integers"), // NOLINT + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Booleans"), // NOLINT }; EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKeys(keys, arraysize(keys))); EXPECT_HRESULT_SUCCEEDED(ResetMetrics(false)); // User. diff --git a/omaha/common/update_request.cc b/omaha/common/update_request.cc index ad5be2881..11183df35 100644 --- a/omaha/common/update_request.cc +++ b/omaha/common/update_request.cc @@ -69,9 +69,9 @@ UpdateRequest* UpdateRequest::Create(bool is_machine, request.check_period_sec = check_period_sec; } - request.dlpref = cm->GetDownloadPreferenceGroupPolicy(); + request.dlpref = cm->GetDownloadPreferenceGroupPolicy(NULL); - request.domain_joined = IsEnrolledToDomain(); + request.domain_joined = IsEnterpriseManaged(); // Hardware platform attributes. // @@ -104,10 +104,9 @@ UpdateRequest* UpdateRequest::Create(bool is_machine, // Software platform attributes. request.os.platform = kPlatformWin; - VERIFY1(SUCCEEDED(goopdate_utils::GetOSInfo(&request.os.version, - &request.os.service_pack))); - request.os.arch = xml::ConvertProcessorArchitectureToString( - SystemInfo::GetProcessorArchitecture()); + VERIFY_SUCCEEDED(goopdate_utils::GetOSInfo(&request.os.version, + &request.os.service_pack)); + request.os.arch = SystemInfo::GetArchitecture(); return update_request.release(); } @@ -117,7 +116,7 @@ UpdateRequest* UpdateRequest::Create(bool is_machine, const CString& install_source, const CString& origin_url) { CString request_id; - VERIFY1(SUCCEEDED(GetGuid(&request_id))); + VERIFY_SUCCEEDED(GetGuid(&request_id)); return Create(is_machine, session_id, install_source, origin_url, request_id); } diff --git a/omaha/common/update_request_unittest.cc b/omaha/common/update_request_unittest.cc index 3982e752d..38f7e1685 100644 --- a/omaha/common/update_request_unittest.cc +++ b/omaha/common/update_request_unittest.cc @@ -31,6 +31,15 @@ class UpdateRequestTest : public ::testing::TestWithParam { bool IsDomain() { return GetParam(); } + + void SetUp() override { + ClearGroupPolicies(); + } + + void TearDown() override { + ClearGroupPolicies(); + } + }; INSTANTIATE_TEST_CASE_P(IsDomain, UpdateRequestTest, ::testing::Bool()); @@ -84,23 +93,19 @@ TEST_P(UpdateRequestTest, DlPref) { UpdateRequest::Create(false, _T("unittest"), _T("unittest"), CString())); EXPECT_STREQ(_T(""), update_request->request().dlpref); - EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueDownloadPreference, - _T("unknown"))); + EXPECT_SUCCEEDED(SetPolicyString(kRegValueDownloadPreference, _T("unknown"))); update_request.reset( UpdateRequest::Create(false, _T("unittest"), _T("unittest"), CString())); EXPECT_STREQ(_T(""), update_request->request().dlpref); - EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueDownloadPreference, - kDownloadPreferenceCacheable)); + EXPECT_SUCCEEDED(SetPolicyString(kRegValueDownloadPreference, + kDownloadPreferenceCacheable)); update_request.reset( UpdateRequest::Create(false, _T("unittest"), _T("unittest"), CString())); EXPECT_STREQ(IsDomain() ? kDownloadPreferenceCacheable : _T(""), update_request->request().dlpref); RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain); - RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); } } // namespace xml diff --git a/omaha/common/url_utils.cc b/omaha/common/url_utils.cc index f535e118a..827e8fd3a 100644 --- a/omaha/common/url_utils.cc +++ b/omaha/common/url_utils.cc @@ -25,9 +25,8 @@ HRESULT BuildQueryString(const std::vector& query_params, CString query_part; CString encoded_value; - for (std::vector::const_iterator scan = query_params.begin(); - scan != query_params.end(); ++scan) { - HRESULT hr = StringEscape(scan->second, false, &encoded_value); + for (auto const&[key, value] : query_params) { + HRESULT hr = StringEscape(value, false, &encoded_value); if (FAILED(hr)) { return hr; } @@ -35,7 +34,7 @@ HRESULT BuildQueryString(const std::vector& query_params, if (!query_part.IsEmpty()) { query_part.AppendChar(_T('&')); } - query_part.Append(scan->first); + query_part.Append(key); query_part.AppendChar(_T('=')); query_part.Append(encoded_value); } diff --git a/omaha/common/url_utils.h b/omaha/common/url_utils.h index 511cfa8d9..d1306b8fa 100644 --- a/omaha/common/url_utils.h +++ b/omaha/common/url_utils.h @@ -18,7 +18,7 @@ namespace omaha { -typedef std::pair QueryElement; +using QueryElement = std::pair; // Assembles the parameters in |query_params| into |query|, escaping the values // as appropriate. Returns S_OK on success, or a failure HRESULT otherwise. diff --git a/omaha/common/url_utils_unittest.cc b/omaha/common/url_utils_unittest.cc index a5887bcfb..b899cb1bb 100644 --- a/omaha/common/url_utils_unittest.cc +++ b/omaha/common/url_utils_unittest.cc @@ -24,11 +24,20 @@ namespace omaha { TEST(BuildQueryStringTest, Do) { std::vector params; - params.push_back(std::make_pair(_T("one"), _T("1"))); - params.push_back(std::make_pair(_T("2"), _T("two"))); + params.push_back(std::make_pair(L"one", L"1")); + params.push_back(std::make_pair(L"2", L"two")); CString query; EXPECT_HRESULT_SUCCEEDED(BuildQueryString(params, &query)); - EXPECT_STREQ(query, _T("one=1&2=two")); + EXPECT_STREQ(query, L"one=1&2=two"); +} + +TEST(BuildQueryStringTest, EncodesQueryElements) { + std::vector params; + params.push_back(std::make_pair(L"first", L" '`")); + params.push_back(std::make_pair(L"second", L"&=")); + CString query; + EXPECT_HRESULT_SUCCEEDED(BuildQueryString(params, &query)); + EXPECT_STREQ(query, L"first=%20'%60&second=%26="); } } // namespace omaha diff --git a/omaha/common/web_services_client_unittest.cc b/omaha/common/web_services_client_unittest.cc index d188b312f..150a58f5e 100644 --- a/omaha/common/web_services_client_unittest.cc +++ b/omaha/common/web_services_client_unittest.cc @@ -54,7 +54,7 @@ class WebServicesClientTest : public testing::Test, web_service_client_.reset(); } - NetworkRequest* network_request() const { + NetworkRequest* GetNetworkRequest() const { return web_service_client_->network_request_.get(); } @@ -93,7 +93,7 @@ TEST_F(WebServicesClientTest, Send) { xml::response::Response response(update_response_->response()); EXPECT_STREQ(_T("3.0"), response.protocol); - NetworkRequest* network_request(network_request()); + NetworkRequest* network_request = GetNetworkRequest(); CString cookie; EXPECT_HRESULT_FAILED(network_request->QueryHeadersString( @@ -126,7 +126,7 @@ TEST_P(WebServicesClientTest, SendUsingCup) { xml::response::Response response(update_response_->response()); EXPECT_STREQ(_T("3.0"), response.protocol); - NetworkRequest* network_request(network_request()); + NetworkRequest* network_request = GetNetworkRequest(); CString no_request_age_header; network_request->QueryHeadersString( @@ -163,26 +163,6 @@ TEST_P(WebServicesClientTest, SendUsingCup) { GetVersionString()); EXPECT_STREQ(expected_updater_header, updater_header); - // A CUP transaction has either a request or a response CUP cookie and - // the ETag response header. - CString request_cookie; - network_request->QueryHeadersString( - WINHTTP_QUERY_COOKIE | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, - WINHTTP_HEADER_NAME_BY_INDEX, - &request_cookie); - const bool has_cup_request_cookie = request_cookie.Find(_T("c=")) != -1; - - CString response_cookie; - network_request->QueryHeadersString(WINHTTP_QUERY_SET_COOKIE, - WINHTTP_HEADER_NAME_BY_INDEX, - &response_cookie); - const bool has_cup_response_cookie = response_cookie.Find(_T("c=")) != -1; - - CString etag; - EXPECT_HRESULT_SUCCEEDED(network_request->QueryHeadersString( - WINHTTP_QUERY_ETAG, WINHTTP_HEADER_NAME_BY_INDEX, &etag)); - EXPECT_FALSE(etag.IsEmpty()); - // Check the custom headers after the response has been received. EXPECT_LT(0, web_service_client_->http_xdaystart_header_value()); EXPECT_LT(0, web_service_client_->http_xdaynum_header_value()); @@ -223,7 +203,7 @@ TEST_F(WebServicesClientTest, SendForcingHttps) { update_response_.get())); EXPECT_TRUE(web_service_client_->is_http_success()); - NetworkRequest* network_request(network_request()); + NetworkRequest* network_request = GetNetworkRequest(); CString app_ids_header; network_request->QueryHeadersString( @@ -235,7 +215,7 @@ TEST_F(WebServicesClientTest, SendForcingHttps) { _T("{E608D3AC-AA44-4754-A391-DA830AE78EA4}"), app_ids_header); - // Do a couple of sanity checks on the parsing of the response. + // Do a couple of checks on the parsing of the response. xml::response::Response response(update_response_->response()); EXPECT_STREQ(_T("3.0"), response.protocol); ASSERT_EQ(2, response.apps.size()); @@ -259,7 +239,7 @@ TEST_F(WebServicesClientTest, SendWithCustomHeader) { xml::response::Response response(update_response_->response()); EXPECT_STREQ(_T("3.0"), response.protocol); - NetworkRequest* network_request(network_request()); + NetworkRequest* network_request = GetNetworkRequest(); CString request_age_header; network_request->QueryHeadersString( @@ -285,7 +265,7 @@ TEST_P(WebServicesClientTest, SendString) { response.get())); EXPECT_TRUE(web_service_client_->is_http_success()); - NetworkRequest* network_request(network_request()); + NetworkRequest* network_request = GetNetworkRequest(); CString interactive_header; network_request->QueryHeadersString( @@ -324,7 +304,7 @@ TEST_F(WebServicesClientTest, SendStringWithCustomHeader) { response.get())); EXPECT_TRUE(web_service_client_->is_http_success()); - NetworkRequest* network_request(network_request()); + NetworkRequest* network_request = GetNetworkRequest(); CString foobar_header; network_request->QueryHeadersString( diff --git a/omaha/common/webplugin_utils.cc b/omaha/common/webplugin_utils.cc deleted file mode 100644 index d694016d0..000000000 --- a/omaha/common/webplugin_utils.cc +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2008-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/common/webplugin_utils.h" - -#include "omaha/base/app_util.h" -#include "omaha/base/debug.h" -#include "omaha/base/error.h" -#include "omaha/base/file.h" -#include "omaha/base/logging.h" -#include "omaha/base/path.h" -#include "omaha/base/safe_format.h" -#include "omaha/base/string.h" -#include "omaha/base/system.h" -#include "omaha/base/utils.h" -#include "omaha/base/xml_utils.h" -#include "omaha/common/command_line_builder.h" -#include "omaha/common/const_cmd_line.h" -#include "omaha/common/const_goopdate.h" -#include "omaha/common/goopdate_utils.h" -#include "omaha/common/lang.h" -#include "omaha/net/network_request.h" -#include "omaha/net/simple_request.h" - -namespace omaha { - -namespace webplugin_utils { - -HRESULT SanitizeExtraArgs(const CString& extra_args_in, - CString* extra_args_out) { - ASSERT1(extra_args_out); - - HRESULT hr = StringEscape(extra_args_in, true, extra_args_out); - if (FAILED(hr)) { - return hr; - } - - // Now we unescape a selective white-list of characters. - extra_args_out->Replace(_T("%3D"), _T("=")); - extra_args_out->Replace(_T("%26"), _T("&")); - extra_args_out->Replace(_T("%7B"), _T("{")); - extra_args_out->Replace(_T("%7D"), _T("}")); - extra_args_out->Replace(_T("%25"), _T("%")); - - return hr; -} - -HRESULT BuildWebPluginCommandLine(const CString& url_domain, - const CString& extra_args, - CString* final_cmd_line_args) { - ASSERT1(!extra_args.IsEmpty()); - ASSERT1(final_cmd_line_args); - - CORE_LOG(L2, (_T("[BuildWebPluginCommandLine][%s][%s]"), - url_domain, extra_args)); - - CString extra_args_sanitized; - HRESULT hr = webplugin_utils::SanitizeExtraArgs(extra_args, - &extra_args_sanitized); - if (FAILED(hr)) { - return hr; - } - - CommandLineBuilder install_builder(COMMANDLINE_MODE_INSTALL); - install_builder.set_extra_args(extra_args_sanitized); - CString cmd_line_args(install_builder.GetCommandLineArgs()); - - CString url_domain_encoded; - CString cmd_line_args_encoded; - hr = StringEscape(url_domain, true, &url_domain_encoded); - if (FAILED(hr)) { - return hr; - } - - hr = StringEscape(cmd_line_args, true, &cmd_line_args_encoded); - if (FAILED(hr)) { - return hr; - } - - CommandLineBuilder webplugin_builder(COMMANDLINE_MODE_WEBPLUGIN); - webplugin_builder.set_webplugin_url_domain(url_domain_encoded); - webplugin_builder.set_webplugin_args(cmd_line_args_encoded); - webplugin_builder.set_install_source(kCmdLineInstallSource_OneClick); - CString webplugin_cmd_line(webplugin_builder.GetCommandLineArgs()); - - CString cmd_line_web_plugin; - SafeCStringFormat(&cmd_line_web_plugin, _T("/%s"), kCmdLineWebPlugin); - - if (!String_StartsWith(webplugin_cmd_line, cmd_line_web_plugin, false)) { - return E_UNEXPECTED; - } - - *final_cmd_line_args = - webplugin_cmd_line.Mid(cmd_line_web_plugin.GetLength() + 1); - - CORE_LOG(L2, (_T("[BuildWebPluginCommandLine][%s]"), *final_cmd_line_args)); - return S_OK; -} - -HRESULT IsLanguageSupported(const CString& webplugin_args) { - CString cmd_line; - SafeCStringFormat(&cmd_line, _T("gu.exe %s"), webplugin_args); - CommandLineArgs parsed_args; - HRESULT hr = ParseCommandLine(cmd_line, &parsed_args); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[ParseCommandLine failed][0x%08x]"), hr)); - return hr; - } - - - if (!lang::IsLanguageSupported(parsed_args.extra.language)) { - CORE_LOG(LE, (_T("Language not supported][%s]"), - parsed_args.extra.language)); - return GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED; - } - - return S_OK; -} - -HRESULT BuildOneClickWorkerArgs(const CommandLineArgs& args, - CString* oneclick_args) { - ASSERT1(oneclick_args); - - // Since this is being called via WebPlugin only, we can rebuild the - // command line arguments from the valid params we can send on. - // For example, the web plugin will not send crash_cmd or debug_cmd - // or reg_server or unreg_server so we don't have to worry about those here. - CString cmd_line_args; - CommandLineArgs webplugin_cmdline_args; - - // ParseCommandLine assumes the first argument is the program being run. - // Don't want to enforce that constraint on our callers, so we prepend with a - // fake exe name. - CString args_to_parse; - SafeCStringFormat(&args_to_parse, _T("%s %s"), - kOmahaShellFileName, - args.webplugin_args); - - // Parse the arguments we received as the second parameter to /webplugin. - HRESULT hr = ParseCommandLine(args_to_parse, &webplugin_cmdline_args); - if (FAILED(hr)) { - return hr; - } - - // Silent and other non-standard installs could be malicious. Prevent them. - if (webplugin_cmdline_args.mode != COMMANDLINE_MODE_INSTALL) { - return E_INVALIDARG; - } - if (webplugin_cmdline_args.is_silent_set || - webplugin_cmdline_args.is_eula_required_set) { - return E_INVALIDARG; - } - - CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL); - builder.set_extra_args(webplugin_cmdline_args.extra_args_str); - - // We expect this value from the plugin. - ASSERT1(!args.install_source.IsEmpty()); - if (args.install_source.IsEmpty()) { - return E_INVALIDARG; - } - - builder.set_install_source(args.install_source); - - *oneclick_args = builder.GetCommandLineArgs(); - - return S_OK; -} - -// It is important that current_goopdate_path be the version path and not the -// Update\ path. -HRESULT CopyGoopdateToTempDir(const CPath& current_goopdate_path, - CPath* goopdate_temp_path) { - ASSERT1(goopdate_temp_path); - - // Create a unique directory in the user's temp directory. - GUID guid = GUID_NULL; - HRESULT hr = ::CoCreateGuid(&guid); - if (FAILED(hr)) { - return hr; - } - CString guid_str = GuidToString(guid); - ASSERT1(!guid_str.IsEmpty()); - - CString temp_dir = app_util::GetTempDir(); - ASSERT1(!temp_dir.IsEmpty()); - - CPath temp_path = temp_dir.GetString(); - temp_path.Append(guid_str); - temp_path.Canonicalize(); - - hr = CreateDir(temp_path, NULL); - if (FAILED(hr)) { - return hr; - } - - hr = File::CopyTree(current_goopdate_path, temp_path, true); - if (FAILED(hr)) { - return hr; - } - - CORE_LOG(L2, (_T("[CopyGoopdateToTempDir][temp_path = %s]"), temp_path)); - *goopdate_temp_path = temp_path; - return S_OK; -} - -HRESULT DoOneClickInstall(const CommandLineArgs& args) { - CString cmd_line_args; - HRESULT hr = BuildOneClickWorkerArgs(args, &cmd_line_args); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[BuildOneClickWorkerArgs failed][0x%08x]"), hr)); - return hr; - } - - CORE_LOG(L2, (_T("[DoOneClickInstall][cmd_line_args: %s]"), cmd_line_args)); - - // Check if we're running from the machine dir. - // If we're not, we must be running from user directory since OneClick only - // works against installed versions of Omaha. - CPath current_goopdate_path(app_util::GetCurrentModuleDirectory()); - CPath goopdate_temp_path; - hr = CopyGoopdateToTempDir(current_goopdate_path, &goopdate_temp_path); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[CopyGoopdateToTempDir failed][0x%08x]"), hr)); - return hr; - } - - CPath goopdate_temp_exe_path = goopdate_temp_path; - goopdate_temp_exe_path.Append(kOmahaShellFileName); - - // Launch goopdate again with the updated command line arguments. - hr = System::ShellExecuteProcess(goopdate_temp_exe_path, - cmd_line_args, - NULL, - NULL); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[ShellExecuteProcess failed][%s][0x%08x]"), - goopdate_temp_exe_path, hr)); - return hr; - } - - return S_OK; -} - -} // namespace webplugin_utils - -} // namespace omaha diff --git a/omaha/common/webplugin_utils.h b/omaha/common/webplugin_utils.h deleted file mode 100644 index 0281b80e7..000000000 --- a/omaha/common/webplugin_utils.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2008-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_COMMON_WEBPLUGIN_UTILS_H__ -#define OMAHA_COMMON_WEBPLUGIN_UTILS_H__ - -#include -#include -#include -#include "base/basictypes.h" -#include "omaha/common/command_line.h" -#include "omaha/common/config_manager.h" - -namespace omaha { - -namespace webplugin_utils { - -// This function escapes all unsafe characters, and then unescapes a selective -// white-list of characters: '=', '&', '{', '}', '%'. For a properly formatted -// extra args string, extra_args_out will be exactly equal to extra_args_in. -HRESULT SanitizeExtraArgs(const CString& extra_args_in, - CString* extra_args_out); - -// This function builds a sanitized /pi command line. -HRESULT BuildWebPluginCommandLine(const CString& url_domain, - const CString& extra_args, - CString* final_cmd_line_args); - -// Parses the arguments, extracts the language parameter, and verifies that we -// support the requested language. -HRESULT IsLanguageSupported(const CString& webplugin_args); - -// Copies required Goopdate files to a temp location before installing. -HRESULT CopyGoopdateToTempDir(const CPath& current_goopdate_path, - CPath* goopdate_temp_path); - -// Launches google_update.exe based on parameters sent with /webplugin. -HRESULT DoOneClickInstall(const CommandLineArgs& args); - -// Creates request string for the webplugin URL check webservice call. -HRESULT BuildOneClickRequestString(const CommandLineArgs& args, - CString* request_str); - -// Builds up the command line arguments to re-launch google_update.exe -// when called with /pi. -HRESULT BuildOneClickWorkerArgs(const CommandLineArgs& args, CString* args_out); - -} // namespace webplugin_utils - -} // namespace omaha - -#endif // OMAHA_COMMON_WEBPLUGIN_UTILS_H__ diff --git a/omaha/common/webplugin_utils_unittest.cc b/omaha/common/webplugin_utils_unittest.cc deleted file mode 100644 index 3737aa711..000000000 --- a/omaha/common/webplugin_utils_unittest.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2008-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/base/app_util.h" -#include "omaha/base/error.h" -#include "omaha/base/file.h" -#include "omaha/base/path.h" -#include "omaha/base/utils.h" -#include "omaha/common/webplugin_utils.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -namespace webplugin_utils { - -#define YOUTUBEUPLOADEREN_TAG \ - _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \ - _T("appname=YouTubeUploader&needsadmin=False&lang=en\"") - - -TEST(WebPluginUtilsTest, SanitizeExtraArgs_Valid) { - CString extra_args = _T("appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google%20Chrome&needsadmin=true&lang=en"); - - CString escaped_extra_args; - EXPECT_SUCCEEDED(SanitizeExtraArgs(extra_args, &escaped_extra_args)); - EXPECT_STREQ(extra_args, escaped_extra_args); -} - -TEST(WebPluginUtilsTest, SanitizeExtraArgs_Invalid) { - CString extra_args = _T("\"appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google Chrome&needsadmin=true&lang=en\"") - _T(" /offline 1"); - - CString escaped_extra_args; - EXPECT_SUCCEEDED(SanitizeExtraArgs(extra_args, &escaped_extra_args)); - EXPECT_STREQ(_T("%22appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}&") - _T("appname=Google%20Chrome&needsadmin=true&lang=en%22") - _T("%20%2Foffline%201"), - escaped_extra_args); -} - -TEST(WebPluginUtilsTest, BuildWebPluginCommandLine_Valid) { - CString url_domain(_T("http://www.google.com/")); - CString extra_args = _T("appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google%20Chrome&needsadmin=true&lang=en"); - - CString final_cmd_line_args; - EXPECT_SUCCEEDED(BuildWebPluginCommandLine(url_domain, - extra_args, - &final_cmd_line_args)); - EXPECT_STREQ( - _T("\"http:%2F%2Fwww.google.com%2F\" \"%2Finstall%20%22appguid=") - _T("%7B8A69D345-D564-463c-AFF1-A69D9E530F96%7D%26appname=") - _T("Google%2520Chrome%26needsadmin=true%26lang=") - _T("en%22\" /installsource oneclick"), - final_cmd_line_args); -} - -TEST(WebPluginUtilsTest, BuildWebPluginCommandLine_Invalid) { - CString url_domain(_T("http://www.google.com/")); - CString extra_args = _T("\"appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google Chrome&needsadmin=true&lang=en\"") - _T(" /offline 1"); - - ExpectAsserts expect_asserts; - CString final_cmd_line_args; - EXPECT_SUCCEEDED(BuildWebPluginCommandLine(url_domain, - extra_args, - &final_cmd_line_args)); - EXPECT_STREQ( - _T("\"http:%2F%2Fwww.google.com%2F\" \"%2Finstall%20%22%2522appguid=") - _T("%7B8A69D345-D564-463c-AFF1-A69D9E530F96%7D%26appname=") - _T("Google%2520Chrome%26needsadmin=true%26lang=") - _T("en%2522%2520%252Foffline%25201%22\" /installsource oneclick"), - final_cmd_line_args); -} - -TEST(WebPluginUtilsTest, BuildOneClickWorkerArgs_Valid) { - CommandLineArgs args; - CString oneclick_args; - - args.install_source = _T("oneclick"); - args.webplugin_args = _T("/install \"appguid=") - _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google Chrome&needsadmin=true&lang=en\""); - EXPECT_EQ(S_OK, BuildOneClickWorkerArgs(args, &oneclick_args)); - - EXPECT_STREQ(_T("/install ") - _T("\"appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google Chrome&needsadmin=true&lang=en\" ") - _T("/installsource oneclick"), - oneclick_args); -} - -// This tests valid command line args that are not valid to be sent through -// to google_update.exe (e.g. /install). -TEST(WebPluginUtilsTest, BuildOneClickWorkerArgs_Invalid) { - CommandLineArgs args; - CString oneclick_args; - - args.install_source = _T("oneclick"); - - args.webplugin_args = _T("/handoff ") YOUTUBEUPLOADEREN_TAG; - EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args)); - - args.webplugin_args = _T("/regserver"); - EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args)); - - args.webplugin_args = _T("/unregserver"); - EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args)); - - args.webplugin_args = _T("/install ") YOUTUBEUPLOADEREN_TAG _T(" /silent"); - EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args)); -} - -TEST(WebPluginUtilsTest, CopyGoopdateToTempDir) { - CPath current_goopdate_path(app_util::GetCurrentModuleDirectory()); - current_goopdate_path.Append(_T("unittest_support\\omaha_1.3.x\\")); - CPath goopdate_temp_path; - ASSERT_SUCCEEDED(CopyGoopdateToTempDir(current_goopdate_path, - &goopdate_temp_path)); - - std::vector files; - EXPECT_HRESULT_SUCCEEDED(FindFilesEx(goopdate_temp_path, _T("*.*"), &files)); - - EXPECT_EQ(3, files.size()); - - std::map files_map; - for (size_t file_index = 0; file_index < files.size(); ++file_index) { - files_map[files[file_index]] = 1; - } - - EXPECT_TRUE(files_map.find(_T("GoogleUpdate.exe")) != files_map.end()); - EXPECT_TRUE(files_map.find(_T("goopdate.dll")) != files_map.end()); - EXPECT_TRUE(files_map.find(_T("goopdateres_en.dll")) != files_map.end()); - - EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(goopdate_temp_path)); -} - -TEST(WebPluginUtilsTest, IsLanguageSupported_InvalidArgs) { - CString args = _T("/en"); - EXPECT_FAILED(IsLanguageSupported(args)); -} - -TEST(WebPluginUtilsTest, IsLanguageSupported_LangOK) { - CString args = _T("/install \"appguid=") - _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google Chrome&needsadmin=true&lang=en\""); - EXPECT_SUCCEEDED(IsLanguageSupported(args)); -} - -TEST(WebPluginUtilsTest, IsLanguageSupported_LangNotFound) { - CString args = _T("/install \"appguid=") - _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}") - _T("&appname=Google Chrome&needsadmin=true&lang=zz\""); - - EXPECT_EQ(GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED, - IsLanguageSupported(args)); -} - -} // namespace webplugin_utils - -} // namespace omaha - diff --git a/omaha/common/xml_const.cc b/omaha/common/xml_const.cc index 6fee88537..799331e15 100644 --- a/omaha/common/xml_const.cc +++ b/omaha/common/xml_const.cc @@ -132,6 +132,7 @@ const TCHAR* const kStateCancelled = _T("state_cancelled"); const TCHAR* const kStatus = _T("status"); const TCHAR* const kSuccessAction = _T("onsuccess"); const TCHAR* const kSuccessUrl = _T("successurl"); +const TCHAR* const kTargetChannel = _T("release_channel"); const TCHAR* const kTargetVersionPrefix = _T("targetversionprefix"); const TCHAR* const kTestSource = _T("testsource"); const TCHAR* const kTerminateAllBrowsers = _T("terminateallbrowsers"); @@ -155,9 +156,6 @@ const TCHAR* const kAppDefinedPrefix = _T("_"); namespace value { -const TCHAR* const kArchAmd64 = _T("x64"); -const TCHAR* const kArchIntel = _T("x86"); -const TCHAR* const kArchUnknown = _T("unknown"); const TCHAR* const kBits = _T("bits"); const TCHAR* const kCacheable = _T("cacheable"); const TCHAR* const kClientRegulated = _T("cr"); diff --git a/omaha/common/xml_const.h b/omaha/common/xml_const.h index 99988f0e6..d83748b2f 100644 --- a/omaha/common/xml_const.h +++ b/omaha/common/xml_const.h @@ -132,6 +132,7 @@ extern const TCHAR* const kStateCancelled; extern const TCHAR* const kStatus; extern const TCHAR* const kSuccessAction; extern const TCHAR* const kSuccessUrl; +extern const TCHAR* const kTargetChannel; extern const TCHAR* const kTargetVersionPrefix; extern const TCHAR* const kTestSource; extern const TCHAR* const kTerminateAllBrowsers; @@ -155,9 +156,6 @@ extern const TCHAR* const kAppDefinedPrefix; namespace value { -extern const TCHAR* const kArchAmd64; -extern const TCHAR* const kArchIntel; -extern const TCHAR* const kArchUnknown; extern const TCHAR* const kBits; extern const TCHAR* const kCacheable; extern const TCHAR* const kClientRegulated; diff --git a/omaha/common/xml_parser.cc b/omaha/common/xml_parser.cc index 2579f17d8..8df917535 100644 --- a/omaha/common/xml_parser.cc +++ b/omaha/common/xml_parser.cc @@ -146,20 +146,6 @@ HRESULT VerifyProtocolCompatibility(const CString& actual_version, } // namespace -CString ConvertProcessorArchitectureToString(DWORD arch) { - switch (arch) { - case PROCESSOR_ARCHITECTURE_INTEL: - return xml::value::kArchIntel; - - case PROCESSOR_ARCHITECTURE_AMD64: - return xml::value::kArchAmd64; - - default: - ASSERT1(false); - return xml::value::kArchUnknown; - } -} - // The ElementHandler classes should also be in an anonymous namespace but // the base class cannot be because it is used in the header file. @@ -1464,18 +1450,17 @@ HRESULT XmlParser::BuildUpdateCheckElement(const request::App& app, } } - if (!app.update_check.target_version_prefix.IsEmpty()) { - // RollbackToTargetVersion only applies if the TargetVersionPrefix is set. - if (app.update_check.is_rollback_allowed) { - hr = AddXMLAttributeNode(element, - kXmlNamespace, - xml::attribute::kRollbackAllowed, - xml::value::kTrue); - if (FAILED(hr)) { - return hr; - } + if (app.update_check.is_rollback_allowed) { + hr = AddXMLAttributeNode(element, + kXmlNamespace, + xml::attribute::kRollbackAllowed, + xml::value::kTrue); + if (FAILED(hr)) { + return hr; } + } + if (!app.update_check.target_version_prefix.IsEmpty()) { hr = AddXMLAttributeNode(element, kXmlNamespace, xml::attribute::kTargetVersionPrefix, @@ -1485,6 +1470,16 @@ HRESULT XmlParser::BuildUpdateCheckElement(const request::App& app, } } + if (!app.update_check.target_channel.IsEmpty()) { + hr = AddXMLAttributeNode(element, + kXmlNamespace, + xml::attribute::kTargetChannel, + app.update_check.target_channel); + if (FAILED(hr)) { + return hr; + } + } + hr = parent_node->appendChild(element, NULL); if (FAILED(hr)) { return hr; diff --git a/omaha/common/xml_parser.h b/omaha/common/xml_parser.h index 0cf08db9b..51465d21b 100644 --- a/omaha/common/xml_parser.h +++ b/omaha/common/xml_parser.h @@ -38,8 +38,6 @@ namespace xml { class ElementHandler; -CString ConvertProcessorArchitectureToString(DWORD processor_architecture); - // Public static methods instantiate a temporary instance of this class, which // then parses the specified document. This avoids reusing instances of the // parser and dealing with stale and dirty data. diff --git a/omaha/common/xml_parser_unittest.cc b/omaha/common/xml_parser_unittest.cc index 0732d4a11..7c9a91275 100644 --- a/omaha/common/xml_parser_unittest.cc +++ b/omaha/common/xml_parser_unittest.cc @@ -25,15 +25,6 @@ #include "omaha/goopdate/update_response_utils.h" #include "omaha/testing/unit_test.h" -namespace { - -const int kSeedManifestFileCount = 1; -const int kSeedManifestResponseCount = 7; - -const int kExpectedRequestLength = 2048; - -} // namespace - namespace omaha { namespace xml { @@ -48,8 +39,12 @@ class XmlParserTest : public ::testing::TestWithParam { return GetParam(); } - virtual void SetUp() { - RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + void SetUp() override { + ClearGroupPolicies(); + } + + void TearDown() override { + ClearGroupPolicies(); } // Allows test fixtures access to implementation details of UpdateRequest. @@ -103,6 +98,7 @@ TEST_F(XmlParserTest, GenerateRequestWithoutUserId_MachineUpdateRequest) { app1.update_check.is_valid = true; app1.update_check.is_rollback_allowed = true; app1.update_check.target_version_prefix = "55.2"; + app1.update_check.target_channel = "dev"; app1.data.push_back(data1); app1.data.push_back(data2); app1.ping.active = ACTIVE_NOTRUN; @@ -128,7 +124,7 @@ TEST_F(XmlParserTest, GenerateRequestWithoutUserId_MachineUpdateRequest) { app2.cohort_name = _T("Name2"); xml_request.apps.push_back(app2); - CString expected_buffer = _T("some untrusted data"); // NOLINT + CString expected_buffer = _T("some untrusted data"); // NOLINT CString actual_buffer; EXPECT_HRESULT_SUCCEEDED(XmlParser::SerializeRequest(*update_request, @@ -243,8 +239,53 @@ TEST_F(XmlParserTest, Parse) { // Array of two request strings that are almost same except the second one // contains some unsupported elements that we expect to be ignored. CStringA buffer_strings[] = { - "{\n \"distribution\": {\n \"verbose_logging\": true\n }\n}\n", // NOLINT - "{\n \"distribution\": {\n \"verbose_logging\": true\n }\n}\nSome strings inside an unsupported element, should be ignored.", // NOLINT + "{\n \"distribution\": {\n \"verbose_logging\": true\n " + "}\n}\n", // NOLINT + "{\n \"distribution\": {\n \"verbose_logging\": true\n " + "}\n}\nSome strings inside an " + "unsupported element, should be ignored.", }; for (int i = 0; i < arraysize(buffer_strings); i++) { @@ -322,7 +363,7 @@ TEST_F(XmlParserTest, Parse) { update_response_utils::ValidateUntrustedData(app.data)); EXPECT_STREQ(i == 0 ? _T("win") : _T(""), xml_response.sys_req.platform); - EXPECT_STREQ(i == 0 ? _T("x86") : _T(""), xml_response.sys_req.arch); + EXPECT_STREQ(i == 0 ? _T("x86,-arm64") : _T(""), xml_response.sys_req.arch); EXPECT_STREQ(i == 0 ? _T("6.0") : _T(""), xml_response.sys_req.min_os_version); } @@ -492,9 +533,8 @@ TEST_P(XmlParserTest, DlPref) { kRegValueIsEnrolledToDomain, IsDomain() ? 1UL : 0UL)); - EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueDownloadPreference, - kDownloadPreferenceCacheable)); + EXPECT_SUCCEEDED(SetPolicyString(kRegValueDownloadPreference, + kDownloadPreferenceCacheable)); std::unique_ptr update_request( UpdateRequest::Create(false, _T(""), _T("is"), _T(""))); @@ -531,7 +571,6 @@ TEST_P(XmlParserTest, DlPref) { EXPECT_STREQ(expected_buffer, actual_buffer); RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain); - RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); } TEST_P(XmlParserTest, DlPrefUnknownPolicy) { @@ -540,9 +579,8 @@ TEST_P(XmlParserTest, DlPrefUnknownPolicy) { IsDomain() ? 1UL : 0UL)); // If a policy different than "cacheable" is set, then the policy is ignored. - EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueDownloadPreference, - _T("unknown policy"))); + EXPECT_SUCCEEDED( + SetPolicyString(kRegValueDownloadPreference, _T("unknown policy"))); std::unique_ptr update_request( UpdateRequest::Create(false, _T(""), _T("is"), _T(""))); @@ -576,7 +614,6 @@ TEST_P(XmlParserTest, DlPrefUnknownPolicy) { EXPECT_STREQ(expected_buffer, actual_buffer); RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain); - RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); } TEST_F(XmlParserTest, PingFreshness) { diff --git a/omaha/core/build.scons b/omaha/core/build.scons index 3835a8607..358f3a4bf 100644 --- a/omaha/core/build.scons +++ b/omaha/core/build.scons @@ -36,61 +36,27 @@ local_env.ComponentLibrary('core', inputs) core_env = env.Clone() omaha_version_info = core_env['omaha_versions_info'][0] -core_env['CPPPATH'] += [ - '$OBJ_ROOT', - ] + core_env.Append( - CCFLAGS = [ - '/wd5038', - ], LIBS = [ 'advapi32.lib', - 'bits.lib', - 'comctl32.lib', 'crypt32.lib', - 'delayimp.lib', - 'imagehlp.lib', - 'iphlpapi.lib', 'kernel32.lib', - 'msi.lib', - 'msimg32.lib', - 'mstask.lib', 'netapi32.lib', - 'ole32.lib', 'psapi.lib', - 'rpcns4.lib', - 'rpcrt4.lib', - 'shell32.lib', 'shlwapi.lib', - 'taskschd.lib', 'user32.lib', 'userenv.lib', - 'uxtheme.lib', 'version.lib', - 'wininet.lib', 'wintrust.lib', - 'ws2_32.lib', 'wtsapi32.lib', core_env['atls_libs'][core_env.Bit('debug')], core_env['crt_libs'][core_env.Bit('debug')], core_env.GetMultiarchLibName('base'), - core_env.GetMultiarchLibName('breakpad'), - core_env.GetMultiarchLibName('client'), core_env.GetMultiarchLibName('common'), - core_env.GetMultiarchLibName('core'), - core_env.GetMultiarchLibName('crash_handler'), - core_env.GetMultiarchLibName('crx_file'), - core_env.GetMultiarchLibName('google_update_recovery'), core_env.GetMultiarchLibName('goopdate_lib'), - core_env.GetMultiarchLibName('libprotobuf'), core_env.GetMultiarchLibName('logging'), - core_env.GetMultiarchLibName('net'), - core_env.GetMultiarchLibName('omaha3_idl'), core_env.GetMultiarchLibName('security'), - core_env.GetMultiarchLibName('service'), - core_env.GetMultiarchLibName('setup'), - core_env.GetMultiarchLibName('statsreport'), - core_env.GetMultiarchLibName('ui'), ], RCFLAGS = [ '/DVERSION_MAJOR=%d' % omaha_version_info.version_major, @@ -111,10 +77,11 @@ if core_env.Bit('has_device_management'): google_core_res_target = 'resource.res' google_core_res = core_env.RES(source = 'resource.rc', - target = google_core_res_target) + target = google_core_res_target) core_env.Depends(google_core_res, 'GoogleUpdateCore.manifest') google_core_inputs = [ + 'core_launcher.cc', 'winmain.cc', google_core_res, ] @@ -126,7 +93,7 @@ unsigned_core = core_env.ComponentProgram( source=google_core_inputs, ) -signed_core = core_env.DualSignedBinary( +signed_core = core_env.SignedBinary( target='%s.exe' % exe_name, source=unsigned_core, ) diff --git a/omaha/core/core.cc b/omaha/core/core.cc index 9452ec7b7..1f96a47c3 100644 --- a/omaha/core/core.cc +++ b/omaha/core/core.cc @@ -55,7 +55,6 @@ #include "omaha/common/scheduled_task_utils.h" #include "omaha/common/stats_uploader.h" #include "omaha/core/core_metrics.h" -#include "omaha/core/scheduler.h" #include "omaha/core/system_monitor.h" #include "omaha/goopdate/app_command.h" #include "omaha/goopdate/app_command_configuration.h" @@ -111,7 +110,14 @@ bool Core::AreScheduledTasksHealthy() const { HRESULT ua_task_last_exit_code = scheduled_task_utils::GetExitCodeGoopdateTaskUA(is_system_); - if (ua_task_last_exit_code == SCHED_S_TASK_HAS_NOT_RUN && + // On Windows 10, we can rely on the last exit code to be + // SCHED_S_TASK_HAS_NOT_RUN. + // On Windows 7, we cannot, so we fallback to checking the last run time. + const bool ua_task_has_not_run = + ua_task_last_exit_code == SCHED_S_TASK_HAS_NOT_RUN || + !scheduled_task_utils::HasGoopdateTaskEverRunUA(is_system_); + + if (ua_task_has_not_run && !ConfigManager::Is24HoursSinceLastUpdate(is_system_)) { // Not 24 hours yet since install or update. Let us give the UA task the // benefit of the doubt, and assume all is well for right now. @@ -172,42 +178,6 @@ bool Core::ShouldRunForever() const { return result; } -bool Core::ShouldRunCore(bool is_system) { - ConfigManager* cm = ConfigManager::Instance(); - const time64 time_difference_ms = cm->GetTimeSinceLastCoreRunMs(is_system); - - const bool result = time_difference_ms >= kMaxWaitBetweenCoreRunsMs; - CORE_LOG(L3, (_T("[ShouldRunCore][%d][%llu]"), result, time_difference_ms)); - return result; -} - -HRESULT Core::UpdateLastCoreRunTime(bool is_system) { - const time64 now = GetCurrentMsTime(); - return ConfigManager::Instance()->SetLastCoreRunTimeMs(is_system, now); -} - -HRESULT Core::StartCoreIfNeeded(bool is_system) { - CORE_LOG(L3, (_T("[Core::StartCoreIfNeeded][%d]"), is_system)); - - if (!ShouldRunCore(is_system)) { - return S_OK; - } - - CommandLineBuilder builder(omaha::COMMANDLINE_MODE_CORE); - CString cmd_line(builder.GetCommandLineArgs()); - scoped_process process; - HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_system, - cmd_line, - address(process)); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[Unable to start Core][%#x]"), hr)); - return hr; - } - - VERIFY1(SUCCEEDED(UpdateLastCoreRunTime(is_system))); - return S_OK; -} - HRESULT Core::DoMain(bool is_system, bool is_crash_handler_enabled) { main_thread_id_ = ::GetCurrentThreadId(); is_system_ = is_system; @@ -277,14 +247,14 @@ HRESULT Core::DoMain(bool is_system, bool is_crash_handler_enabled) { return hr; } - std::unique_ptr scheduler(new Scheduler(*this)); - hr = scheduler->Initialize(); + auto scheduler = std::make_unique(); + hr = InitializeScheduler(scheduler.get()); if (FAILED(hr)) { return hr; } std::unique_ptr system_monitor(new SystemMonitor(is_system_)); - VERIFY1(SUCCEEDED(system_monitor->Initialize(true))); + VERIFY_SUCCEEDED(system_monitor->Initialize(true)); system_monitor->set_observer(this); // Start processing messages and events from the system. @@ -320,12 +290,12 @@ HRESULT Core::ShutdownInternal() const { void Core::LastCheckedDeleted() { OPT_LOG(L1, (_T("[Core::LastCheckedDeleted]"))); - VERIFY1(SUCCEEDED(StartUpdateWorker())); + VERIFY_SUCCEEDED(StartUpdateWorker()); } void Core::NoRegisteredClients() { OPT_LOG(L1, (_T("[Core::NoRegisteredClients]"))); - VERIFY1(SUCCEEDED(StartUpdateWorker())); + VERIFY_SUCCEEDED(StartUpdateWorker()); } HRESULT Core::DoRun() { @@ -334,7 +304,7 @@ HRESULT Core::DoRun() { // Trim the process working set to minimum. It does not need a more complex // algorithm for now. Likely the working set will increase slightly over time // as the core is handling events. - VERIFY1(SUCCEEDED(System::EmptyProcessWorkingSet())); + VERIFY_SUCCEEDED(System::EmptyProcessWorkingSet()); return DoHandleEvents(); } @@ -415,7 +385,7 @@ HRESULT Core::StartCodeRed() const { HRESULT hr = System::StartProcessWithArgs(exe_path, cmd_line); if (SUCCEEDED(hr)) { ++metric_core_cr_succeeded; - VERIFY1(SUCCEEDED(UpdateLastCodeRedCheckTime())); + VERIFY_SUCCEEDED(UpdateLastCodeRedCheckTime()); } else { CORE_LOG(LE, (_T("[can't start Code Red worker][0x%08x]"), hr)); } @@ -471,7 +441,7 @@ void Core::LaunchAppCommandsOnOSUpgrade() const { // Generate a session ID for any pings that might be generated. CString session_id; - VERIFY1(SUCCEEDED(GetGuid(&session_id))); + VERIFY_SUCCEEDED(GetGuid(&session_id)); // Enumerate all registered app commands. If any of them fail to launch, we // record the failure but continue enumerating. @@ -531,7 +501,7 @@ void Core::LaunchAppCommandsOnOSUpgrade() const { // Save the current OS as the old OS version, preventing this code from // running until the OS version changes again. - VERIFY1(SUCCEEDED(app_registry_utils::SetLastOSVersion(is_system_, NULL))); + VERIFY_SUCCEEDED(app_registry_utils::SetLastOSVersion(is_system_, NULL)); ++metric_core_osupgrade_completed; } @@ -551,7 +521,7 @@ HRESULT Core::StartCrashHandler() const { void Core::AggregateMetrics() const { CORE_LOG(L2, (_T("[aggregate core metrics]"))); CollectMetrics(); - VERIFY1(SUCCEEDED(omaha::AggregateMetrics(is_system_))); + VERIFY_SUCCEEDED(omaha::AggregateMetrics(is_system_)); } // Collects: working set, peak working set, handle count, process uptime, @@ -559,10 +529,10 @@ void Core::AggregateMetrics() const { // user time. void Core::CollectMetrics() const { uint64 working_set(0), peak_working_set(0); - VERIFY1(SUCCEEDED(System::GetProcessMemoryStatistics(&working_set, + VERIFY_SUCCEEDED(System::GetProcessMemoryStatistics(&working_set, &peak_working_set, NULL, - NULL))); + NULL)); metric_core_working_set = working_set; metric_core_peak_working_set = peak_working_set; @@ -594,11 +564,44 @@ void Core::CollectMetrics() const { uint64 free_bytes_all_users(0); CString directory_name(app_util::GetCurrentModuleDirectory()); - VERIFY1(SUCCEEDED(System::GetDiskStatistics(directory_name, + VERIFY_SUCCEEDED(System::GetDiskStatistics(directory_name, &free_bytes_current_user, &total_bytes_current_user, - &free_bytes_all_users))); + &free_bytes_all_users)); metric_core_disk_space_available = free_bytes_current_user; } +HRESULT Core::InitializeScheduler(const Scheduler* scheduler) { + ASSERT1(scheduler); + + const ConfigManager* cm = ConfigManager::Instance(); + // Start update worker + HRESULT hr = scheduler->StartWithDelay(cm->GetUpdateWorkerStartUpDelayMs(), + cm->GetAutoUpdateTimerIntervalMs(), + [this]() { StartUpdateWorker(); }); + + if (FAILED(hr)) { + OPT_LOG(LW, (L"[Failed to start update worker scheduler][0x%08x]", hr)); + return hr; + } + + // Start Code Red worker + const int cr_timer_interval = cm->GetCodeRedTimerIntervalMs(); + hr = scheduler->StartWithDebugTimer( + cr_timer_interval, [this, cr_timer_interval](HighresTimer* debug_timer) { + StartCodeRed(); + if (debug_timer) { + int actual_time_ms = static_cast(debug_timer->GetElapsedMs()); + metric_core_cr_actual_timer_interval_ms = actual_time_ms; + } + metric_core_cr_expected_timer_interval_ms = cr_timer_interval; + }); + + if (FAILED(hr)) { + OPT_LOG(LW, (L"[Failed to start code red scheduler][0x%08x]", hr)); + return hr; + } + return S_OK; +} + } // namespace omaha diff --git a/omaha/core/core.h b/omaha/core/core.h index 80476f02c..c1cebb4b3 100644 --- a/omaha/core/core.h +++ b/omaha/core/core.h @@ -30,6 +30,7 @@ #include "base/basictypes.h" #include "omaha/base/shutdown_callback.h" #include "omaha/core/google_update_core.h" +#include "omaha/core/scheduler.h" #include "omaha/core/system_monitor.h" #include "omaha/goopdate/google_update3.h" #include "omaha/third_party/smartany/scoped_any.h" @@ -112,8 +113,7 @@ class Core // Collects ambient core metrics. void CollectMetrics()const; - static bool ShouldRunCore(bool is_system); - static HRESULT UpdateLastCoreRunTime(bool is_system); + HRESULT InitializeScheduler(const Scheduler* scheduler); bool is_system_; diff --git a/omaha/core/core_launcher.cc b/omaha/core/core_launcher.cc new file mode 100644 index 000000000..5c59792e6 --- /dev/null +++ b/omaha/core/core_launcher.cc @@ -0,0 +1,95 @@ +// Copyright 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +#include "omaha/core/core_launcher.h" + +#include + +#include "omaha/base/constants.h" +#include "omaha/base/debug.h" +#include "omaha/base/logging.h" +#include "omaha/base/reg_key.h" +#include "omaha/common/command_line_builder.h" +#include "omaha/common/goopdate_utils.h" +#include "omaha/third_party/smartany/scoped_any.h" + +namespace omaha { + +namespace { + +time64 GetLastCoreRunTimeMs(bool is_machine) { + const wchar_t* reg_update_key(is_machine ? MACHINE_REG_UPDATE + : USER_REG_UPDATE); + time64 last_core_run_time = 0; + if (SUCCEEDED(RegKey::GetValue(reg_update_key, kRegValueLastCoreRun, + &last_core_run_time))) { + return last_core_run_time; + } + + return 0; +} + +time64 GetTimeSinceLastCoreRunMs(bool is_machine) { + const time64 now = GetCurrentMsTime(); + const time64 last_core_run = GetLastCoreRunTimeMs(is_machine); + + if (now < last_core_run) { + return UINT64_MAX; + } + + return now - last_core_run; +} + +HRESULT UpdateLastCoreRunTime(bool is_machine) { + const time64 now = GetCurrentMsTime(); + const wchar_t* reg_update_key(is_machine ? MACHINE_REG_UPDATE + : USER_REG_UPDATE); + return RegKey::SetValue(reg_update_key, kRegValueLastCoreRun, now); +} + +} // namespace + +bool ShouldRunCore(bool is_system) { + const time64 time_difference_ms = GetTimeSinceLastCoreRunMs(is_system); + + const bool result = time_difference_ms >= kMaxWaitBetweenCoreRunsMs; + CORE_LOG(L3, (L"[ShouldRunCore][%d][%llu]", result, time_difference_ms)); + return result; +} + +HRESULT StartCoreIfNeeded(bool is_system) { + CORE_LOG(L3, (L"[Core::StartCoreIfNeeded][%d]", is_system)); + + if (!ShouldRunCore(is_system)) { + return S_OK; + } + + CommandLineBuilder builder(omaha::COMMANDLINE_MODE_CORE); + CString cmd_line(builder.GetCommandLineArgs()); + scoped_process process; + HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_system, + StartMode::kBackground, + cmd_line, + address(process)); + if (FAILED(hr)) { + CORE_LOG(LE, (L"[Unable to start Core][%#x]", hr)); + return hr; + } + + VERIFY_SUCCEEDED(UpdateLastCoreRunTime(is_system)); + return S_OK; +} + +} // namespace omaha diff --git a/omaha/goopdate/file_hash.h b/omaha/core/core_launcher.h similarity index 68% rename from omaha/goopdate/file_hash.h rename to omaha/core/core_launcher.h index 756ef04b6..24216918b 100644 --- a/omaha/goopdate/file_hash.h +++ b/omaha/core/core_launcher.h @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. +// Copyright 2019 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,20 +13,19 @@ // limitations under the License. // ======================================================================== -#ifndef OMAHA_GOOPDATE_FILE_HASH_ -#define OMAHA_GOOPDATE_FILE_HASH_ +// Launches the core process by starting the shell exe with /c argument -#include -#include +#ifndef OMAHA_CORE_CORE_LAUNCHER_H_ +#define OMAHA_CORE_CORE_LAUNCHER_H_ + +#include namespace omaha { -// Representation of downloaded file hash values. -struct FileHash { - CString sha1; - CString sha256; -}; +bool ShouldRunCore(bool is_system); + +HRESULT StartCoreIfNeeded(bool is_system); } // namespace omaha -#endif // OMAHA_GOOPDATE_FILE_HASH_ +#endif // OMAHA_CORE_CORE_LAUNCHER_H_ diff --git a/omaha/core/core_unittest.cc b/omaha/core/core_unittest.cc index 14e177102..a0de7451f 100644 --- a/omaha/core/core_unittest.cc +++ b/omaha/core/core_unittest.cc @@ -29,6 +29,7 @@ #include "omaha/common/goopdate_utils.h" #include "omaha/common/scheduled_task_utils.h" #include "omaha/core/core.h" +#include "omaha/core/core_launcher.h" #include "omaha/goopdate/app_command_test_base.h" #include "omaha/setup/setup_service.h" #include "omaha/testing/unit_test.h" @@ -242,7 +243,7 @@ class CoreUtilsTest : public testing::Test { } bool ShouldRunCore() { - return Core::ShouldRunCore(is_machine_); + return omaha::ShouldRunCore(is_machine_); } bool ShouldRunCodeRed() { diff --git a/omaha/core/scheduler.cc b/omaha/core/scheduler.cc index d2363e7f6..22b2c4a67 100644 --- a/omaha/core/scheduler.cc +++ b/omaha/core/scheduler.cc @@ -14,116 +14,144 @@ // ======================================================================== #include "omaha/core/scheduler.h" -#include + #include "omaha/base/debug.h" #include "omaha/base/error.h" -#include "omaha/base/highres_timer-win32.h" #include "omaha/base/logging.h" -#include "omaha/base/queue_timer.h" -#include "omaha/common/config_manager.h" -#include "omaha/core/core.h" -#include "omaha/core/core_metrics.h" namespace omaha { -Scheduler::Scheduler(const Core& core) - : core_(core) { - CORE_LOG(L1, (_T("[Scheduler::Scheduler]"))); +Scheduler::SchedulerItem::SchedulerItem(HANDLE timer_queue, + int start_delay_ms, + int interval_ms, + bool has_debug_timer, + ScheduledWorkWithTimer work) + : start_delay_ms_(start_delay_ms), interval_ms_(interval_ms), work_(work) { + if (has_debug_timer) { + debug_timer_.reset(new HighresTimer()); + } + + if (timer_queue) { + timer_.reset( + new QueueTimer(timer_queue, &SchedulerItem::TimerCallback, this)); + VERIFY_SUCCEEDED( + ScheduleNext(timer_.get(), debug_timer_.get(), start_delay_ms)); + } } -Scheduler::~Scheduler() { - CORE_LOG(L1, (_T("[Scheduler::~Scheduler]"))); +Scheduler::SchedulerItem::~SchedulerItem() { + // QueueTimer dtor may block for pending callbacks. + if (timer_) { + timer_.reset(); + } - if (update_timer_.get()) { - update_timer_.reset(NULL); + if (debug_timer_) { + debug_timer_.reset(); } +} - if (code_red_timer_.get()) { - code_red_timer_.reset(NULL); +// static +HRESULT Scheduler::SchedulerItem::ScheduleNext(QueueTimer* timer, + HighresTimer* debug_timer, + int start_after_ms) { + if (!timer) { + return E_FAIL; } - if (timer_queue_) { - // The destructor blocks on deleting the timer queue and it waits for - // all timer callbacks to complete. - ::DeleteTimerQueueEx(timer_queue_, INVALID_HANDLE_VALUE); + if (debug_timer) { + debug_timer->Start(); } -} -HRESULT Scheduler::Initialize() { - CORE_LOG(L1, (_T("[Scheduler::Initialize]"))); + const HRESULT hr = timer->Start(start_after_ms, 0, WT_EXECUTEONLYONCE); - timer_queue_ = ::CreateTimerQueue(); - if (!timer_queue_) { - return HRESULTFromLastError(); + if (FAILED(hr)) { + CORE_LOG(LE, (L"[can't start queue timer][0x%08x]", hr)); } - cr_debug_timer_.reset(new HighresTimer); + return hr; +} + +// static +void Scheduler::SchedulerItem::TimerCallback(QueueTimer* timer) { + ASSERT1(timer); + if (!timer) { + return; + } - update_timer_.reset(new QueueTimer(timer_queue_, - &Scheduler::TimerCallback, - this)); - code_red_timer_.reset(new QueueTimer(timer_queue_, - &Scheduler::TimerCallback, - this)); + SchedulerItem* item = reinterpret_cast(timer->ctx()); + ASSERT1(item); - ConfigManager* config_manager = ConfigManager::Instance(); - int cr_timer_interval_ms = config_manager->GetCodeRedTimerIntervalMs(); - VERIFY1(SUCCEEDED(ScheduleCodeRedTimer(cr_timer_interval_ms))); + if (!item) { + CORE_LOG(LE, (L"[Expected timer context to contain SchedulerItem]")); + return; + } - int au_timer_interval_ms = config_manager->GetUpdateWorkerStartUpDelayMs(); - VERIFY1(SUCCEEDED(ScheduleUpdateTimer(au_timer_interval_ms))); + // This may be long running, |item| may be deleted in the meantime, + // however the dtor should block on deleting the |timer| and allow + // pending callbacks to run. + if (item && item->work_) { + item->work_(item->debug_timer()); + } - return S_OK; + if (item) { + const HRESULT hr = SchedulerItem::ScheduleNext(timer, + item->debug_timer(), + item->interval_ms()); + if (FAILED(hr)) { + CORE_LOG(L1, (L"[Scheduling next timer callback failed][0x%08x]", hr)); + } + } } -void Scheduler::TimerCallback(QueueTimer* timer) { - ASSERT1(timer); - Scheduler* scheduler = static_cast(timer->ctx()); - ASSERT1(scheduler); - scheduler->HandleCallback(timer); +Scheduler::Scheduler() { + CORE_LOG(L1, (L"[Scheduler::Scheduler]")); + timer_queue_ = ::CreateTimerQueue(); + if (!timer_queue_) { + CORE_LOG(LE, (L"[Failed to create Timer Queue][%d]", ::GetLastError())); + } } -// First, do the useful work and then reschedule the timer. Otherwise, it is -// possible that timer notifications overlap, and the timer can't be further -// rescheduled: http://b/1228095 -void Scheduler::HandleCallback(QueueTimer* timer) { - ConfigManager* config_manager = ConfigManager::Instance(); - if (update_timer_.get() == timer) { - core_.StartUpdateWorker(); - int au_timer_interval_ms = config_manager->GetAutoUpdateTimerIntervalMs(); - VERIFY1(SUCCEEDED(ScheduleUpdateTimer(au_timer_interval_ms))); - } else if (code_red_timer_.get() == timer) { - core_.StartCodeRed(); - int actual_time_ms = static_cast(cr_debug_timer_->GetElapsedMs()); - metric_core_cr_actual_timer_interval_ms = actual_time_ms; - CORE_LOG(L3, (_T("[code red actual period][%d ms]"), actual_time_ms)); - int cr_timer_interval_ms = config_manager->GetCodeRedTimerIntervalMs(); - VERIFY1(SUCCEEDED(ScheduleCodeRedTimer(cr_timer_interval_ms))); - } else { - ASSERT1(false); +Scheduler::~Scheduler() { + CORE_LOG(L1, (L"[Scheduler::~Scheduler]")); + + timers_.clear(); + + if (timer_queue_) { + // The destructor blocks on deleting the timer queue and it waits for + // all timer callbacks to complete. + ::DeleteTimerQueueEx(timer_queue_, INVALID_HANDLE_VALUE); + timer_queue_ = NULL; } +} - // Since core is a long lived process, aggregate its metrics once in a while. - core_.AggregateMetrics(); +HRESULT Scheduler::StartWithDebugTimer(int interval, + ScheduledWorkWithTimer work) const { + return DoStart(interval, interval, work, true /*has_debug_timer*/); } -HRESULT Scheduler::ScheduleUpdateTimer(int interval_ms) { - HRESULT hr = update_timer_->Start(interval_ms, 0, WT_EXECUTEONLYONCE); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[can't start update queue timer][0x%08x]"), hr)); - } - return hr; +HRESULT Scheduler::StartWithDelay(int delay, + int interval, + ScheduledWork work) const { + return DoStart(delay, interval, std::bind(work)); } -HRESULT Scheduler::ScheduleCodeRedTimer(int interval_ms) { - metric_core_cr_expected_timer_interval_ms = interval_ms; - cr_debug_timer_->Start(); - HRESULT hr = code_red_timer_->Start(interval_ms, 0, WT_EXECUTEONLYONCE); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[can't start Code Red queue timer][0x%08x]"), hr)); +HRESULT Scheduler::Start(int interval, ScheduledWork work) const { + return DoStart(interval, interval, std::bind(work)); +} + +HRESULT Scheduler::DoStart(int start_delay, + int interval, + ScheduledWorkWithTimer work_fn, + bool has_debug_timer) const { + CORE_LOG(L1, (L"[Scheduler::Start]")); + + if (!timer_queue_) { + return HRESULTFromLastError(); } - return hr; + + timers_.emplace_back(timer_queue_, start_delay, interval, has_debug_timer, + work_fn); + return S_OK; } } // namespace omaha - diff --git a/omaha/core/scheduler.h b/omaha/core/scheduler.h index 457595599..c97b0b25e 100644 --- a/omaha/core/scheduler.h +++ b/omaha/core/scheduler.h @@ -20,40 +20,82 @@ #define OMAHA_CORE_SCHEDULER_H__ #include -#include +#include +#include #include #include "base/basictypes.h" +#include "omaha/base/highres_timer-win32.h" +#include "omaha/base/queue_timer.h" namespace omaha { -class Core; -class HighresTimer; -class QueueTimer; +using ScheduledWork = std::function; +using ScheduledWorkWithTimer = std::function; class Scheduler { public: - explicit Scheduler(const Core& core); + explicit Scheduler(); ~Scheduler(); - // Starts the scheduler. - HRESULT Initialize(); + // Starts the scheduler that executes |work| with regular |interval| (ms). + HRESULT Start(int interval, ScheduledWork work) const; + + // Starts the scheduler that executes |work| with regular |interval| (ms) + // after an initial |delay| (ms). + HRESULT StartWithDelay(int delay, int interval, ScheduledWork work) const; + + // Start the scheduler on a regular |interval| (ms). The callback is provided + // a timer which starts after the previous item finishes execution. + HRESULT StartWithDebugTimer(int interval, ScheduledWorkWithTimer work) const; private: - static void TimerCallback(QueueTimer* timer); - void HandleCallback(QueueTimer* timer); - HRESULT ScheduleUpdateTimer(int interval_ms); - HRESULT ScheduleCodeRedTimer(int interval_ms); + class SchedulerItem { + public: + SchedulerItem(HANDLE timer_queue, + int start_delay, + int interval, + bool has_debug_timer, + ScheduledWorkWithTimer work_fn); + + ~SchedulerItem(); + + HighresTimer* debug_timer() const { + return debug_timer_ ? debug_timer_.get() : nullptr; + } + + int interval_ms() const { return interval_ms_; } + + private: + int start_delay_ms_; + int interval_ms_; + + std::unique_ptr timer_; - const Core& core_; + // Measures the actual time interval between events for debugging + // purposes. The timer is started when an alarm is set and then, + // the value of the timer is read when the alarm goes off. + std::unique_ptr debug_timer_; + + ScheduledWorkWithTimer work_; + + static HRESULT ScheduleNext(QueueTimer* timer, + HighresTimer* debug_timer, + int interval_ms); + static void TimerCallback(QueueTimer* timer); + + DISALLOW_COPY_AND_ASSIGN(SchedulerItem); + }; + + HRESULT DoStart(int start_delay, + int interval, + ScheduledWorkWithTimer work, + bool has_debug_timer = false) const; + + // Timer queue handle for all QueueTimer objects. HANDLE timer_queue_; - std::unique_ptr update_timer_; - std::unique_ptr code_red_timer_; - // Measures the actual time interval between code red events for debugging - // purposes. The timer is started when a code red alarm is set and then, - // the value of the timer is read when the alarm goes off. - std::unique_ptr cr_debug_timer_; + mutable std::list timers_; DISALLOW_COPY_AND_ASSIGN(Scheduler); }; @@ -61,4 +103,3 @@ class Scheduler { } // namespace omaha #endif // OMAHA_CORE_SCHEDULER_H__ - diff --git a/omaha/core/scheduler_unittest.cc b/omaha/core/scheduler_unittest.cc new file mode 100644 index 000000000..7969d834e --- /dev/null +++ b/omaha/core/scheduler_unittest.cc @@ -0,0 +1,184 @@ +// Copyright 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +#include "omaha/core/scheduler.h" + +#include + +#include "omaha/base/constants.h" +#include "omaha/base/highres_timer-win32.h" +#include "omaha/testing/unit_test.h" +#include "omaha/third_party/smartany/scoped_any.h" + +namespace omaha { + +namespace { + +inline void AssertSignalledBefore(HANDLE handle, DWORD timeout_ms) { + ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(handle, timeout_ms)); +} + +inline void AssertAllSignalledBefore(std::vector& handles, + DWORD timeout_ms) { + constexpr bool kWaitAll = true; + std::vector raw_handles; + for (auto& handle : handles) { + raw_handles.emplace_back(get(handle)); + } + const DWORD res = ::WaitForMultipleObjects( + raw_handles.size(), &raw_handles[0], kWaitAll, timeout_ms); + ASSERT_EQ(WAIT_OBJECT_0, res); +} + +inline void AssertTimeoutAfter(HANDLE handle, DWORD timeout_ms) { + ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(handle, timeout_ms)); +} + +} // namespace + +class SchedulerTest : public ::testing::Test { + protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(SchedulerTest, ScheduledTaskReschedules) { + int call_count = 0; + std::vector event_handles(4); + + for (auto& handle : event_handles) { + reset(handle, ::CreateEvent(NULL, true, false, NULL)); + } + + Scheduler scheduler; + // Increase call count after 200ms and every 300ms afterwards + HRESULT hr = + scheduler.StartWithDelay(200, 300, [&call_count, &event_handles]() { + if (call_count < 4) { + ::SetEvent(get(event_handles[call_count])); + } + call_count++; + }); + ASSERT_SUCCEEDED(hr); + AssertAllSignalledBefore(event_handles, 1500); + EXPECT_GE(4, call_count); +} + +TEST_F(SchedulerTest, DeleteWhenCallbackExpires) { + int call_count = 0; + std::vector callbacks(2); + for (auto& handle : callbacks) { + reset(handle, ::CreateEvent(NULL, true, false, NULL)); + } + + // Create the scheduler in a new scope + { + Scheduler scheduler; + // Timer that runs every 250 ms + HRESULT hr = scheduler.Start(250, [&call_count, &callbacks]() { + ::SetEvent(get(callbacks[call_count])); + call_count++; + }); + // Wait for one callback, then scheduler should go out of scope + AssertSignalledBefore(get(callbacks[0]), 300); + } + // Second callback should never fire + AssertTimeoutAfter(get(callbacks[1]), 1000); + EXPECT_EQ(call_count, 1); +} + +TEST_F(SchedulerTest, DeleteSoonBeforeCallbackExpires) { + int call_count = 0; + constexpr int kInterval = 500; + constexpr int kTimeout = kInterval - 50; + scoped_handle callback_fired(::CreateEvent(NULL, true, false, NULL)); + { + Scheduler scheduler; + HRESULT hr = scheduler.Start(kInterval, [&call_count, &callback_fired]() { + call_count++; + ::SetEvent(get(callback_fired)); + }); + ASSERT_SUCCEEDED(hr); + AssertTimeoutAfter(get(callback_fired), kTimeout); + } + EXPECT_EQ(call_count, 0); +} + +TEST_F(SchedulerTest, DoesntUseDebugTimer) { + int call_count = 0; + constexpr int kExpectedIntervalMs = 100; + constexpr int kTimeout = 500; + scoped_handle callback_fired(::CreateEvent(NULL, true, false, NULL)); + { + Scheduler scheduler; + HRESULT hr = + scheduler.Start(kExpectedIntervalMs, [&call_count, &callback_fired]() { + call_count++; + ::SetEvent(get(callback_fired)); + }); + ASSERT_SUCCEEDED(hr); + AssertSignalledBefore(get(callback_fired), kTimeout); + } + ASSERT_EQ(call_count, 1); +} + +TEST_F(SchedulerTest, UsesDebugTimer) { + int call_count = 0; + constexpr int kExpectedIntervalMs = 500; + scoped_handle callback_handle(::CreateEvent(NULL, true, false, NULL)); + { + Scheduler scheduler; + HRESULT hr = scheduler.StartWithDebugTimer( + kExpectedIntervalMs, [&call_count, + &callback_handle](HighresTimer* debug_timer) { + ASSERT_TRUE(debug_timer != nullptr); + EXPECT_GE(debug_timer->GetElapsedMs(), kExpectedIntervalMs); + call_count++; + ::SetEvent(get(callback_handle)); + }); + ASSERT_SUCCEEDED(hr); + AssertSignalledBefore(get(callback_handle), kExpectedIntervalMs + 100); + } + ASSERT_EQ(call_count, 1); +} + +TEST_F(SchedulerTest, LongCallbackBlocks) { + auto scheduler = std::make_unique(); + constexpr int kInterval = 50; + constexpr int kCallbackDelay = 500; + + scoped_handle callback_start(::CreateEvent(NULL, true, false, NULL)); + scoped_handle callback_end(::CreateEvent(NULL, true, false, NULL)); + + HRESULT hr = scheduler->Start( + kInterval, [&callback_start, &callback_end]() { + ::SetEvent(get(callback_start)); + Sleep(kCallbackDelay); + ::SetEvent(get(callback_end)); + }); + ASSERT_SUCCEEDED(hr); + // Try deleting, record how much time it takes + AssertSignalledBefore(get(callback_start), 100); + HighresTimer timer; + timer.Start(); + + // Delete the scheduler, this should block until the long callback finishes + scheduler.reset(); + + AssertSignalledBefore(get(callback_end), 600); + EXPECT_GE(timer.GetElapsedMs(), kCallbackDelay); +} + +} // namespace omaha diff --git a/omaha/core/system_monitor.cc b/omaha/core/system_monitor.cc index 7629db872..691e03243 100644 --- a/omaha/core/system_monitor.cc +++ b/omaha/core/system_monitor.cc @@ -40,19 +40,19 @@ HRESULT SystemMonitor::Initialize(bool monitor_registry) { registry_monitor_.reset(new RegistryMonitor); if (SUCCEEDED(registry_monitor_->Initialize())) { HKEY root_key = is_machine_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - VERIFY1(SUCCEEDED(registry_monitor_->MonitorValue( + VERIFY_SUCCEEDED(registry_monitor_->MonitorValue( root_key, GOOPDATE_MAIN_KEY, kRegValueLastChecked, REG_DWORD, RegistryValueChangeCallback, - this))); - VERIFY1(SUCCEEDED(registry_monitor_->MonitorKey( + this)); + VERIFY_SUCCEEDED(registry_monitor_->MonitorKey( root_key, GOOPDATE_REG_RELATIVE_CLIENTS, RegistryKeyChangeCallback, - this))); - VERIFY1(SUCCEEDED(registry_monitor_->StartMonitoring())); + this)); + VERIFY_SUCCEEDED(registry_monitor_->StartMonitoring()); } } diff --git a/omaha/core/winmain.cc b/omaha/core/winmain.cc index 706bc1d44..c1cc6e7b0 100644 --- a/omaha/core/winmain.cc +++ b/omaha/core/winmain.cc @@ -14,12 +14,12 @@ // ======================================================================== #include -#include -#include + #include "omaha/base/omaha_version.h" -#include "omaha/core/core.h" +#include "omaha/base/utils.h" +#include "omaha/core/core_launcher.h" -int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { +int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { omaha::EnableSecureDllLoading(); scoped_co_init init_com_apt(COINIT_MULTITHREADED); @@ -36,5 +36,5 @@ int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { return hr; } - return omaha::Core::StartCoreIfNeeded(is_system); + return omaha::StartCoreIfNeeded(is_system); } diff --git a/omaha/crashhandler/build.scons b/omaha/crashhandler/build.scons index 5b913c351..0e3fb1a5b 100644 --- a/omaha/crashhandler/build.scons +++ b/omaha/crashhandler/build.scons @@ -87,6 +87,7 @@ def BuildCrashHandler(local_env): gch_env.GetMultiarchLibName('breakpad'), gch_env.GetMultiarchLibName('common'), gch_env.GetMultiarchLibName('crash_handler'), + gch_env.GetMultiarchLibName('goopdate_lib'), gch_env.GetMultiarchLibName('net'), gch_env.GetMultiarchLibName('security'), gch_env.GetMultiarchLibName('statsreport'), @@ -118,7 +119,7 @@ def BuildCrashHandler(local_env): source=gch_inputs, ) - signed_crash_handler = gch_env.DualSignedBinary( + signed_crash_handler = gch_env.SignedBinary( target='%s.exe' % exe_name, source=unsigned_crash_handler, ) @@ -133,7 +134,15 @@ def BuildCrashAnalysisTest(local_env): td_env.Append( CPPPATH = [], LIBS = [ + td_env['atls_libs'][td_env.Bit('debug')], td_env['crt_libs'][td_env.Bit('debug')], + td_env.GetMultiarchLibName('base'), + 'netapi32.lib', + 'psapi.lib', + 'shlwapi.lib', + 'userenv.lib', + 'version.lib', + 'wtsapi32.lib', ], RCFLAGS = [ '/DVERSION_MAJOR=%d' % omaha_version_info.version_major, diff --git a/omaha/crashhandler/crash_analyzer_checks.cc b/omaha/crashhandler/crash_analyzer_checks.cc index fe30b9b2f..37477e889 100644 --- a/omaha/crashhandler/crash_analyzer_checks.cc +++ b/omaha/crashhandler/crash_analyzer_checks.cc @@ -20,6 +20,7 @@ #include #include "omaha/base/safe_format.h" +#include "omaha/base/utils.h" namespace omaha { @@ -92,8 +93,8 @@ CrashAnalysisResult NtFunctionsOnStack::Run() { // Because the crash handler process is running on the same system // as the process which crashed we can assume that they are mapped // in the same location in both processes. - HMODULE ntdll = ::LoadLibraryA("ntdll.dll"); - HMODULE kernel32 = ::LoadLibraryA("kernel32.dll"); + HMODULE ntdll = LoadSystemLibrary(_T("ntdll.dll")); + HMODULE kernel32 = LoadSystemLibrary(_T("kernel32.dll")); std::vector functions; functions.push_back(reinterpret_cast( ::GetProcAddress(kernel32, "HeapCreate"))); diff --git a/omaha/crashhandler/crash_analyzer_debugee_test.cc b/omaha/crashhandler/crash_analyzer_debugee_test.cc index e9e37c394..e0ddf8ba6 100644 --- a/omaha/crashhandler/crash_analyzer_debugee_test.cc +++ b/omaha/crashhandler/crash_analyzer_debugee_test.cc @@ -20,6 +20,8 @@ #include #include +#include "omaha/base/utils.h" + typedef void (*DebugeeFunction)(void); typedef std::map DebugeeMap; DebugeeMap debugee_map; @@ -43,7 +45,7 @@ void MaxExecMappings() { } void NtFunctionsOnStack() { - HMODULE ntdll = ::LoadLibraryA("ntdll.dll"); + HMODULE ntdll = omaha::LoadSystemLibrary(_T("ntdll.dll")); FARPROC ptr = ::GetProcAddress(ntdll, "ZwProtectVirtualMemory"); AwaitTheReaper(); } @@ -62,7 +64,8 @@ void TestWildStackPointer() { void TestPENotInModuleList() { MEMORY_BASIC_INFORMATION mbi = {0}; - BYTE* ntdll = reinterpret_cast(::LoadLibraryA("ntdll.dll")); + BYTE* ntdll = reinterpret_cast( + omaha::LoadSystemLibrary(_T("ntdll.dll"))); ::VirtualQuery(ntdll, &mbi, sizeof(mbi)); LPVOID buffer = ::VirtualAlloc(NULL, mbi.RegionSize, diff --git a/omaha/crashhandler/crash_dump_util.cc b/omaha/crashhandler/crash_dump_util.cc index 0bcae77dd..511860937 100644 --- a/omaha/crashhandler/crash_dump_util.cc +++ b/omaha/crashhandler/crash_dump_util.cc @@ -35,7 +35,6 @@ namespace { const TCHAR* const kLaunchedForMinidump = _T("CrashHandlerLaunchedForMinidump"); const TCHAR* const kCrashInfoKey = _T("CrashHandlerEnv_CrashInfo"); -const TCHAR* const kUntrustedIntegrityLevelString = _T("S-1-16-0"); HRESULT SetValueToEnvironmentBlock(EnvironmentBlockModifier* eb_mod, const TCHAR* name, diff --git a/omaha/crashhandler/crash_handler.cc b/omaha/crashhandler/crash_handler.cc index 70b1f29e8..5d626ea84 100644 --- a/omaha/crashhandler/crash_handler.cc +++ b/omaha/crashhandler/crash_handler.cc @@ -29,7 +29,6 @@ #include "omaha/crashhandler/crash_handler.h" #include -#include #include #include @@ -107,10 +106,10 @@ HRESULT CrashHandler::Main(bool is_system) { COMMANDLINE_MODE_CRASH_HANDLER); custom_info_map[kCrashCustomInfoCommandLineMode] = command_line_mode; - VERIFY1(SUCCEEDED( + VERIFY_SUCCEEDED( OmahaExceptionHandler::Create(is_system, custom_info_map, - &exception_handler_))); + &exception_handler_)); // Are we allowed to monitor crashes? if (!ConfigManager::Instance()->CanCollectStats(is_system)) { @@ -323,29 +322,14 @@ HRESULT CrashHandler::StartServer() { } UTIL_LOG(L6, (_T("[listening on crash pipe][%s]"), pipe_name)); - CSecurityDesc pipe_sec_desc; - hr = crash_utils::BuildPipeSecurityAttributes(is_system_, &pipe_sec_desc); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[BuildPipeSecurityAttributes() failed][0x%08x]"), hr)); - return GOOPDATE_E_CRASH_SECURITY_FAILED; - } - CSecurityAttributes pipe_sec_attrs; - pipe_sec_attrs.Set(pipe_sec_desc); - -#ifdef DEBUG - // Print SDDL to log for debugging. - CString sddl; - pipe_sec_desc.ToString(&sddl, OWNER_SECURITY_INFORMATION | - GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION | - SACL_SECURITY_INFORMATION | - LABEL_SECURITY_INFORMATION); - CORE_LOG(L1, (_T("[Pipe security SDDL][%s]"), sddl)); -#endif - + // https://bit.ly/3fygY37 + // The ACLs in the default security descriptor for a named pipe grant full + // control to the LocalSystem account, administrators, and the creator owner. + // They also grant read access to members of the Everyone group and the + // anonymous account. crash_server_.reset(new google_breakpad::CrashGenerationServer( std::wstring(pipe_name), - &pipe_sec_attrs, + NULL, BreakpadClientConnected, NULL, BreakpadClientCrashed, this, BreakpadClientDisconnected, NULL, @@ -638,7 +622,7 @@ HRESULT CrashHandler::RunUntilShutdown() { // Trim the process working set to minimum. It does not need a more complex // algorithm for now. Likely the working set will increase slightly over time // as the CrashHandler is handling events. - VERIFY1(SUCCEEDED(System::EmptyProcessWorkingSet())); + VERIFY_SUCCEEDED(System::EmptyProcessWorkingSet()); // Pump messages. If the shutdown event is set, a WM_QUIT will be posted to // this thread (from a thread pool thread running Shutdown(), below) to exit. diff --git a/omaha/crashhandler/crash_worker.cc b/omaha/crashhandler/crash_worker.cc index 3d61993f5..6b48ca1c5 100644 --- a/omaha/crashhandler/crash_worker.cc +++ b/omaha/crashhandler/crash_worker.cc @@ -85,7 +85,7 @@ HRESULT InitializeWorkerDesktop() { } scoped_hdesk worker_desktop( - ::CreateDesktop(_T("GoogleCrashHandlerWorkerDesktop"), + ::CreateDesktop(CRASH_HANDLER_NAME _T("WorkerDesktop"), NULL, NULL, 0, diff --git a/omaha/data/Sha1OmahaTestCert.cer b/omaha/data/Sha1OmahaTestCert.cer deleted file mode 100644 index 229620b21..000000000 Binary files a/omaha/data/Sha1OmahaTestCert.cer and /dev/null differ diff --git a/omaha/data/Sha1OmahaTestCert.pfx b/omaha/data/Sha1OmahaTestCert.pfx deleted file mode 100644 index b8d2aee2e..000000000 Binary files a/omaha/data/Sha1OmahaTestCert.pfx and /dev/null differ diff --git a/omaha/enterprise/generate_group_policy_template.py b/omaha/enterprise/generate_group_policy_template.py index 1e38d9716..0e99be8f5 100644 --- a/omaha/enterprise/generate_group_policy_template.py +++ b/omaha/enterprise/generate_group_policy_template.py @@ -53,7 +53,7 @@ EXPLAIN !!Explain_AutoUpdateCheckPeriod PART !!Part_AutoUpdateCheckPeriod NUMERIC VALUENAME AutoUpdateCheckPeriodMinutes - DEFAULT 1400 ; 23 hours 20 minutes. + DEFAULT 295 ; 4 hours 55 minutes. MIN 0 MAX 43200 ; 30 days. SPIN 60 ; Increment in hour chunks. @@ -167,6 +167,37 @@ EXPLAIN !!Explain_Applications """ +INSTALL_POLICY_ITEMLIST_BEGIN = """\ + ITEMLIST + NAME !!Name_InstallsEnabled + VALUE NUMERIC 1 + NAME !!Name_InstallsEnabledMachineOnly + VALUE NUMERIC 4 + NAME !!Name_InstallsDisabled + VALUE NUMERIC 0""" + +INSTALL_POLICY_FORCE_INSTALL_MACHINE = r""" + NAME !!Name_ForceInstallsMachine + VALUE NUMERIC 5""" + +INSTALL_POLICY_FORCE_INSTALL_USER = r""" + NAME !!Name_ForceInstallsUser + VALUE NUMERIC 6""" + +INSTALL_POLICY_ITEMLIST_APP_SPECIFIC = """\ +$ForceInstalls$""" + +INSTALL_POLICY_ITEMLIST_END = r""" + END ITEMLIST + REQUIRED""" + +INSTALL_POLICY_ITEMLIST = INSTALL_POLICY_ITEMLIST_BEGIN + \ + INSTALL_POLICY_ITEMLIST_END + +INSTALL_POLICY_ITEMLIST_APP_SPECIFIC = INSTALL_POLICY_ITEMLIST_BEGIN + \ + INSTALL_POLICY_ITEMLIST_APP_SPECIFIC + \ + INSTALL_POLICY_ITEMLIST_END + UPDATE_POLICY_ITEMLIST = """\ ITEMLIST NAME !!Name_UpdatesEnabled @@ -186,9 +217,11 @@ SUPPORTED !!Sup_GoogleUpdate1_2_145_5 #endif EXPLAIN !!Explain_DefaultAllowInstallation - VALUENAME InstallDefault - VALUEOFF NUMERIC 0 - VALUEON NUMERIC 1 + PART !!Part_InstallPolicy DROPDOWNLIST + VALUENAME InstallDefault +""" + +INSTALL_POLICY_ITEMLIST + """ + END PART END POLICY POLICY !!Pol_DefaultUpdatePolicy @@ -213,9 +246,12 @@ SUPPORTED !!Sup_GoogleUpdate1_2_145_5 #endif EXPLAIN !!Explain_Install$AppLegalId$ - VALUENAME Install$AppGuid$ - VALUEOFF NUMERIC 0 - VALUEON NUMERIC 1 + PART !!Part_InstallPolicy DROPDOWNLIST + VALUENAME Install$AppGuid$ +""" + +INSTALL_POLICY_ITEMLIST_APP_SPECIFIC.replace(' ', ' ') + +""" + END PART END POLICY POLICY !!Pol_UpdatePolicy @@ -230,6 +266,17 @@ END PART END POLICY + POLICY !!Pol_TargetChannel + #if version >= 4 + SUPPORTED !!Sup_GoogleUpdate1_3_35_453 + #endif + EXPLAIN !!Explain_TargetChannel$AppLegalId$ + + PART !!Part_TargetChannel EDITTEXT + VALUENAME "TargetChannel$AppGuid$" + END PART + END POLICY + POLICY !!Pol_TargetVersionPrefix #if version >= 4 SUPPORTED !!Sup_GoogleUpdate1_3_33_5 @@ -266,6 +313,7 @@ ALLOW_INSTALLATION_POLICY = 'Allow installation' DEFAULT_ALLOW_INSTALLATION_POLICY = ALLOW_INSTALLATION_POLICY + ' default' UPDATE_POLICY = 'Update policy override' +TARGET_CHANNEL_POLICY = 'Target Channel override' TARGET_VERSION_POLICY = 'Target version prefix override' ROLLBACK_VERSION_POLICY = 'Rollback to Target version' DEFAULT_UPDATE_POLICY = UPDATE_POLICY + ' default' @@ -292,6 +340,7 @@ Sup_GoogleUpdate1_3_26_0=At least Google Update 1.3.26.0 Sup_GoogleUpdate1_3_33_5=At least Google Update 1.3.33.5 Sup_GoogleUpdate1_3_34_3=At least Google Update 1.3.34.3 +Sup_GoogleUpdate1_3_35_453=At least Google Update 1.3.35.453 Cat_Google=Google Cat_GoogleUpdate=Google Update @@ -309,6 +358,7 @@ Pol_AllowInstallation=""" + ALLOW_INSTALLATION_POLICY + """ Pol_DefaultUpdatePolicy=""" + DEFAULT_UPDATE_POLICY + """ Pol_UpdatePolicy=""" + UPDATE_POLICY + """ +Pol_TargetChannel=""" + TARGET_CHANNEL_POLICY + """ Pol_TargetVersionPrefix=""" + TARGET_VERSION_POLICY + """ Pol_RollbackToTargetVersion=""" + ROLLBACK_VERSION_POLICY + """ @@ -317,13 +367,21 @@ Part_UpdateCheckSuppressedStartHour=Hour in a day that start to suppress update check Part_UpdateCheckSuppressedStartMin=Minute in hour that starts to suppress update check Part_UpdateCheckSuppressedDurationMin=Number of minutes to suppress update check each day -Part_DisableAllAutoUpdateChecks=Disable all auto-update checks (not recommended) +Part_DisableAllAutoUpdateChecks=Disable all periodic network traffic (not recommended) Part_ProxyMode=Choose how to specify proxy server settings Part_ProxyServer=Address or URL of proxy server Part_ProxyPacUrl=URL to a proxy .pac file +Part_InstallPolicy=Policy Part_UpdatePolicy=Policy +Part_TargetChannel=Target Channel Part_TargetVersionPrefix=Target version prefix +Name_InstallsEnabled=Always allow Installs (recommended) +Name_InstallsEnabledMachineOnly=Always allow Machine-Wide Installs, but not Per-User Installs +Name_InstallsDisabled=Installs disabled +Name_ForceInstallsMachine=Force Installs (Machine-Wide) +Name_ForceInstallsUser=Force Installs (Per-User) + Name_UpdatesEnabled=""" + UPDATES_ENABLED + """ (recommended) Name_ManualUpdatesOnly=""" + MANUAL_UPDATES_ONLY + """ Name_AutomaticUpdatesOnly=""" + AUTOMATIC_UPDATES_ONLY + """ @@ -346,6 +404,9 @@ # pylint: disable-msg=C6310 # pylint: disable-msg=C6013 +ADM_DOMAIN_REQUIREMENT_EN = """\ +This policy is available only on Windows instances that are joined to a Microsoft Active Directory domain.""" + # "application's" should be preceded by a different word in different contexts. # The word is specified by replacing the $PreApplicationWord$ token. STRINGS_UPDATE_POLICY_OPTIONS = """\ @@ -365,17 +426,17 @@ HORIZONTAL_RULE + """ Explain_Preferences=General policies for Google Update. -Explain_AutoUpdateCheckPeriod=Minimum number of minutes between automatic update checks. +Explain_AutoUpdateCheckPeriod=Minimum number of minutes between automatic update checks.\\n\\nSet this policy to the value 0 to disable all periodic network traffic by Google Update. This is not recommended, as it prevents Google Update itself from receiving stability and security updates.\\n\\nThe "Update policy override default" and per-application "Update policy override" settings should be used to manage application updates rather than this setting.\\n\\n%(domain_requirement)s -Explain_UpdateCheckSuppressedPeriod=If this setting is enabled, update checks will be suppressed during each day starting from Hour:Minute for a period of Duration (in minutes). Duration does not account for daylight savings time. So for instance, if the start time is 22:00, and with a duration of 480 minutes, the updates will be suppressed for 8 hours regardless of whether daylight savings time changes happen in between. +Explain_UpdateCheckSuppressedPeriod=If this setting is enabled, update checks will be suppressed during each day starting from Hour:Minute for a period of Duration (in minutes). Duration does not account for daylight savings time. So for instance, if the start time is 22:00, and with a duration of 480 minutes, the updates will be suppressed for 8 hours regardless of whether daylight savings time changes happen in between.\\n\\n%(domain_requirement)s -Explain_DownloadPreference=If enabled, the Google Update server will attempt to provide cache-friendly URLs for update payloads in its responses. +Explain_DownloadPreference=If enabled, the Google Update server will attempt to provide cache-friendly URLs for update payloads in its responses.\\n\\n%(domain_requirement)s -Explain_ProxyMode=Allows you to specify the proxy server used by Google Update.\\n\\nIf you choose to never use a proxy server and always connect directly, all other options are ignored.\\n\\nIf you choose to use system proxy settings or auto detect the proxy server, all other options are ignored.\\n\\nIf you choose fixed server proxy mode, you can specify further options in 'Address or URL of proxy server'.\\n\\nIf you choose to use a .pac proxy script, you must specify the URL to the script in 'URL to a proxy .pac file'. -Explain_ProxyServer=You can specify the URL of the proxy server here.\\n\\nThis policy only takes effect if you have selected manual proxy settings at 'Choose how to specify proxy server settings'. -Explain_ProxyPacUrl=You can specify a URL to a proxy .pac file here.\\n\\nThis policy only takes effect if you have selected manual proxy settings at 'Choose how to specify proxy server settings'. +Explain_ProxyMode=Allows you to specify the proxy server used by Google Update.\\n\\nIf you choose to never use a proxy server and always connect directly, all other options are ignored.\\n\\nIf you choose to use system proxy settings or auto detect the proxy server, all other options are ignored.\\n\\nIf you choose fixed server proxy mode, you can specify further options in 'Address or URL of proxy server'.\\n\\nIf you choose to use a .pac proxy script, you must specify the URL to the script in 'URL to a proxy .pac file.'\\n\\n%(domain_requirement)s +Explain_ProxyServer=You can specify the URL of the proxy server here.\\n\\nThis policy only takes effect if you have selected manual proxy settings at 'Choose how to specify proxy server settings'.\\n\\n%(domain_requirement)s +Explain_ProxyPacUrl=You can specify a URL to a proxy .pac file here.\\n\\nThis policy only takes effect if you have selected manual proxy settings at 'Choose how to specify proxy server settings'.\\n\\n%(domain_requirement)s -""" + +""" % {"domain_requirement": ADM_DOMAIN_REQUIREMENT_EN} + HORIZONTAL_RULE + '; ' + APPLICATIONS_CATEGORY + '\n' + HORIZONTAL_RULE + """ @@ -384,32 +445,50 @@ Explain_DefaultAllowInstallation=Specifies the default behavior for whether Google software can be installed using Google Update/Google Installer.\\ \\n\\nCan be overridden by the \"""" + ALLOW_INSTALLATION_POLICY + """\" for individual applications.\\ - \\n\\nOnly affects installation of Google software using Google Update/Google Installer. Cannot prevent running the application installer directly or installation of Google software that does not use Google Update/Google Installer for installation. + \\n\\nOnly affects installation of Google software using Google Update/Google Installer. Cannot prevent running the application installer directly or installation of Google software that does not use Google Update/Google Installer for installation.\\ + \\n\\n%(domain_requirement)s Explain_DefaultUpdatePolicy=Specifies the default policy for software updates from Google.\\ - \\n\\nCan be overridden by the \"""" + UPDATE_POLICY + """\" for individual applications.\\ + \\n\\nCan be overridden by the \"""" \ + % {"domain_requirement": ADM_DOMAIN_REQUIREMENT_EN} + +UPDATE_POLICY + """\" for individual applications.\\ """ + STRINGS_UPDATE_POLICY_OPTIONS.replace('$PreApplicationWord$', 'each') + """\\ \\n\\nOnly affects updates for Google software that uses Google Update for updates. Does not prevent auto-updates of Google software that does not use Google Update for updates.\\ \\n\\nUpdates for Google Update are not affected by this setting; Google Update will continue to update itself while it is installed.\\ - \\n\\nWARNING: Disabing updates will also prevent updates of any new Google applications released in the future, possibly including dependencies for future versions of installed applications. + \\n\\nWARNING: Disabing updates will also prevent updates of any new Google applications released in the future, possibly including dependencies for future versions of installed applications.\\ + \\n\\n%(domain_requirement)s -""" + +""" % {"domain_requirement": ADM_DOMAIN_REQUIREMENT_EN} + HORIZONTAL_RULE + '; Individual Applications\n' + HORIZONTAL_RULE) DEFAULT_ROLLBACK_DISCLAIMER = """This policy is meant to serve as temporary measure when Enterprise Administrators need to downgrade for business reasons. To ensure users are protected by the latest security updates, the most recent version should be used. When versions are downgraded to older versions, there could be incompatibilities.""" +FORCE_INSTALLS_MACHINE_EXPLAIN = """Force Installs (Machine-Wide): Allows Deploying $AppName$ to all machines where Google Update is pre-installed. Requires Google Update 1.3.36.82 or higher.\\n\\n""" +FORCE_INSTALLS_USER_EXPLAIN = """Force Installs (Per-User): Allows Deploying $AppName$ on a Per-User basis to all machines where Google Update is pre-installed Per-User. Requires Google Update 1.3.36.82 or higher.\\n\\n""" + STRINGS_APP_POLICY_EXPLANATIONS_TEMPLATE = (""" ; $AppName$ Explain_Install$AppLegalId$=Specifies whether $AppName$ can be installed using Google Update/Google Installer.\\ - \\n\\nIf this policy is not configured, $AppName$ can be installed as specified by \"""" + DEFAULT_ALLOW_INSTALLATION_POLICY + """\". + \\n\\nIf this policy is not configured, $AppName$ can be installed as specified by \"""" + DEFAULT_ALLOW_INSTALLATION_POLICY + """\".\\ + \\n\\n$ForceInstallsExplain$%(domain_requirement)s Explain_AutoUpdate$AppLegalId$=Specifies how Google Update handles available $AppName$ updates from Google.\\ - \\n\\nIf this policy is not configured, Google Update handles available updates as specified by \"""" + DEFAULT_UPDATE_POLICY + """\".\\ + \\n\\nIf this policy is not configured, Google Update handles available updates as specified by \"""" % {"domain_requirement": ADM_DOMAIN_REQUIREMENT_EN} + DEFAULT_UPDATE_POLICY + """\".\\ """ + -STRINGS_UPDATE_POLICY_OPTIONS.replace('$PreApplicationWord$', 'the') + '$AppUpdateExplainExtra$') + """ +STRINGS_UPDATE_POLICY_OPTIONS.replace('$PreApplicationWord$', 'the') + '$AppUpdateExplainExtra$') + """\\ + \\n\\n%(domain_requirement)s + +Explain_TargetChannel$AppLegalId$=Specifies which Channel $AppName$ should be updated to.\\ + \\n\\nWhen this policy is enabled, the app will be updated to the Channel with this policy value.\\ + \\n\\nSome examples:\\n\\ + 1) Not configured: app will be updated to the latest version available in the default Channel for the app.\\n\\ + 2) Policy value is set to "stable": the app will be updated to the latest stable version.\\n\\ + 2) Policy value is set to "beta": the app will be updated to the latest beta version.\\n\\ + 3) Policy value is "dev": the app will be updated to the latest dev version.\\n\\ + \\n\\n%(domain_requirement)s Explain_TargetVersionPrefix$AppLegalId$=Specifies which version $AppName$ should be updated to.\\ \\n\\nWhen this policy is enabled, the app will be updated to the version prefixed with this policy value.\\ @@ -417,14 +496,15 @@ 1) Not configured: app will be updated to the latest version available.\\n\\ 2) Policy value is set to "55.": the app will be updated to any minor version of 55 (e.g., 55.24.34 or 55.60.2).\\n\\ 3) Policy value is "55.2.": the app will be updated to any minor version of 55.2 (e.g., 55.2.34 or 55.2.2).\\n\\ - 4) Policy value is "55.24.34": the app will be updated to this specific version only. + 4) Policy value is "55.24.34": the app will be updated to this specific version only.\\ + \\n\\n%(domain_requirement)s -Explain_RollbackToTargetVersion$AppLegalId$=Specifies that Google Update should roll installations of $AppName$ back to the version indicated by \"""" + TARGET_VERSION_POLICY + """\".\\ - \\n\\nThis policy setting has no effect unless \"""" + TARGET_VERSION_POLICY + """\" is set.\\ - \\n\\nIf this policy is not configured or is disabled, installs that have a version higher than that specified by \"""" + TARGET_VERSION_POLICY + """\" will be left as-is.\\ - \\n\\nIf this policy is enabled, installs that have a version higher than that specified by \"""" + TARGET_VERSION_POLICY + """\" will be downgraded to the highest available version that matches the target version.\\ - \\n\\n$AppRollbackDisclaimer$ -""" +Explain_RollbackToTargetVersion$AppLegalId$=Specifies that Google Update should roll installations of $AppName$ back if the client has a higher version than that available.\\ + \\n\\nIf this policy is not configured or is disabled, installs that have a version higher than that available will be left as-is. This could be the case if \"""" % {"domain_requirement": ADM_DOMAIN_REQUIREMENT_EN} + TARGET_CHANNEL_POLICY + """\" is set to a Channel with a lower version, if \"""" + TARGET_VERSION_POLICY + """\" matches a lower version on the Channel, or if a user had installed a higher version.\\ + \\n\\nIf this policy is enabled, installs that have a version higher than that available will be downgraded to the highest available version, respecting any configured target Channel and target version.\\ + \\n\\n$AppRollbackDisclaimer$\\ + \\n\\n%(domain_requirement)s +""" % {"domain_requirement": ADM_DOMAIN_REQUIREMENT_EN} # pylint: enable-msg=C6013 # pylint: enable-msg=C6310 @@ -514,12 +594,26 @@ def _WriteTemplateForApp(template, app): strings. """ - (app_name, app_guid, update_explain_extra, rollback_disclaimer) = app + (app_name, app_guid, update_explain_extra, rollback_disclaimer, + force_install_machine, force_install_user) = app + if not rollback_disclaimer: rollback_disclaimer = DEFAULT_ROLLBACK_DISCLAIMER rollback_disclaimer = string.replace(rollback_disclaimer, '\n', '\\n') + + force_installs = '' + force_installs_explain = '' + if force_install_machine: + force_installs += INSTALL_POLICY_FORCE_INSTALL_MACHINE + force_installs_explain += FORCE_INSTALLS_MACHINE_EXPLAIN + if force_install_user: + force_installs += INSTALL_POLICY_FORCE_INSTALL_USER + force_installs_explain += FORCE_INSTALLS_USER_EXPLAIN + # pylint: disable-msg=C6004 - return (template.replace('$AppName$', app_name) + return (template.replace('$ForceInstalls$', force_installs) + .replace('$ForceInstallsExplain$', force_installs_explain) + .replace('$AppName$', app_name) .replace('$AppLegalId$', _CreateLegalIdentifier(app_name)) .replace('$AppGuid$', app_guid) .replace('$AppUpdateExplainExtra$', update_explain_extra) @@ -585,11 +679,15 @@ def WriteGroupPolicyTemplate(target_path, apps): ('Google Test Foo', '{D6B08267-B440-4c85-9F79-E195E80D9937}', ' Check http://www.google.com/test_foo/.', - 'Disclaimer'), + 'Disclaimer', + True, + True), (u'Google User Test Foo\u00a9\u00ae\u2122', '{104844D6-7DDA-460b-89F0-FBF8AFDD0A67}', ' Check http://www.google.com/user_test_foo/.', - ''), + '', + False, + True), ] TEST_GOLD_FILENAME = 'test_gold.adm' TEST_OUTPUT_FILENAME = 'test_out.adm' diff --git a/omaha/enterprise/generate_group_policy_template_admx.py b/omaha/enterprise/generate_group_policy_template_admx.py index 4f36930f9..91c51ad48 100644 --- a/omaha/enterprise/generate_group_policy_template_admx.py +++ b/omaha/enterprise/generate_group_policy_template_admx.py @@ -52,6 +52,8 @@ displayName="$(string.Sup_GoogleUpdate1_3_33_5)" /> + ''' @@ -198,11 +200,29 @@ displayName="$(string.Pol_DefaultAllowInstallation)" explainText="$(string.Explain_DefaultAllowInstallation)" presentation="$(presentation.Pol_DefaultAllowInstallation)" - key="%(RootPolicyKey)s" valueName="InstallDefault"> + key="%(RootPolicyKey)s"> - - + + + + + + + + + + + + + + + + + + + ''' +INSTALL_POLICY_FORCE_INSTALL_MACHINE = r''' + + + + + ''' + +INSTALL_POLICY_FORCE_INSTALL_USER = r''' + + + + + ''' + ADMX_APP_POLICY_TEMPLATE = '''\ + key="%(RootPolicyKey)s"> - - + + + + + + + + + + + + + + + + + %(ForceInstalls)s + + + + + + + + + - Minutes between update checks @@ -580,16 +684,28 @@ def _GeneratePolicies(apps): - + + Policy + Policy - + + Policy + Policy - \ + + + + + + + @@ -641,6 +757,14 @@ def GenerateGroupPolicyTemplateAdml(apps): if not rollback_disclaimer: rollback_disclaimer = ADML_DEFAULT_ROLLBACK_DISCLAIMER + force_install_machine = app[4] + force_install_user = app[5] + force_installs_explain = '' + if force_install_machine: + force_installs_explain += FORCE_INSTALLS_MACHINE_EXPLAIN % app_name + if force_install_user: + force_installs_explain += FORCE_INSTALLS_USER_EXPLAIN % app_name + app_category = ('Cat_' + app_legal_id, app_name) string_definition_list.append(app_category) @@ -649,7 +773,13 @@ def GenerateGroupPolicyTemplateAdml(apps): 'Specifies whether %s can be installed using Google Update/Google ' 'Installer.\n\n' 'If this policy is not configured, %s can be installed as specified ' - 'by "Allow installation default".' % (app_name, app_name)) + 'by "Allow installation default".\n\n' + '%s' + '%s' % (app_name, + app_name, + force_installs_explain, + ADML_DOMAIN_REQUIREMENT_EN)) + string_definition_list.append(app_install_policy_explanation) app_auto_update_policy_explanation = ( @@ -670,10 +800,27 @@ def GenerateGroupPolicyTemplateAdml(apps): 'If you select manual updates, you should periodically check for ' 'updates using the application\'s manual update mechanism if ' 'available. If you disable updates, you should periodically check ' - 'for updates and distribute them to users.%s' % - (app_name, app_additional_help_msg)) + 'for updates and distribute them to users.%s\n\n' + '%s' % + (app_name, app_additional_help_msg, ADML_DOMAIN_REQUIREMENT_EN)) string_definition_list.append(app_auto_update_policy_explanation) + app_target_channel_explanation = ( + 'Explain_TargetChannel' + app_legal_id, + 'Specifies which Channel %s should be updated to.\n\n' + 'When this policy is enabled, the app will be updated to the Channel ' + 'with this policy value.\n\nSome examples:\n' + '1) Not configured: app will be updated to the latest version ' + 'available in the default Channel for the app.\n' + '2) Policy value is set to "stable": the app will be updated to the ' + 'latest stable version.\n' + '2) Policy value is set to "beta": the app will be updated to the ' + 'latest beta version.\n' + '2) Policy value is set to "dev": the app will be updated to the ' + 'latest dev version.\n' + '%s' % (app_name, ADML_DOMAIN_REQUIREMENT_EN)) + string_definition_list.append(app_target_channel_explanation) + app_target_version_prefix_explanation = ( 'Explain_TargetVersionPrefix' + app_legal_id, 'Specifies which version %s should be updated to.\n\n' @@ -686,22 +833,25 @@ def GenerateGroupPolicyTemplateAdml(apps): '3) Policy value is "55.2.": the app will be updated to any minor ' 'version of 55.2 (e.g., 55.2.34 or 55.2.2).\n' '4) Policy value is "55.24.34": the app will be updated to this ' - 'specific version only.' % app_name) + 'specific version only.\n\n' + '%s' % (app_name, ADML_DOMAIN_REQUIREMENT_EN)) string_definition_list.append(app_target_version_prefix_explanation) app_rollback_to_target_version_explanation = ( 'Explain_RollbackToTargetVersion' + app_legal_id, - 'Specifies that Google Update should roll installations of %s back to ' - 'the version indicated by "Target version prefix override".\n\n' - 'This policy setting has no effect unless "Target version prefix ' - 'override" is set.\n\n' + 'Specifies that Google Update should roll installations of %s back if ' + 'the client has a higher version than that available.\n\n' 'If this policy is not configured or is disabled, installs that have a ' - 'version higher than that specified by "Target version prefix ' - 'override" will be left as-is.\n\n' + 'version higher than that available will be left as-is. This could be ' + 'the case if "Target channel override" is set to a Channel with a ' + 'lower version, if "Target version prefix override" matches a lower ' + 'version on the Channel, or if a user had installed a higher ' + 'version.\n\n' 'If this policy is enabled, installs that have a version higher than ' - 'that specified by "Target version prefix override" will be downgraded ' - 'to the highest available version that matches the target version.\n\n' - '%s' % (app_name, rollback_disclaimer)) + 'that available will be downgraded to the highest available version, ' + 'respecting any configured target Channel and target version.\n\n' + '%s\n\n' + '%s' % (app_name, rollback_disclaimer, ADML_DOMAIN_REQUIREMENT_EN)) string_definition_list.append(app_rollback_to_target_version_explanation) app_resource_strings = [] @@ -766,13 +916,18 @@ def WriteGroupPolicyTemplateAdml(target_path, apps): # Run a unit test when the module is run directly. if __name__ == '__main__': TEST_APPS = [ - ('Google Test Foo', '{D6B08267-B440-4c85-9F79-E195E80D9937}', + ('Google Test Foo', + '{D6B08267-B440-4c85-9F79-E195E80D9937}', ' Check http://www.google.com/test_foo/.', - 'Disclaimer'), + 'Disclaimer', + True, + True), (u'Google User Test Foo\u00a9\u00ae\u2122', '{104844D6-7DDA-460b-89F0-FBF8AFDD0A67}', ' Check http://www.google.com/user_test_foo/.', - ''), + '', + False, + True), ] module_dir = os.path.abspath(os.path.dirname(__file__)) gold_path = os.path.join(module_dir, 'test_gold.admx') diff --git a/omaha/enterprise/installer/build.scons b/omaha/enterprise/installer/build.scons index e030e77d6..94bc0f2c1 100644 --- a/omaha/enterprise/installer/build.scons +++ b/omaha/enterprise/installer/build.scons @@ -108,6 +108,7 @@ def _BuildSampleForTest(): build_enterprise_installer.BuildEnterpriseInstaller( env, + 'Google', product_name, product_version, product_guid, product_custom_params, product_installer_path, diff --git a/omaha/enterprise/installer/build_enterprise_installer.py b/omaha/enterprise/installer/build_enterprise_installer.py index de4f632ea..b31299d48 100644 --- a/omaha/enterprise/installer/build_enterprise_installer.py +++ b/omaha/enterprise/installer/build_enterprise_installer.py @@ -34,6 +34,7 @@ def BuildGoogleUpdateFragment(env, metainstaller_path, + company_name, product_name, product_version, product_guid, @@ -48,6 +49,7 @@ def BuildGoogleUpdateFragment(env, Args: env: environment to build with metainstaller_path: path to the Omaha metainstaller to include + company_name: name of the company the fragment is being built for product_name: name of the product the fragment is being built for product_version: product version to be installed product_guid: Omaha application ID of the product the fragment is being @@ -81,6 +83,7 @@ def BuildGoogleUpdateFragment(env, msi_product_version, product_version, '"%s"' % product_guid, + '"%s"' % company_name, product_custom_params=product_custom_params, metainstaller_path=str(env.File(metainstaller_path).abspath)) @@ -225,6 +228,7 @@ def _BuildMsiForExe(env, def BuildEnterpriseInstaller(env, + company_name, product_name, product_version, product_guid, @@ -245,6 +249,7 @@ def BuildEnterpriseInstaller(env, Args: env: environment to build with + company_name: name of the company for whom the product is being built product_name: name of the product being built product_version: product version to be installed product_guid: product's Omaha application ID @@ -280,6 +285,7 @@ def BuildEnterpriseInstaller(env, google_update_wixobj_output = BuildGoogleUpdateFragment( env, metainstaller_path, + company_name, product_name, product_version, product_guid, diff --git a/omaha/enterprise/installer/custom_actions/build.scons b/omaha/enterprise/installer/custom_actions/build.scons index 85e95253f..2872630da 100644 --- a/omaha/enterprise/installer/custom_actions/build.scons +++ b/omaha/enterprise/installer/custom_actions/build.scons @@ -71,7 +71,7 @@ def BuildShowErrorActionDll(omaha_version_info): source=dll_inputs, ) - signed_dll = dll_env.DualSignedBinary( + signed_dll = dll_env.SignedBinary( target='%s%s.dll' % (prefix, signed_target_name), source=unsigned_dll, ) diff --git a/omaha/enterprise/installer/custom_actions/msi_tag_extractor.cc b/omaha/enterprise/installer/custom_actions/msi_tag_extractor.cc index c333b0355..a19766c72 100644 --- a/omaha/enterprise/installer/custom_actions/msi_tag_extractor.cc +++ b/omaha/enterprise/installer/custom_actions/msi_tag_extractor.cc @@ -15,8 +15,8 @@ #include "omaha/enterprise/installer/custom_actions/msi_tag_extractor.h" -#include #include +#include #include @@ -24,23 +24,24 @@ namespace { // A fixed string serves as the magic number to identify whether it's the start // of a valid tag. -const char kTagMagicNumber[] = "Gact"; +const char kTagMagicNumber[] = "Gact2.0Omaha"; const size_t kMagicNumberLength = arraysize(kTagMagicNumber) - 1; const char kStringMapDelimeter = '&'; const char kKeyValueDelimeter = '='; -const DWORD kMaxTagStringLengthAllowed = 4096; +const DWORD kMaxTagStringLengthAllowed = 81920; // 80K -// The smallest meaningful tag is 'Gact00', indicating a zero-length payload. -const DWORD kMinTagLengthAllowed = static_cast( - kMagicNumberLength + sizeof(uint16)); // NOLINT +// The smallest meaningful tag is 'Gact2.0Omaha\0\0', indicating a zero-length +// payload. +const DWORD kMinTagLengthAllowed = + static_cast(kMagicNumberLength + sizeof(uint16)); const DWORD kMaxTagLength = kMaxTagStringLengthAllowed + kMinTagLengthAllowed; // Read a value from the given buffer. The layout is assumed to be // big-endian. Caller must make sure the buffer has enough length. -template +template T GetValueFromBuffer(const void* buffer) { const uint8* value_buffer = static_cast(buffer); T value = T(); @@ -52,9 +53,7 @@ T GetValueFromBuffer(const void* buffer) { return value; } -bool is_not_alnum(char c) { - return !isalnum(c); -} +bool is_not_alnum(char c) { return !isalnum(c); } bool IsValidTagKey(const std::string& str) { return find_if(str.begin(), str.end(), is_not_alnum) == str.end(); @@ -76,27 +75,26 @@ bool IsValidTagValue(const std::string& str) { namespace custom_action { -MsiTagExtractor::MsiTagExtractor() { -} +MsiTagExtractor::MsiTagExtractor() {} bool MsiTagExtractor::ReadTagFromFile(const wchar_t* filename) { HANDLE file_handle = NULL; file_handle = ::CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { return false; } std::vector tag_buffer; - bool result = ReadTagToBuffer(file_handle, &tag_buffer) && - ParseTagBuffer(tag_buffer); + bool result = + ReadTagToBuffer(file_handle, &tag_buffer) && ParseTagBuffer(tag_buffer); ::CloseHandle(file_handle); return result; } -// Load last 4K bytes from file, search for magic number 'Gact' in the +// Load last 80K bytes from file, search for magic number 'Gact2.0Omaha' in the // buffer. If found, assume that's the start of tag and continue parsing. bool MsiTagExtractor::ReadTagToBuffer(HANDLE file_handle, std::vector* tag) const { @@ -124,27 +122,24 @@ bool MsiTagExtractor::ReadTagToBuffer(HANDLE file_handle, // Add one extra zero at the end to prevent out-of-bounds reads. std::vector buffer(bytes_to_read + 1); DWORD num_bytes_read = 0; - if (!::ReadFile(file_handle, - &buffer[0], - bytes_to_read, - &num_bytes_read, + if (!::ReadFile(file_handle, &buffer[0], bytes_to_read, &num_bytes_read, NULL) || num_bytes_read != bytes_to_read) { return false; } - // Search for the magic number in the loaded buffer. - char* magic_number_pos = std::search(&buffer[0], - &buffer[bytes_to_read], - kTagMagicNumber, - kTagMagicNumber + kMagicNumberLength); + // Search for the last occurrence of the magic number in the loaded buffer. + auto magic_number_pos = + std::find_end(buffer.begin(), buffer.end(), kTagMagicNumber, + kTagMagicNumber + kMagicNumberLength); + // Make sure we found the magic number, and the buffer after it is no less // than the required minimum tag size (kMinTagLengthAllowed). - if (&buffer[bytes_to_read] - magic_number_pos < kMinTagLengthAllowed) { + if (std::distance(buffer.end(), magic_number_pos) < kMinTagLengthAllowed) { return false; } - tag->assign(magic_number_pos, &buffer[bytes_to_read]); + tag->assign(magic_number_pos, buffer.end()); return true; } @@ -167,21 +162,20 @@ bool MsiTagExtractor::ParseTagBuffer(const std::vector& tag_buffer) { return true; } -bool MsiTagExtractor::ParseMagicNumber( - const std::vector& tag_buffer, size_t* parse_position) const { +bool MsiTagExtractor::ParseMagicNumber(const std::vector& tag_buffer, + size_t* parse_position) const { if (tag_buffer.size() <= kMagicNumberLength + *parse_position) { return false; } - bool result = memcmp(&tag_buffer[*parse_position], - kTagMagicNumber, + bool result = memcmp(&tag_buffer[*parse_position], kTagMagicNumber, kMagicNumberLength) == 0; *parse_position += kMagicNumberLength; return result; } -uint16 MsiTagExtractor::ParseTagLength( - const std::vector& tag_buffer, size_t* parse_position) const { +uint16 MsiTagExtractor::ParseTagLength(const std::vector& tag_buffer, + size_t* parse_position) const { uint16 tag_length = GetValueFromBuffer(&tag_buffer[*parse_position]); *parse_position += sizeof(tag_length); return tag_length; @@ -192,8 +186,7 @@ uint16 MsiTagExtractor::ParseTagLength( // 2. All key/value must be alpha-numeric strings, while value can be empty. // Unrecognized tag will be ignored. void MsiTagExtractor::ParseSimpleAsciiStringMap( - const std::vector& tag_buffer, - size_t* parse_position, + const std::vector& tag_buffer, size_t* parse_position, size_t tag_length) { // Construct a string with at most |tag_length| characters, but stop at the // first '\0' if it comes before that. @@ -235,4 +228,3 @@ bool MsiTagExtractor::GetValue(const char* key, std::string* value) const { } } // namespace custom_action - diff --git a/omaha/enterprise/installer/custom_actions/msi_tag_extractor.h b/omaha/enterprise/installer/custom_actions/msi_tag_extractor.h index 6ac550ce1..32acdb4db 100644 --- a/omaha/enterprise/installer/custom_actions/msi_tag_extractor.h +++ b/omaha/enterprise/installer/custom_actions/msi_tag_extractor.h @@ -31,7 +31,7 @@ namespace custom_action { // // Tag specification: // - When reading multi-byte numbers, it's in big endian. -// - Tag area begins with magic number 'Gact'. +// - Tag area begins with magic number 'Gact2.0Omaha'. // - The next 2-bytes is tag string length. // - Then follows the real tag string. The string is in format of: // "key1=value1&key2=value2". Both the key and the value must be @@ -41,17 +41,21 @@ namespace custom_action { // +-------------------------------------+ // ~ .............................. ~ // | .............................. | -// | End of MSI file | End of raw MSI. +// | Other parts of MSI file | // +-------------------------------------+ -// | Magic number 'Gact' | Tag starts -// | Tag string length | +// | Start of the certificate | +// ~ .............................. ~ +// ~ .............................. ~ +// | Magic number 'Gact2.0Omaha' | Tag starts +// | Tag length (2 bytes in big-endian)) | // | tag string | // +-------------------------------------+ // -// A real example (MSI file tagged with 'brand=CDCD&key2=Test'): +// A real example (an MSI file tagged with 'brand=CDCD&key2=Test'): // +-----------------------------------------------------------------+ -// | G a c t \0 024 b r a n d = C D C D | -// | & k e y 2 = T e s t | +// | G a c t 2 . 0 O m a h a 0x0 0x14 b r | +// | a n d = C D C D & k e y 2 = T e | +// | s t | // +-----------------------------------------------------------------+ class MsiTagExtractor { public: diff --git a/omaha/enterprise/installer/custom_actions/msi_tag_extractor_test.cc b/omaha/enterprise/installer/custom_actions/msi_tag_extractor_test.cc index 926b3ec9d..932ffc463 100644 --- a/omaha/enterprise/installer/custom_actions/msi_tag_extractor_test.cc +++ b/omaha/enterprise/installer/custom_actions/msi_tag_extractor_test.cc @@ -13,89 +13,44 @@ // limitations under the License. // ======================================================================== +#include "omaha/enterprise/installer/custom_actions/msi_tag_extractor.h" + #include #include + #include #include + #include "omaha/base/app_util.h" #include "omaha/base/file.h" #include "omaha/base/utils.h" #include "omaha/testing/unit_test.h" -#include "omaha/enterprise/installer/custom_actions/msi_tag_extractor.h" - namespace omaha { -namespace { - -const TCHAR kSourceMsi[] = _T("GoogleUpdateHelper.msi"); -const TCHAR kTaggedMsi[] = _T("tagged_GoogleUpdateHelper.msi"); - -} // namesapce - class MsiTagExtractorTest : public testing::Test { protected: virtual void SetUp() { unittest_file_path_ = app_util::GetModuleDirectory(NULL); EXPECT_TRUE(::PathAppend(CStrBuf(unittest_file_path_, MAX_PATH), - _T("..\\staging\\unittest_support"))); + _T("unittest_support\\tagged_msi"))); EXPECT_TRUE(File::Exists(unittest_file_path_)); } - virtual void TearDown() { - File::Remove(GetTaggedMsiPath()); - } - - CString GetSourceMsiPath() const { - CString source_msi_path(unittest_file_path_); - EXPECT_TRUE(::PathAppend(CStrBuf(source_msi_path, MAX_PATH), kSourceMsi)); - EXPECT_TRUE(File::Exists(source_msi_path)); - return source_msi_path; - } - - CString GetTaggedMsiPath() const { + CString GetMsiFilePath(const CString& file_name) const { CString tagged_msi_path(unittest_file_path_); - EXPECT_TRUE(::PathAppend(CStrBuf(tagged_msi_path, MAX_PATH), kTaggedMsi)); + EXPECT_TRUE(::PathAppend(CStrBuf(tagged_msi_path, MAX_PATH), file_name)); + EXPECT_TRUE(File::Exists(tagged_msi_path)); return tagged_msi_path; } - HRESULT CreateMsiFileWithTag(const uint8* tag_buffer, - size_t size) const { - const CString& source_msi(GetSourceMsiPath()); - const CString& output_msi(GetTaggedMsiPath()); - std::ifstream in; - std::ofstream out; - - in.open(source_msi, std::ifstream::in | std::ifstream::binary); - out.open(output_msi, std::ofstream::out | std::ofstream::binary); - if (!in.is_open() || !out.is_open()) { - return E_FAIL; - } - - // Copy MSI package first. - out << in.rdbuf(); - - if (tag_buffer) { - out.write(reinterpret_cast(tag_buffer), - static_cast(size)); - } - return S_OK; - } - private: CString unittest_file_path_; }; TEST_F(MsiTagExtractorTest, ValidTag) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 10, // Tag string length. - 'b', 'r', 'a', 'n', 'd', '=', 'Q', 'A', 'Q', 'A', // BRAND=QAQA. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-brand-only.msi's tag:BRAND=QAQA + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-brand-only.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); @@ -110,15 +65,8 @@ TEST_F(MsiTagExtractorTest, ValidTag) { } TEST_F(MsiTagExtractorTest, ValidTag_AmpersandAtEnd) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 11, // Tag string length. - 'b', 'r', 'a', 'n', 'd', '=', 'Q', 'A', 'Q', 'A', '&', 0, // brand=QAQA. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-ampersand-ending.msi's tag: BRAND=QAQA& + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-ampersand-ending.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); @@ -128,54 +76,13 @@ TEST_F(MsiTagExtractorTest, ValidTag_AmpersandAtEnd) { } TEST_F(MsiTagExtractorTest, MultiValidTags) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 206, // Tag string length. - - // appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}& - 'a', 'p', 'p', 'g', 'u', 'i', 'd', '=', - '{', '8', 'A', '6', '9', 'D', '3', '4', '5', '-', - 'D', '5', '6', '4', '-', '4', '6', '3', 'C', '-', - 'A', 'F', 'F', '1', '-', - 'A', '6', '9', 'D', '9', 'E', '5', '3', '0', 'F', '9', '6', '}', '&', - - // iid={2D8C18E9-8D3A-4EFC-6D61-AE23E3530EA2}& - 'i', 'i', 'd', '=', - '{', '2', 'D', '8', 'C', '1', '8', 'E', '9', '-', - '8', 'D', '3', 'A', '-', '4', 'E', 'F', 'C', '-', - '6', 'D', '6', '1', '-', - 'A', 'E', '2', '3', 'E', '3', '5', '3', '0', 'E', 'A', '2', '}', '&', - - // lang=en& - 'l', 'a', 'n', 'g', '=', 'e', 'n', '&', - - // browser=4& - 'b', 'r', 'o', 'w', 's', 'e', 'r', '=', '4', '&', - - // usagestats=0& - 'u', 's', 'a', 'g', 'e', 's', 't', 'a', 't', 's', '=', '0', '&', - - // appname=Google%20Chrome& - 'a', 'p', 'p', 'n', 'a', 'm', 'e', '=', 'G', 'o', 'o', 'g', 'l', 'e', - '%', '2', '0', 'C', 'h', 'r', 'o', 'm', 'e', '&', - - // needsadmin=prefers& - 'n', 'e', 'e', 'd', 's', 'a', 'd', 'm', 'i', 'n', '=', - 'p', 'r', 'e', 'f', 'e', 'r', 's', '&', - - // brand=CHMB& - 'b', 'r', 'a', 'n', 'd', '=', 'C', 'H', 'M', 'B', '&', - - // installdataindex=defaultbrowser - 'i', 'n', 's', 't', 'a', 'l', 'l', 'd', 'a', 't', 'a', 'i', 'n', 'd', 'e', - 'x', '=', 'd', 'e', 'f', 'a', 'u', 'l', 't', 'b', 'r', 'o', 'w', 's', 'e', - 'r', - - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // File GUH-multiple.msi has tag: + // appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}& + // iid={2D8C18E9-8D3A-4EFC-6D61-AE23E3530EA2}& + // lang=en&browser=4&usagestats=0&appname=Google%20Chrome& + // needsadmin=prefers&brand=CHMB& + // installdataindex=defaultbrowser + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-multiple.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); @@ -209,188 +116,92 @@ TEST_F(MsiTagExtractorTest, MultiValidTags) { } TEST_F(MsiTagExtractorTest, EmptyKey) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 18, // Tag string length. - '=', 'V', 'a', 'l', 'u', 'e', '&', // =Value - 'B', 'R', 'A', 'N', 'D', '=', 'Q', 'A', 'Q', 'A', 0, // BRAND=QAQA. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-empty-key.msi's tag: =value&BRAND=QAQA + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-empty-key.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_TRUE(tag_extractor.GetValue("BRAND", &brand_code)); + EXPECT_TRUE(tag_extractor.GetValue("brand", &brand_code)); EXPECT_EQ(brand_code.compare("QAQA"), 0); } TEST_F(MsiTagExtractorTest, EmptyValue) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 7, // Tag string length. - 'B', 'R', 'A', 'N', 'D', '=', 0, // BRAND=. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-empty-value.msi's tag: BRAND= + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-empty-value.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_TRUE(tag_extractor.GetValue("BRAND", &brand_code)); + EXPECT_TRUE(tag_extractor.GetValue("brand", &brand_code)); EXPECT_TRUE(brand_code.empty()); } TEST_F(MsiTagExtractorTest, NoTagString) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 0, // Tag string length. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-empty-tag.msi's tag:(empty string) + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-empty-tag.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BRAND", &brand_code)); + EXPECT_FALSE(tag_extractor.GetValue("brand", &brand_code)); } TEST_F(MsiTagExtractorTest, NoTag) { - EXPECT_SUCCEEDED(CreateMsiFileWithTag(NULL, 0)); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // The original MSI is in parent folder. + std::wstring tagged_msi(GetMsiFilePath(_T("..\\") MAIN_EXE_BASE_NAME _T("Helper.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_FALSE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); } TEST_F(MsiTagExtractorTest, InvalidMagicNumber) { - const uint8 tag[] = { - '_', 'a', 'c', 't', // Invalid magic number. - 0, 10, // Tag string length. - 'B', 'R', 'A', 'N', 'D', '=', 'Q', 'A', 'Q', 'A', // BRAND=QAQA. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-invalid-marker.msi's has invalid magic number "Gact2.0Foo". + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-invalid-marker.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_FALSE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); - std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BRAND", &brand_code)); + EXPECT_FALSE(tag_extractor.GetValue("brand", &brand_code)); } -TEST_F(MsiTagExtractorTest, InvalidTagLength) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 88, // Invalid tag length. - 'B', 'R', 'A', 'N', 'D', '=', 'Q', 'A', 'Q', 'A', 0, // BRAND=QAQA. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); - custom_action::MsiTagExtractor tag_extractor; - EXPECT_FALSE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); - - std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BRAND", &brand_code)); -} - TEST_F(MsiTagExtractorTest, InvalidTagCharacterInKey) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 10, // Tag string length. - 'B', 'R', '*', 'N', 'D', '=', 'Q', 'A', 'Q', 'A', // BR*ND=QAQA. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-invalid-key.msi's has invalid charaters in the tag key. + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-invalid-key.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BR*ND", &brand_code)); + EXPECT_FALSE(tag_extractor.GetValue("br*nd", &brand_code)); } TEST_F(MsiTagExtractorTest, InvalidTagCharacterInValue) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 10, // Tag string length. - 'B', 'R', 'A', 'N', 'D', '=', 'Q', 'A', '*', 'A', // BRAND=QA*A. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-invalid-value.msi's has invalid charaters in the tag value. + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-invalid-value.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BRAND", &brand_code)); + EXPECT_FALSE(tag_extractor.GetValue("brand", &brand_code)); } TEST_F(MsiTagExtractorTest, InvalidTagFormat) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 5, // Tag string length. - 'B', 'R', 'A', 'N', 'D', // No '='. - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-bad-format.msi's has invalid tag format. + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-bad-format.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BRAND", &brand_code)); + EXPECT_FALSE(tag_extractor.GetValue("brand", &brand_code)); } TEST_F(MsiTagExtractorTest, InvalidTagFormat2) { - const uint8 tag[] = { - 'G', 'a', 'c', 't', // magic number. - 0, 24, // Tag string length. - '=', '=', '=', '=', '=', '=', '=', '&', - '=', '=', '=', '=', '=', '=', '=', '&', - '&', '&', '=', '&', '&', '&', '&', '0', - 0, 0, 0, 0, // 4 bytes of 0s. - }; - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - - std::wstring tagged_msi(GetTaggedMsiPath()); + // GUH-bad-format2.msi's has invalid tag format. + std::wstring tagged_msi(GetMsiFilePath(_T("GUH-bad-format.msi"))); custom_action::MsiTagExtractor tag_extractor; EXPECT_TRUE(tag_extractor.ReadTagFromFile(tagged_msi.c_str())); std::string brand_code; - EXPECT_FALSE(tag_extractor.GetValue("BRAND", &brand_code)); -} - -// Verifies that tag extractor won't crash with garbage tag. -TEST_F(MsiTagExtractorTest, RandomTag) { - const int kNumPassesToRun = 10; - srand(static_cast(time(NULL))); - - uint8 tag[1024]; - for (int i = 0; i < kNumPassesToRun; ++i) { - // Fill tag with random data. - for (int j = 0; j < arraysize(tag); ++j) { - tag[j] = static_cast(rand() % 0x100); // NOLINT - } - - EXPECT_SUCCEEDED(CreateMsiFileWithTag(tag, arraysize(tag))); - std::wstring tagged_msi(GetTaggedMsiPath()); - custom_action::MsiTagExtractor tag_extractor; - tag_extractor.ReadTagFromFile(tagged_msi.c_str()); - } + EXPECT_FALSE(tag_extractor.GetValue("brand", &brand_code)); } } // namespace omaha diff --git a/omaha/enterprise/installer/custom_actions/show_error_action.cc b/omaha/enterprise/installer/custom_actions/show_error_action.cc index bfc2912b8..6bde2c687 100644 --- a/omaha/enterprise/installer/custom_actions/show_error_action.cc +++ b/omaha/enterprise/installer/custom_actions/show_error_action.cc @@ -31,13 +31,12 @@ #include "omaha/enterprise/installer/custom_actions/msi_custom_action.h" -#define SOFTWARE_GOOGLE_UPDATE L"Software\\Google\\Update" +#define SOFTWARE_GOOGLE_UPDATE L"Software\\" PATH_COMPANY_NAME_ANSI "\\Update" #define SOFTWARE_GOOGLE_UPDATE_CLIENTSTATE \ SOFTWARE_GOOGLE_UPDATE L"\\ClientState" namespace { -const int kGuidStringLength = 38; // 128/4 + 4 dashes + 2 braces. const DWORD kInstallerResultFailedCustomError = 1; const wchar_t kPropertyCustomActionData[] = L"CustomActionData"; const wchar_t kRegKeyClientState[] = SOFTWARE_GOOGLE_UPDATE_CLIENTSTATE; diff --git a/omaha/enterprise/installer/enterprise_installer.wxs.xml b/omaha/enterprise/installer/enterprise_installer.wxs.xml index deecce2e3..be73c7e09 100644 --- a/omaha/enterprise/installer/enterprise_installer.wxs.xml +++ b/omaha/enterprise/installer/enterprise_installer.wxs.xml @@ -1,5 +1,6 @@ + @@ -57,14 +58,14 @@ diff --git a/omaha/enterprise/installer/enterprise_standalone_installer.wxs.xml b/omaha/enterprise/installer/enterprise_standalone_installer.wxs.xml index 937697b85..bd9f32e92 100644 --- a/omaha/enterprise/installer/enterprise_standalone_installer.wxs.xml +++ b/omaha/enterprise/installer/enterprise_standalone_installer.wxs.xml @@ -1,8 +1,9 @@ + - + @@ -69,11 +70,11 @@ - false + diff --git a/omaha/enterprise/installer/google_update_installer_fragment.wxs.xml b/omaha/enterprise/installer/google_update_installer_fragment.wxs.xml index 72f019fe8..ff6c975df 100644 --- a/omaha/enterprise/installer/google_update_installer_fragment.wxs.xml +++ b/omaha/enterprise/installer/google_update_installer_fragment.wxs.xml @@ -1,6 +1,6 @@ - + @@ -25,7 +25,7 @@ - diff --git a/omaha/enterprise/installer/test/test_setup.cc b/omaha/enterprise/installer/test/test_setup.cc index 407ec6cda..bd24b377a 100644 --- a/omaha/enterprise/installer/test/test_setup.cc +++ b/omaha/enterprise/installer/test/test_setup.cc @@ -15,12 +15,12 @@ // // Author: grt // -// A dummy app installer that does nothing more than write error information to +// An app installer that does nothing more than write error information to // the registry as per the Google Update Installer Result API and return 1. #include -#define GOOGLE_UPDATE_KEY L"SOFTWARE\\Google\\Update" +#define GOOGLE_UPDATE_KEY L"SOFTWARE\\" PATH_COMPANY_NAME_ANSI "\\Update" #define TEST_SETUP_APP_GUID L"{665BDD8E-F40C-4384-A9C6-CA3CD5665C83}" namespace { diff --git a/omaha/enterprise/installer/utils.py b/omaha/enterprise/installer/utils.py index 01c3b0679..e768a6390 100644 --- a/omaha/enterprise/installer/utils.py +++ b/omaha/enterprise/installer/utils.py @@ -91,35 +91,38 @@ def GenerateNameBasedGUID(namespace, name): # Generate 128 unique bits. mymd5 = md5.new() mymd5.update(namespace + name) - md5_hash = mymd5.digest() + md5_hex_digest = mymd5.hexdigest() + md5_hex_digits = [md5_hex_digest[x:x+2].upper() + for x in range(0, len(md5_hex_digest), 2)] # Set various reserved bits to make this a valid GUID. # "Set the four most significant bits (bits 12 through 15) of the # time_hi_and_version field to the appropriate 4-bit version number # from Section 4.1.3." - version = ord(md5_hash[6]) + version = int(md5_hex_digits[6], 16) version = 0x30 | (version & 0x0f) # "Set the two most significant bits (bits 6 and 7) of the # clock_seq_hi_and_reserved to zero and one, respectively." - clock_seq_hi_and_reserved = ord(md5_hash[8]) + clock_seq_hi_and_reserved = int(md5_hex_digits[8], 16) clock_seq_hi_and_reserved = 0x80 | (clock_seq_hi_and_reserved & 0x3f) return ( - '%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X' % ( - ord(md5_hash[0]), ord(md5_hash[1]), ord(md5_hash[2]), - ord(md5_hash[3]), - ord(md5_hash[4]), ord(md5_hash[5]), - version, ord(md5_hash[7]), - clock_seq_hi_and_reserved, ord(md5_hash[9]), - ord(md5_hash[10]), ord(md5_hash[11]), ord(md5_hash[12]), - ord(md5_hash[13]), ord(md5_hash[14]), ord(md5_hash[15]))) + '%s-%s-%02X%s-%02X%s-%s' % ( + ''.join(md5_hex_digits[0:4]), + ''.join(md5_hex_digits[4:6]), + version, + md5_hex_digits[7], + clock_seq_hi_and_reserved, + md5_hex_digits[9], + ''.join(md5_hex_digits[10:]))) def GetWixCandleFlags( product_name, product_name_legal_identifier, msi_product_version, product_version, product_guid, + company_name=None, custom_action_dll_path=None, product_uninstaller_additional_args=None, msi_product_id=None, @@ -143,6 +146,9 @@ def GetWixCandleFlags( '-dProductGuid=' + product_guid, ] + if company_name: + flags.append('-dCompanyName=' + company_name) + if custom_action_dll_path: flags.append('-dMsiInstallerCADll=' + custom_action_dll_path) diff --git a/omaha/enterprise/test_gold.adm b/omaha/enterprise/test_gold.adm index 250ac99ec..90e43da0c 100644 Binary files a/omaha/enterprise/test_gold.adm and b/omaha/enterprise/test_gold.adm differ diff --git a/omaha/enterprise/test_gold.adml b/omaha/enterprise/test_gold.adml index ef99061be..5d59fa97a 100644 Binary files a/omaha/enterprise/test_gold.adml and b/omaha/enterprise/test_gold.adml differ diff --git a/omaha/enterprise/test_gold.admx b/omaha/enterprise/test_gold.admx index 0e9f6db54..9a404a941 100644 Binary files a/omaha/enterprise/test_gold.admx and b/omaha/enterprise/test_gold.admx differ diff --git a/omaha/google_update/build.scons b/omaha/google_update/build.scons index 6968bdb04..822bdc9b4 100644 --- a/omaha/google_update/build.scons +++ b/omaha/google_update/build.scons @@ -94,7 +94,7 @@ unsigned_exe_output = exe_env.ComponentProgram( source=exe_inputs, ) -sign_output = env.DualSignedBinary( +sign_output = env.SignedBinary( target='GoogleUpdate_signed.exe', source=unsigned_exe_output, ) diff --git a/omaha/goopdate/app.cc b/omaha/goopdate/app.cc index b74c1f3d6..1e50dc311 100644 --- a/omaha/goopdate/app.cc +++ b/omaha/goopdate/app.cc @@ -352,11 +352,11 @@ STDMETHODIMP App::get_currentState(IDispatch** current_state) { &download_time_remaining_ms, &next_download_retry_time); if (SUCCEEDED(hr)) { - VERIFY1(SUCCEEDED(AppManager::Instance()->WriteDownloadProgress( + VERIFY_SUCCEEDED(AppManager::Instance()->WriteDownloadProgress( *this, bytes_downloaded, total_bytes_to_download, - download_time_remaining_ms))); + download_time_remaining_ms)); } break; case STATE_WAITING_TO_INSTALL: @@ -366,8 +366,8 @@ STDMETHODIMP App::get_currentState(IDispatch** current_state) { // we ignore any read errors. GetInstallProgress(&install_progress_percentage, &install_time_remaining_ms); - VERIFY1(SUCCEEDED(AppManager::Instance()->WriteInstallProgress( - *this, install_progress_percentage, install_time_remaining_ms))); + VERIFY_SUCCEEDED(AppManager::Instance()->WriteInstallProgress( + *this, install_progress_percentage, install_time_remaining_ms)); break; case STATE_INSTALL_COMPLETE: install_progress_percentage = 100; @@ -378,8 +378,8 @@ STDMETHODIMP App::get_currentState(IDispatch** current_state) { ASSERT1(completion_result_ == PingEvent::EVENT_RESULT_SUCCESS || completion_result_ == PingEvent::EVENT_RESULT_SUCCESS_REBOOT); - VERIFY1(SUCCEEDED(AppManager::Instance()->WriteInstallProgress( - *this, install_progress_percentage, install_time_remaining_ms))); + VERIFY_SUCCEEDED(AppManager::Instance()->WriteInstallProgress( + *this, install_progress_percentage, install_time_remaining_ms)); break; case STATE_PAUSED: break; @@ -399,7 +399,7 @@ STDMETHODIMP App::get_currentState(IDispatch** current_state) { break; } - VERIFY1(SUCCEEDED(AppManager::Instance()->WriteStateValue(*this, state()))); + VERIFY_SUCCEEDED(AppManager::Instance()->WriteStateValue(*this, state())); if (FAILED(hr)) { return hr; @@ -538,6 +538,16 @@ HRESULT App::GetInstallProgress(LONG* install_progress_percentage, return S_OK; } +HRESULT App::ResetInstallProgress() { + ASSERT1(model()->IsLockedByCaller()); + + const CString base_key_name(ConfigManager::Instance()->registry_client_state( + app_bundle_->is_machine())); + const CString app_id_key_name(AppendRegKeyPath(base_key_name, + app_guid_string())); + return RegKey::DeleteValue(app_id_key_name, kRegValueInstallerProgress); +} + AppBundle* App::app_bundle() { __mutexScope(model()->lock()); return app_bundle_; @@ -623,7 +633,6 @@ CurrentState App::state() const { bool App::is_update() const { __mutexScope(model()->lock()); - ASSERT1(current_version_->version().IsEmpty() != is_update_); return is_update_; } @@ -907,7 +916,7 @@ void App::AddPingEvent(const PingEventPtr& ping_event) { CORE_LOG(L3, (_T("[ping event added][%s]"), ping_event->ToString())); - VERIFY1(SUCCEEDED(app_bundle()->BuildAndPersistPing())); + VERIFY_SUCCEEDED(app_bundle()->BuildAndPersistPing()); } HRESULT App::CheckGroupPolicy() const { @@ -917,8 +926,8 @@ HRESULT App::CheckGroupPolicy() const { if (!ConfigManager::Instance()->CanUpdateApp( app_guid_, !app_bundle_->is_auto_update())) { - if (ConfigManager::GetEffectivePolicyForAppUpdates(app_guid_) == - kPolicyAutomaticUpdatesOnly) { + if (ConfigManager::Instance()->GetEffectivePolicyForAppUpdates( + app_guid_, NULL) == kPolicyAutomaticUpdatesOnly) { // This return code allows Omaha clients to show a message indicating // Manual Updates are disabled, but Automatic Updates are enabled. This // is to reassure end-users that administrators have enabled automatic @@ -929,7 +938,8 @@ HRESULT App::CheckGroupPolicy() const { return GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY; } } else { - if (!ConfigManager::Instance()->CanInstallApp(app_guid_)) { + if (!ConfigManager::Instance()->CanInstallApp(app_guid_, + app_bundle_->is_machine())) { return GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY; } } @@ -937,16 +947,23 @@ HRESULT App::CheckGroupPolicy() const { return S_OK; } +CString App::GetTargetChannel() const { + __mutexScope(model()->lock()); + + return ConfigManager::Instance()->GetTargetChannel(app_guid_, NULL); +} + bool App::IsRollbackToTargetVersionAllowed() const { __mutexScope(model()->lock()); - return ConfigManager::IsRollbackToTargetVersionAllowed(app_guid_); + return ConfigManager::Instance()->IsRollbackToTargetVersionAllowed(app_guid_, + NULL); } CString App::GetTargetVersionPrefix() const { __mutexScope(model()->lock()); - return ConfigManager::GetTargetVersionPrefix(app_guid_); + return ConfigManager::Instance()->GetTargetVersionPrefix(app_guid_, NULL); } void App::UpdateNumBytesDownloaded(uint64 num_bytes) { @@ -1041,7 +1058,7 @@ void App::QueueUpdateCheck() { if (is_eula_accepted_ == TRISTATE_NONE) { CString message; StringFormatter formatter(app_bundle_->display_language()); - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); Error(ErrorContext(GOOPDATE_E_CALL_UNEXPECTED), message); } diff --git a/omaha/goopdate/app.h b/omaha/goopdate/app.h index e03a9bba7..463b79085 100644 --- a/omaha/goopdate/app.h +++ b/omaha/goopdate/app.h @@ -331,6 +331,10 @@ class App : public ModelObject { // disabled by Group Policy. HRESULT CheckGroupPolicy() const; + // Returns the target channel for the app, if the machine is joined to a + // domain and has the corresponding policy set. + CString GetTargetChannel() const; + // Returns whether the RollbackToTargetVersion policy has been set for the // app. bool IsRollbackToTargetVersionAllowed() const; @@ -380,6 +384,9 @@ class App : public ModelObject { // Returns the time interval between update is available and user cancel. int GetTimeSinceDownloadStart() const; + // Deletes "InstallerProgress" under Google\\Update\\ClientState\\{AppID}. + HRESULT ResetInstallProgress(); + private: // TODO(omaha): accessing directly the data members bypasses locking. Review // the places where members are accessed by friends and check the caller locks @@ -461,7 +468,7 @@ class App : public ModelObject { int day_of_install_; int day_of_last_response_; - // This value is stored in ClientState but not currently populated from there. + // This value is stored in ClientState. Tristate usage_stats_enable_; // This value is stored by the clients in one of several registry locations. diff --git a/omaha/goopdate/app_bundle.cc b/omaha/goopdate/app_bundle.cc index 061f9bcd6..95c282103 100644 --- a/omaha/goopdate/app_bundle.cc +++ b/omaha/goopdate/app_bundle.cc @@ -57,7 +57,7 @@ AppBundle::AppBundle(bool is_machine, Model* model) CORE_LOG(L3, (_T("[AppBundle::AppBundle][0x%p]"), this)); app_bundle_state_.reset(new fsm::AppBundleStateInit); - VERIFY1(SUCCEEDED(GetGuid(&request_id_))); + VERIFY_SUCCEEDED(GetGuid(&request_id_)); } AppBundle::~AppBundle() { @@ -78,6 +78,10 @@ AppBundle::~AppBundle() { delete apps_[i]; } + for (size_t i = 0; i < uninstalled_apps_.size(); ++i) { + delete uninstalled_apps_[i]; + } + // If the thread running this AppBundle does not exit before the // NetworkConfigManager::DeleteInstance() happens in GoopdateImpl::Main, the // update_check_client_ destructor will crash. Resetting here explicitly. @@ -206,13 +210,13 @@ void AppBundle::BuildPing(std::unique_ptr* my_ping) { for (size_t i = 0; i != apps_.size(); ++i) { if (apps_[i]->is_eula_accepted()) { - ping->BuildRequest(apps_[i], false); + ping->BuildRequest(apps_[i]); } } for (size_t i = 0; i != uninstalled_apps_.size(); ++i) { if (uninstalled_apps_[i]->is_eula_accepted()) { - ping->BuildRequest(uninstalled_apps_[i], false); + ping->BuildRequest(uninstalled_apps_[i]); } } @@ -248,30 +252,37 @@ HRESULT AppBundle::SendPingEventsAsync() { CAccessToken token; if (impersonation_token()) { - VERIFY1(SUCCEEDED(DuplicateTokenIntoCurrentProcess(::GetCurrentProcess(), + VERIFY_SUCCEEDED(DuplicateTokenIntoCurrentProcess(::GetCurrentProcess(), impersonation_token(), - &token))); - } - - typedef StaticThreadPoolCallBack1 CB; - Gate send_ping_events_gate; - std::unique_ptr callback(new CB(&AppBundle::SendPingEvents, - internal::SendPingEventsParameters( - ping.get(), - token.GetHandle(), - &send_ping_events_gate))); - HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), - COINIT_MULTITHREADED, - WT_EXECUTELONGFUNCTION); + &token)); + } + + // We Lock the ATL Module here since we want the process to stick around + // until the newly created threadpool item below starts and also completes + // execution. The corresponding Unlock of the ATL Module is done at the end + // of the threadpool proc. + _pAtlModule->Lock(); + ScopeGuard atl_module_unlock = MakeObjGuard(*_pAtlModule, + &CAtlModule::Unlock); + + using Callback = + StaticThreadPoolCallBack1; + HRESULT hr = Goopdate::Instance().QueueUserWorkItem( + std::make_unique( + &AppBundle::SendPingEvents, + internal::SendPingEventsParameters( + ping.get(), + token.GetHandle())), + COINIT_MULTITHREADED, + WT_EXECUTELONGFUNCTION); if (FAILED(hr)) { CORE_LOG(LE, (_T("[QueueUserWorkItem failed][0x%x]"), hr)); return hr; } + atl_module_unlock.Dismiss(); ping.release(); token.Detach(); - callback.release(); - VERIFY1(send_ping_events_gate.Wait(INFINITE)); return S_OK; } @@ -279,15 +290,12 @@ HRESULT AppBundle::SendPingEventsAsync() { void AppBundle::SendPingEvents(internal::SendPingEventsParameters params) { CORE_LOG(L3, (_T("[AppBundle::SendPingEvents]"))); - _pAtlModule->Lock(); ON_SCOPE_EXIT_OBJ(*_pAtlModule, &CAtlModule::Unlock); std::unique_ptr ping(params.ping); scoped_handle impersonation_token(params.impersonation_token); scoped_impersonation impersonate_user(get(impersonation_token)); - VERIFY1(params.send_ping_events_gate->Open()); - // TODO(Omaha): Add sample to metric_ping_succeeded_ms or // metric_ping_failed_ms based on the result of Send(). HRESULT hr = ping->Send(false); @@ -748,7 +756,7 @@ void AppBundle::CompleteAsyncCall() { ASSERT1(is_pending_non_blocking_call()); - VERIFY1(SUCCEEDED(app_bundle_state_->CompleteAsyncCall(this))); + VERIFY_SUCCEEDED(app_bundle_state_->CompleteAsyncCall(this)); user_work_item_ = NULL; } diff --git a/omaha/goopdate/app_bundle.h b/omaha/goopdate/app_bundle.h index 980314780..7ab54e163 100644 --- a/omaha/goopdate/app_bundle.h +++ b/omaha/goopdate/app_bundle.h @@ -29,6 +29,7 @@ #include "goopdate/omaha3_idl.h" #include "omaha/base/constants.h" #include "omaha/base/debug.h" +#include "omaha/base/scope_guard.h" #include "omaha/base/synchronized.h" #include "omaha/common/ping.h" #include "omaha/goopdate/com_wrapper_creator.h" @@ -58,17 +59,14 @@ namespace internal { struct SendPingEventsParameters { public: - SendPingEventsParameters(Ping* p, HANDLE token, Gate* gate) + SendPingEventsParameters(Ping* p, HANDLE token) : ping(p), - impersonation_token(token), - send_ping_events_gate(gate) { + impersonation_token(token) { ASSERT1(ping); - ASSERT1(gate); } Ping* ping; HANDLE impersonation_token; - Gate* send_ping_events_gate; }; } // namespace internal @@ -235,7 +233,7 @@ class AppBundle // The current non-blocking command object if any of them is executing. // The class only checks whether the pointer is NULL to determine if a // non-blocking call is pending. We use a pointer because it can be useful - // for debugging. + // for debugging. The object is not owned by this class. UserWorkItem* user_work_item_; std::unique_ptr update_check_client_; diff --git a/omaha/goopdate/app_bundle_state.h b/omaha/goopdate/app_bundle_state.h index 38ecb2511..b08cc84d7 100644 --- a/omaha/goopdate/app_bundle_state.h +++ b/omaha/goopdate/app_bundle_state.h @@ -83,7 +83,7 @@ class AppBundleState { explicit AppBundleState(BundleState state) : state_(state) {} // These functions provide pass-through access to private AppBundle members. - // TODO(omaha): remove paranoid asserts and implement inline. + // TODO(omaha): remove asserts and implement inline. void AddAppToBundle(AppBundle* app_bundle, App* app); bool IsPendingNonBlockingCall(AppBundle* app_bundle); diff --git a/omaha/goopdate/app_bundle_state_init.cc b/omaha/goopdate/app_bundle_state_init.cc index 178fd671f..2ae87564e 100644 --- a/omaha/goopdate/app_bundle_state_init.cc +++ b/omaha/goopdate/app_bundle_state_init.cc @@ -113,8 +113,8 @@ HRESULT AppBundleStateInit::Initialize(AppBundle* app_bundle) { ASSERT1(!app_bundle->update_check_client_.get()); CString update_check_url; - VERIFY1(SUCCEEDED( - ConfigManager::Instance()->GetUpdateCheckUrl(&update_check_url))); + VERIFY_SUCCEEDED( + ConfigManager::Instance()->GetUpdateCheckUrl(&update_check_url)); auto web_service_client = std::make_unique( app_bundle->is_machine()); hr = web_service_client->Initialize(update_check_url, HeadersVector(), true); diff --git a/omaha/goopdate/app_bundle_state_initialized.cc b/omaha/goopdate/app_bundle_state_initialized.cc index 00fddc1bb..03ec70772 100644 --- a/omaha/goopdate/app_bundle_state_initialized.cc +++ b/omaha/goopdate/app_bundle_state_initialized.cc @@ -106,6 +106,19 @@ HRESULT AppBundleStateInitialized::CreateApp(AppBundle* app_bundle, return hr; } + // For fresh installs, for User installs only, we reload the policies with the + // Group Policy critical section. + // For Machine installs, we do not reload policies for fresh installs, because + // there is the possibility that we are running under GPO, and because GPO + // takes the critical section itself, this can result in a deadlock. Also, + // Machine installs require Admin, and an Admin can bypass Group Policy if + // they wish to, so we are ok with sacrificing some accuracy in terms of + // policy correctness in return for not having deadlocks. + if (!has_reloaded_policy_managers_ && !app_bundle->is_machine()) { + has_reloaded_policy_managers_ = + SUCCEEDED(ConfigManager::Instance()->LoadPolicies(true)); + } + // When overinstalling, we want the install age for the existing install, so // explicitly get it here. These are the only values read from the registry // for installs. @@ -143,6 +156,13 @@ HRESULT AppBundleStateInitialized::CreateInstalledApp(AppBundle* app_bundle, return hr; } + // Updates do not run under GPO, so it is safe to reload the policies with the + // Group Policy critical section for both User and Machine updates. + if (!has_reloaded_policy_managers_) { + has_reloaded_policy_managers_ = + SUCCEEDED(ConfigManager::Instance()->LoadPolicies(true)); + } + return S_OK; } @@ -193,6 +213,13 @@ HRESULT AppBundleStateInitialized::CreateAllInstalledApps( } } + // Updates do not run under GPO, so it is safe to reload the policies with the + // Group Policy critical section for both User and Machine updates. + if (!has_reloaded_policy_managers_) { + has_reloaded_policy_managers_ = + SUCCEEDED(ConfigManager::Instance()->LoadPolicies(true)); + } + return S_OK; } diff --git a/omaha/goopdate/app_bundle_state_initialized.h b/omaha/goopdate/app_bundle_state_initialized.h index 01265e841..964d60a9b 100644 --- a/omaha/goopdate/app_bundle_state_initialized.h +++ b/omaha/goopdate/app_bundle_state_initialized.h @@ -28,7 +28,8 @@ class AppBundleStateInitialized : public AppBundleState { AppBundleStateInitialized() : AppBundleState(STATE_INITIALIZED), has_new_app_(false), - has_installed_app_(false) {} + has_installed_app_(false), + has_reloaded_policy_managers_(false) {} virtual ~AppBundleStateInitialized() {} virtual HRESULT Stop(AppBundle* app_bundle); @@ -60,6 +61,8 @@ class AppBundleStateInitialized : public AppBundleState { bool has_new_app_; bool has_installed_app_; + bool has_reloaded_policy_managers_; + DISALLOW_COPY_AND_ASSIGN(AppBundleStateInitialized); }; diff --git a/omaha/goopdate/app_bundle_unittest.cc b/omaha/goopdate/app_bundle_unittest.cc index aa6aefdb3..7ab674bd2 100644 --- a/omaha/goopdate/app_bundle_unittest.cc +++ b/omaha/goopdate/app_bundle_unittest.cc @@ -143,7 +143,6 @@ const TCHAR* const kGuid1 = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}"); const TCHAR* const kGuid2 = _T("{A979ACBD-1F55-4b12-A35F-4DBCA5A7CCB8}"); const TCHAR* const kGuid3 = _T("{661045C5-4429-4140-BC48-8CEA241D1DEF}"); const TCHAR* const kGuid4 = _T("{AAFA1CF9-E94F-42e6-A899-4CD27F37D5A7}"); -const TCHAR* const kGuid5 = _T("{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}"); const TCHAR* const kGuid6 = _T("{F3F2CFD4-5F98-4bf0-ABB0-BEEEA46C62B4}"); const TCHAR* const kGuid7 = _T("{6FD2272F-8583-4bbd-895A-E65F8003FC7B}"); @@ -153,7 +152,7 @@ void InitializeRegistryForTest(bool is_machine) { ConfigManager::Instance()->registry_client_state(is_machine)); } -class DummyUserWorkItem : public UserWorkItem { +class TestUserWorkItem : public UserWorkItem { private: virtual void DoProcess() {} }; @@ -403,23 +402,23 @@ class AppBundlePopulatedRegistryTest : public AppBundleInitializedTest { EXPECT_SUCCEEDED(ResourceManager::Create( is_machine_, app_util::GetCurrentModuleDirectory(), _T("en"))); - dummy_app_bundle_for_expected_apps_ = model_->CreateAppBundle(is_machine_); - ASSERT_TRUE(dummy_app_bundle_for_expected_apps_.get()); + test_app_bundle_for_expected_apps_ = model_->CreateAppBundle(is_machine_); + ASSERT_TRUE(test_app_bundle_for_expected_apps_.get()); // TODO(omaha): Address with the TODO in AppBundleInitializedTest::SetUp. if (is_machine_) { - SetAppBundleStateForUnitTest(dummy_app_bundle_for_expected_apps_.get(), + SetAppBundleStateForUnitTest(test_app_bundle_for_expected_apps_.get(), new fsm::AppBundleStateInitialized); } else { - EXPECT_SUCCEEDED(dummy_app_bundle_for_expected_apps_->put_displayName( + EXPECT_SUCCEEDED(test_app_bundle_for_expected_apps_->put_displayName( CComBSTR(_T("My Bundle")))); - EXPECT_SUCCEEDED(dummy_app_bundle_for_expected_apps_->put_displayLanguage( + EXPECT_SUCCEEDED(test_app_bundle_for_expected_apps_->put_displayLanguage( CComBSTR(_T("en")))); - EXPECT_SUCCEEDED(dummy_app_bundle_for_expected_apps_->initialize()); + EXPECT_SUCCEEDED(test_app_bundle_for_expected_apps_->initialize()); } } virtual void TearDown() { - dummy_app_bundle_for_expected_apps_.reset(); + test_app_bundle_for_expected_apps_.reset(); ResourceManager::Delete(); @@ -432,7 +431,7 @@ class AppBundlePopulatedRegistryTest : public AppBundleInitializedTest { App* CreateExpectedApp(const TCHAR* app_id) { App* app = NULL; EXPECT_SUCCEEDED( - dummy_app_bundle_for_expected_apps_->createApp(CComBSTR(app_id), &app)); + test_app_bundle_for_expected_apps_->createApp(CComBSTR(app_id), &app)); ASSERT1(app); return app; } @@ -462,7 +461,7 @@ class AppBundlePopulatedRegistryTest : public AppBundleInitializedTest { AppManager::Instance()->ReadAppInstallTimeDiff(opposite_hive_app2); } - std::shared_ptr dummy_app_bundle_for_expected_apps_; + std::shared_ptr test_app_bundle_for_expected_apps_; LLock lock_; private: @@ -625,9 +624,9 @@ TEST_F(AppBundleInitializedUserTest, createApp_SameAppTwice) { } TEST_F(AppBundleInitializedUserTest, createApp_AfterUpdateCheck) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app0_created = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created)); @@ -659,9 +658,9 @@ TEST_F(AppBundleInitializedUserTest, checkForUpdate_NoApps) { // Does not verify the update check occurs. TEST_F(AppBundleInitializedUserTest, checkForUpdate_OneApp) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); @@ -673,9 +672,9 @@ TEST_F(AppBundleInitializedUserTest, checkForUpdate_OneApp) { // Does not verify the update check occurs. TEST_F(AppBundleInitializedUserTest, checkForUpdate_TwoApps) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app0 = NULL; App* app1 = NULL; @@ -688,9 +687,9 @@ TEST_F(AppBundleInitializedUserTest, checkForUpdate_TwoApps) { } TEST_F(AppBundleInitializedUserTest, checkForUpdate_Twice) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); @@ -702,9 +701,9 @@ TEST_F(AppBundleInitializedUserTest, checkForUpdate_Twice) { } TEST_F(AppBundleInitializedUserTest, checkForUpdate_WhileBundleIsBusy) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); @@ -717,9 +716,9 @@ TEST_F(AppBundleInitializedUserTest, checkForUpdate_WhileBundleIsBusy) { // Does not verify the update check occurs. TEST_F(AppBundleInitializedMachineTest, checkForUpdate_OneApp) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); @@ -735,15 +734,15 @@ TEST_F(AppBundleInitializedUserTest, download_WithoutUpdateCheck) { // The AppBundle does not prevent this, but the apps may enter the error state. TEST_F(AppBundleInitializedUserTest, download_AfterInstall) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -761,14 +760,14 @@ TEST_F(AppBundleInitializedUserTest, download_AfterInstall) { // The AppBundle does not prevent this, but the apps may enter the error state. TEST_F(AppBundleInitializedUserTest, download_Twice) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAsync(_)) .Times(2) - .WillRepeatedly(SetWorkItem(&dummy_work_item)); + .WillRepeatedly(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -786,9 +785,9 @@ TEST_F(AppBundleInitializedUserTest, download_Twice) { // Simulates the update check still in progress when download() is called. TEST_F(AppBundleInitializedUserTest, download_WhileBundleIsBusy) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); @@ -802,14 +801,14 @@ TEST_F(AppBundleInitializedUserTest, download_WhileBundleIsBusy) { // The AppBundle does not prevent this, but the apps may enter the error state. TEST_F(AppBundleInitializedMachineTest, download_Twice) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAsync(_)) .Times(2) - .WillRepeatedly(SetWorkItem(&dummy_work_item)); + .WillRepeatedly(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -830,13 +829,13 @@ TEST_F(AppBundleInitializedUserTest, install_WithoutUpdateCheck) { } TEST_F(AppBundleInitializedUserTest, install_WithoutDownload) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -851,15 +850,15 @@ TEST_F(AppBundleInitializedUserTest, install_WithoutDownload) { } TEST_F(AppBundleInitializedUserTest, install_AfterDownload) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -877,14 +876,14 @@ TEST_F(AppBundleInitializedUserTest, install_AfterDownload) { // The AppBundle does not prevent this, but the apps may enter the error state. TEST_F(AppBundleInitializedUserTest, install_Twice) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) .Times(2) - .WillRepeatedly(SetWorkItem(&dummy_work_item)); + .WillRepeatedly(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -904,13 +903,13 @@ TEST_F(AppBundleInitializedUserTest, install_Twice) { // fails. We could move this to AppBundleWrapper, but we would need to expose // some functions to AppBundleWrapper. TEST_F(AppBundleInitializedMachineTest, DISABLED_install_WithoutDownload) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -925,15 +924,15 @@ TEST_F(AppBundleInitializedMachineTest, DISABLED_install_WithoutDownload) { } TEST_F(AppBundleInitializedMachineTest, DISABLED_install_AfterDownload) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; { - ::testing::InSequence dummy; + ::testing::InSequence seq; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); } App* app = NULL; @@ -951,9 +950,9 @@ TEST_F(AppBundleInitializedMachineTest, DISABLED_install_AfterDownload) { // Simulates the update check still in progress when install is called. TEST_F(AppBundleInitializedUserTest, install_WhileBundleIsBusy) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); @@ -1255,9 +1254,9 @@ TEST_F(AppBundlePopulatedRegistryUserTest, &expected_app1, &expected_app2); - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); App* app0_created = NULL; EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1), @@ -1388,6 +1387,7 @@ TEST_F(AppBundlePopulatedRegistryUserTest, EXPECT_NE(REG_SZ, value_type); App* invalid_pv_app = CreateExpectedApp(kInvalidPvTypeAppId); invalid_pv_app->current_version()->set_version(kInvalidPvDwordAsString); + invalid_pv_app->set_days_since_last_active_ping(-1); invalid_pv_app->set_days_since_last_roll_call(-1); SetDisplayName(kDefaultAppName, invalid_pv_app); @@ -1456,9 +1456,9 @@ TEST_F(AppBundlePopulatedRegistryUserTest, EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey( AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid4))); // Avoid assert. - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps()); @@ -1703,8 +1703,8 @@ class AppBundleStateBusyUserTest : public AppBundleStateUserTest { virtual void SetUp() { AppBundleStateUserTest::SetUp(); - DummyUserWorkItem dummy_work_item; - app_bundle_->set_user_work_item(&dummy_work_item); + TestUserWorkItem test_work_item; + app_bundle_->set_user_work_item(&test_work_item); SetAppBundleStateForUnitTest(app_bundle_.get(), new fsm::AppBundleStateBusy); @@ -1940,9 +1940,9 @@ TEST_F(AppBundleStateInitializedUserTest, createAllInstalledApps) { TEST_F(AppBundleStateInitializedUserTest, checkForUpdate) { App* app = NULL; EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app)); - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, CheckForUpdateAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_SUCCEEDED(app_bundle_->checkForUpdate()); EXPECT_EQ(STATE_BUSY, GetBundleState()); @@ -1965,9 +1965,9 @@ TEST_F(AppBundleStateInitializedUserTest, updateAllApps) { EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey( AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid1))); // Avoid assert. - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, UpdateAllAppsAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_SUCCEEDED(app_bundle_->updateAllApps()); EXPECT_EQ(STATE_BUSY, GetBundleState()); @@ -2227,9 +2227,9 @@ TEST_F(AppBundleStateReadyUserTest, checkForUpdate) { } TEST_F(AppBundleStateReadyUserTest, download) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, DownloadAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_SUCCEEDED(app_bundle_->download()); EXPECT_EQ(STATE_BUSY, GetBundleState()); @@ -2238,9 +2238,9 @@ TEST_F(AppBundleStateReadyUserTest, download) { } TEST_F(AppBundleStateReadyUserTest, install) { - DummyUserWorkItem dummy_work_item; + TestUserWorkItem test_work_item; EXPECT_CALL(*worker_, DownloadAndInstallAsync(_)) - .WillOnce(SetWorkItem(&dummy_work_item)); + .WillOnce(SetWorkItem(&test_work_item)); EXPECT_SUCCEEDED(app_bundle_->install()); EXPECT_EQ(STATE_BUSY, GetBundleState()); @@ -2395,8 +2395,8 @@ TEST_F(AppBundleStatePausedUserTest, resume_Succeeds_AsyncCallNotCompleted) { } TEST_F(AppBundleStatePausedUserTest, resume_Succeeds_AsyncCallCompleted) { - DummyUserWorkItem dummy_work_item; - app_bundle_->set_user_work_item(&dummy_work_item); + TestUserWorkItem test_work_item; + app_bundle_->set_user_work_item(&test_work_item); app_bundle_->CompleteAsyncCall(); EXPECT_CALL(*worker_, Resume(app_bundle_.get())) @@ -2415,8 +2415,8 @@ TEST_F(AppBundleStatePausedUserTest, resume_Fails_AsyncCallNotCompleted) { } TEST_F(AppBundleStatePausedUserTest, resume_Fails_AsyncCallCompleted) { - DummyUserWorkItem dummy_work_item; - app_bundle_->set_user_work_item(&dummy_work_item); + TestUserWorkItem test_work_item; + app_bundle_->set_user_work_item(&test_work_item); app_bundle_->CompleteAsyncCall(); EXPECT_CALL(*worker_, Resume(app_bundle_.get())) @@ -2449,8 +2449,8 @@ TEST_F(AppBundleStatePausedUserTest, get_currentState) { // Remains Paused until resumed. TEST_F(AppBundleStatePausedUserTest, CompleteAsyncCall) { - DummyUserWorkItem dummy_work_item; - app_bundle_->set_user_work_item(&dummy_work_item); + TestUserWorkItem test_work_item; + app_bundle_->set_user_work_item(&test_work_item); SetAppBundleStateForUnitTest(app_bundle_.get(), new fsm::AppBundleStatePaused); @@ -2573,8 +2573,8 @@ TEST_F(AppBundleStateStoppedUserTest, get_currentState) { } TEST_F(AppBundleStateStoppedUserTest, CompleteAsyncCall) { - DummyUserWorkItem dummy_work_item; - app_bundle_->set_user_work_item(&dummy_work_item); + TestUserWorkItem test_work_item; + app_bundle_->set_user_work_item(&test_work_item); app_bundle_->CompleteAsyncCall(); diff --git a/omaha/goopdate/app_command_ping_delegate.cc b/omaha/goopdate/app_command_ping_delegate.cc index d81071cd9..46bc1e8a0 100644 --- a/omaha/goopdate/app_command_ping_delegate.cc +++ b/omaha/goopdate/app_command_ping_delegate.cc @@ -71,7 +71,7 @@ void AppCommandPingDelegate::SendPing(PingEvent::Types type, PingEventPtr ping_event( new PingEvent(type, result, error_code, reporting_id_)); - Ping ping(is_machine_, session_id_, kCmdLineInstallSource_OneClick); + Ping ping(is_machine_, session_id_, CString()); std::vector apps; apps.push_back(app_guid_); ping.LoadAppDataFromRegistry(apps); diff --git a/omaha/goopdate/app_command_unittest.cc b/omaha/goopdate/app_command_unittest.cc index 22c57bc07..0a2994147 100644 --- a/omaha/goopdate/app_command_unittest.cc +++ b/omaha/goopdate/app_command_unittest.cc @@ -34,9 +34,6 @@ namespace omaha { namespace { -const TCHAR* const kAppGuid1 = _T("{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}"); -const TCHAR* const kAppGuid2 = _T("{81E5F427-8854-4c9a-A8D3-93F75F3D50DC}"); - const TCHAR* const kBadCmdLine = _T("cmd_garbeldy_gook.exe"); const TCHAR* const kCmdLineExit0 = _T("cmd.exe /c \"exit 0\""); const TCHAR* const kCmdLineExit3 = _T("cmd.exe /c \"exit 3\""); @@ -51,18 +48,6 @@ const TCHAR* const kCmdLineEchoWithSleep = _T("cmd.exe /c \"echo Hello World& ping.exe 2.2.2.2 -n 1 -w 1000 >NUL & ") _T("echo Goodbye World\""); -const TCHAR* const kCmdId1 = _T("command 1"); -const TCHAR* const kCmdId2 = _T("command 2"); -const TCHAR* const kCmdId3 = _T("command 3"); - -const TCHAR* const kSessionId = _T("unittest_session_id"); - -const bool kTrue = true; -const bool kFalse = false; - -const DWORD kOne = 1; -const DWORD kTwo = 2; - CString GetEchoCommandLine(CString string, CString output_file) { CString command_line; _sntprintf_s(CStrBuf(command_line, MAX_PATH), diff --git a/omaha/goopdate/app_manager.cc b/omaha/goopdate/app_manager.cc index 3f6fa3c5e..7466aedbf 100644 --- a/omaha/goopdate/app_manager.cc +++ b/omaha/goopdate/app_manager.cc @@ -552,6 +552,7 @@ HRESULT AppManager::ReadAppDefinedAttributeSubkeys( // ping_freshness // ClientState and ClientStateMedium key // eulaaccepted +// usagestats // ClientState key in HKCU/HKLM/Low integrity // did run // @@ -605,12 +606,12 @@ HRESULT AppManager::ReadAppPersistentData(App* app) { StringFormatter formatter(app->app_bundle()->display_language()); CString company_name; - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_FRIENDLY_COMPANY_NAME, - &company_name))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_FRIENDLY_COMPANY_NAME, + &company_name)); - VERIFY1(SUCCEEDED(formatter.FormatMessage(&app->display_name_, + VERIFY_SUCCEEDED(formatter.FormatMessage(&app->display_name_, IDS_DEFAULT_APP_DISPLAY_NAME, - company_name))); + company_name)); } // If ClientState registry key doesn't exist, the function could return. @@ -656,8 +657,8 @@ HRESULT AppManager::ReadAppPersistentData(App* app) { client_state_key.GetValue(kRegValueLanguage, &app->language_); } - VERIFY1(SUCCEEDED(ReadAppDefinedAttributes(GuidToString(app_guid), - &app->app_defined_attributes_))); + VERIFY_SUCCEEDED(ReadAppDefinedAttributes(GuidToString(app_guid), + &app->app_defined_attributes_)); client_state_key.GetValue(kRegValueAdditionalParams, &app->ap_); client_state_key.GetValue(kRegValueTTToken, &app->tt_token_); @@ -732,9 +733,26 @@ HRESULT AppManager::ReadAppPersistentData(App* app) { app->ping_freshness_ = ping_freshness; } + app->usage_stats_enable_ = GetAppUsageStatsEnabled(app_guid); + return S_OK; } +void AppManager::ReadFirstInstallAppVersion(App* app) { + ASSERT1(app); + + CString pv; + app_registry_utils::GetAppVersion(is_machine_, app->app_guid_string(), &pv); + + if (!pv.IsEmpty()) { + // Because this is an overinstall, we always want the latest version served + // back from the server, so prefix a negative sign to allow the server to + // ignore the version for comparisons. + pv.Insert(0, _T('-')); + app->current_version()->set_version(pv); + } +} + void AppManager::ReadAppInstallTimeDiff(App* app) { ASSERT1(app); app->install_time_diff_sec_ = GetInstallTimeDiffSec(app->app_guid()); @@ -791,6 +809,7 @@ HRESULT AppManager::ReadUninstalledAppPersistentData(App* app) { // eulaaccepted (set/deleted) // browser // usagestats +// ping freshness // Sets eulaaccepted=0 if the app is not already registered and the app's EULA // has not been accepted. Deletes eulaaccepted if the EULA has been accepted. // Only call for initial or over-installs. Do not call for updates to avoid @@ -811,6 +830,17 @@ HRESULT AppManager::WritePreInstallData(const App& app) { return hr; } + // Initialize the ping freshness, if needed. For installs and over-installs, + // ping freshness must be available before sending a completion ping. + // Otherwise, the completion ping contains no ping_freshness, which makes + // impossible to do an accurate user count in the presence of system reimage. + if (!client_state_key.HasValue(kRegValuePingFreshness)) { + GUID ping_freshness = GUID_NULL; + VERIFY_SUCCEEDED(::CoCreateGuid(&ping_freshness)); + client_state_key.SetValue(kRegValuePingFreshness, + GuidToString(ping_freshness)); + } + if (app.is_eula_accepted()) { hr = app_registry_utils::ClearAppEulaNotAccepted(is_machine_, app.app_guid_string()); @@ -825,43 +855,43 @@ HRESULT AppManager::WritePreInstallData(const App& app) { } if (!app.language().IsEmpty()) { - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, - app.language()))); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, + app.language())); } if (app.ap().IsEmpty()) { - VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueAdditionalParams))); + VERIFY_SUCCEEDED(client_state_key.DeleteValue(kRegValueAdditionalParams)); } else { - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams, - app.ap()))); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams, + app.ap())); } CString state_key_path = GetClientStateKeyName(app.app_guid()); - VERIFY1(SUCCEEDED(app_registry_utils::SetAppBranding( + VERIFY_SUCCEEDED(app_registry_utils::SetAppBranding( state_key_path, app.brand_code(), app.client_id(), app.referral_id(), - app.day_of_last_response()))); + app.day_of_last_response())); if (oem_install_utils::IsOemInstalling(is_machine_)) { ASSERT1(is_machine_); - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1")))); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1"))); } if (BROWSER_UNKNOWN == app.browser_type()) { - VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueBrowser))); + VERIFY_SUCCEEDED(client_state_key.DeleteValue(kRegValueBrowser)); } else { DWORD browser_type = app.browser_type(); - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueBrowser, - browser_type))); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValueBrowser, + browser_type)); } if (TRISTATE_NONE != app.usage_stats_enable()) { - VERIFY1(SUCCEEDED(app_registry_utils::SetUsageStatsEnable( + VERIFY_SUCCEEDED(app_registry_utils::SetUsageStatsEnable( is_machine_, app.app_guid_string(), - app.usage_stats_enable()))); + app.usage_stats_enable())); } return S_OK; @@ -1050,9 +1080,9 @@ void AppManager::PersistSuccessfulUpdateCheckResponse( _T("[%s][%d]"), app.app_guid_string(), is_update_available)); __mutexScope(registry_access_lock_); - VERIFY1(SUCCEEDED(SetTTToken(app))); + VERIFY_SUCCEEDED(SetTTToken(app)); - VERIFY1(SUCCEEDED(WriteCohort(app))); + VERIFY_SUCCEEDED(WriteCohort(app)); const CString client_state_key = GetClientStateKeyName(app.app_guid()); @@ -1109,22 +1139,22 @@ void AppManager::PersistSuccessfulInstall(const App& app) { ASSERT1(!::IsEqualGUID(kGoopdateGuid, app.app_guid())); RegKey client_state_key; - VERIFY1(SUCCEEDED(CreateClientStateKey(app.app_guid(), &client_state_key))); + VERIFY_SUCCEEDED(CreateClientStateKey(app.app_guid(), &client_state_key)); - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueProductVersion, - app.next_version()->version()))); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValueProductVersion, + app.next_version()->version())); if (!app.language().IsEmpty()) { - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, - app.language()))); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, + app.language())); } if (::IsEqualGUID(app.iid(), GUID_NULL)) { - VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueInstallationId))); + VERIFY_SUCCEEDED(client_state_key.DeleteValue(kRegValueInstallationId)); } else { - VERIFY1(SUCCEEDED(client_state_key.SetValue( + VERIFY_SUCCEEDED(client_state_key.SetValue( kRegValueInstallationId, - GuidToString(app.iid())))); + GuidToString(app.iid()))); } const CString client_state_key_path = GetClientStateKeyName(app.app_guid()); @@ -1307,14 +1337,14 @@ void AppManager::ClearOemInstalled(const AppIdVector& app_ids) { continue; } - VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueOemInstall))); + VERIFY_SUCCEEDED(state_key.DeleteValue(kRegValueOemInstall)); // The current time is close to when OEM activation has happened. Treat the // current time as the real install time by resetting InstallTime. const DWORD now = Time64ToInt32(GetCurrent100NSTime()); - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now))); - VERIFY1(SUCCEEDED(app_registry_utils::SetInitialDayOfValues( - GetClientStateKeyName(app_guid), -1))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)); + VERIFY_SUCCEEDED(app_registry_utils::SetInitialDayOfValues( + GetClientStateKeyName(app_guid), -1)); } } @@ -1335,16 +1365,16 @@ void AppManager::UpdateUpdateAvailableStats(const GUID& app_guid) const { update_available_count = 0; } ++update_available_count; - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableCount, - update_available_count))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableCount, + update_available_count)); DWORD64 update_available_since_time(0); hr = state_key.GetValue(kRegValueUpdateAvailableSince, &update_available_since_time); if (FAILED(hr)) { // There is no existing value, so this must be the first update notice. - VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableSince, - GetCurrent100NSTime()))); + VERIFY_SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableSince, + GetCurrent100NSTime())); // TODO(omaha): It would be nice to report the version that we were first // told to update to. This is available in UpdateResponse but we do not @@ -1419,6 +1449,19 @@ uint32 AppManager::GetDayOfInstall(const GUID& app_guid) const { return kUnknownDayOfInstall; } +Tristate AppManager::GetAppUsageStatsEnabled(const GUID& app_guid) const { + if (!IsAppRegistered(app_guid) && !IsAppUninstalled(app_guid)) { + return TRISTATE_NONE; + } + + if (app_registry_utils::AreAppUsageStatsEnabled(is_machine_, + GuidToString(app_guid))) { + return TRISTATE_TRUE; + } + + return TRISTATE_FALSE; +} + // Clear the Installation ID if at least one of the conditions is true: // 1) DidRun==yes. First run is the last time we want to use the Installation // ID. So delete Installation ID if it is present. @@ -1474,32 +1517,32 @@ void AppManager::SetLastPingTimeMetrics( const bool did_send_active_ping = (app.did_run() == ACTIVE_RUN && app.days_since_last_active_ping() != 0); if (did_send_active_ping) { - VERIFY1(SUCCEEDED(client_state_key.SetValue( + VERIFY_SUCCEEDED(client_state_key.SetValue( kRegValueActivePingDayStartSec, - static_cast(now - elapsed_seconds_since_day_start)))); + static_cast(now - elapsed_seconds_since_day_start))); } const bool did_send_roll_call = (app.days_since_last_roll_call() != 0); if (did_send_roll_call) { - VERIFY1(SUCCEEDED(client_state_key.SetValue( + VERIFY_SUCCEEDED(client_state_key.SetValue( kRegValueRollCallDayStartSec, - static_cast(now - elapsed_seconds_since_day_start)))); + static_cast(now - elapsed_seconds_since_day_start))); } // Update new-style counting metrics. const bool did_send_day_of_last_activity = (app.did_run() == ACTIVE_RUN && app.day_of_last_activity() != 0); if (did_send_active_ping || did_send_day_of_last_activity) { - VERIFY1(SUCCEEDED(client_state_key.SetValue( + VERIFY_SUCCEEDED(client_state_key.SetValue( kRegValueDayOfLastActivity, - static_cast(elapsed_days_since_datum)))); + static_cast(elapsed_days_since_datum))); } const bool did_send_day_of_roll_call = (app.day_of_last_roll_call() != 0); if (did_send_roll_call || did_send_day_of_roll_call) { - VERIFY1(SUCCEEDED(client_state_key.SetValue( + VERIFY_SUCCEEDED(client_state_key.SetValue( kRegValueDayOfLastRollCall, - static_cast(elapsed_days_since_datum)))); + static_cast(elapsed_days_since_datum))); } // Update the ping freshness value for this ping data. The purpose of the @@ -1508,9 +1551,9 @@ void AppManager::SetLastPingTimeMetrics( // generates a new freshness value, which remains constant until the // user counts are sent to the server. GUID ping_freshness = GUID_NULL; - VERIFY1(SUCCEEDED(::CoCreateGuid(&ping_freshness))); - VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValuePingFreshness, - GuidToString(ping_freshness)))); + VERIFY_SUCCEEDED(::CoCreateGuid(&ping_freshness)); + VERIFY_SUCCEEDED(client_state_key.SetValue(kRegValuePingFreshness, + GuidToString(ping_freshness))); } void AppManager::UpdateDayOfInstallIfNecessary( @@ -1531,9 +1574,9 @@ void AppManager::UpdateDayOfInstallIfNecessary( &existing_day_of_install))) { // Update DayOfInstall only if its value is -1. if (existing_day_of_install == static_cast(-1)) { - VERIFY1(SUCCEEDED(client_state_key.SetValue( + VERIFY_SUCCEEDED(client_state_key.SetValue( kRegValueDayOfInstall, - static_cast(elapsed_days_since_datum)))); + static_cast(elapsed_days_since_datum))); } } } @@ -1551,14 +1594,14 @@ HRESULT AppManager::PersistUpdateCheckSuccessfullySent( ApplicationUsageData app_usage(app.app_bundle()->is_machine(), vista_util::IsVistaOrLater()); - VERIFY1(SUCCEEDED(app_usage.ResetDidRun(app.app_guid_string()))); + VERIFY_SUCCEEDED(app_usage.ResetDidRun(app.app_guid_string())); SetLastPingTimeMetrics( app, elapsed_days_since_datum, elapsed_seconds_since_day_start); UpdateDayOfInstallIfNecessary(app, elapsed_days_since_datum); // Handle the installation id. - VERIFY1(SUCCEEDED(ClearInstallationId(app))); + VERIFY_SUCCEEDED(ClearInstallationId(app)); return S_OK; } diff --git a/omaha/goopdate/app_manager.h b/omaha/goopdate/app_manager.h index db3e38066..42de19bf8 100644 --- a/omaha/goopdate/app_manager.h +++ b/omaha/goopdate/app_manager.h @@ -98,6 +98,12 @@ class AppManager { // Populates the app object with the persisted state stored in the registry. HRESULT ReadAppPersistentData(App* app); + // Populates the app object with the pv value stored in the registry if it + // exists for the app. The pv is set in the `current_version` of the app + // object, with a '-' (negative) prefix. This is to allow for the server to + // always return the latest version for the app in the update response. + void ReadFirstInstallAppVersion(App* app); + // Populates the app object with the install time diff based on the install // time stored in the registry. // If the app is registered or has pv value, app's install time diff will be @@ -208,6 +214,12 @@ class AppManager { // Omaha will not send day_of_install if it is 0. uint32 GetDayOfInstall(const GUID& app_guid) const; + // Returns a Tristate of whether usage stats are enabled. + // Returns TRISTATE_NONE for unregistered apps. + // Returns TRISTATE_TRUE if usage stats consent is true. + // Returns TRISTATE_FALSE if usage stats consent is false or is unset. + Tristate GetAppUsageStatsEnabled(const GUID& app_guid) const; + bool IsAppRegistered(const CString& app_id) const; bool IsAppUninstalled(const CString& app_id) const; bool IsAppOemInstalledAndEulaAccepted(const CString& app_id) const; diff --git a/omaha/goopdate/app_manager_unittest.cc b/omaha/goopdate/app_manager_unittest.cc index b1a47b924..de522befb 100644 --- a/omaha/goopdate/app_manager_unittest.cc +++ b/omaha/goopdate/app_manager_unittest.cc @@ -61,19 +61,19 @@ const TCHAR* const kNonExistentClsid = _T("{BC00156D-3B01-4ba3-9F5E-2C46E8B6E824}"); const TCHAR* const kGuid1ClientsKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}"); const TCHAR* const kGuid1ClientsKeyPathMachine = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}"); const TCHAR* const kGuid1ClientStateKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}"); const TCHAR* const kGuid1ClientStateKeyPathMachine = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}"); @@ -258,6 +258,9 @@ class AppManagerTestBase : public AppTestBaseWithRegistryOverride { kRegValueDayOfLastRollCall, static_cast(num_days_since))); } + + ASSERT_SUCCEEDED(app_registry_utils::SetUsageStatsEnable( + is_machine, app.app_guid_string(), app.usage_stats_enable())); } // App will be cleaned up when bundle is destroyed. @@ -266,7 +269,7 @@ class AppManagerTestBase : public AppTestBaseWithRegistryOverride { App* CreateAppForRegistryPopulation(const TCHAR* app_id) { App* app = NULL; EXPECT_SUCCEEDED( - dummy_app_bundle_for_app_creation_->createApp(CComBSTR(app_id), &app)); + test_app_bundle_for_app_creation_->createApp(CComBSTR(app_id), &app)); ASSERT1(app); // install_time_diff_sec_ is -1 day for new app. After that, the app @@ -303,6 +306,7 @@ class AppManagerTestBase : public AppTestBaseWithRegistryOverride { expected_app->did_run_ = ACTIVE_RUN; expected_app->set_days_since_last_active_ping(3); expected_app->set_days_since_last_roll_call(1); + expected_app->usage_stats_enable_ = TRISTATE_TRUE; } static void PopulateExpectedApp2(App* expected_app) { @@ -318,6 +322,7 @@ class AppManagerTestBase : public AppTestBaseWithRegistryOverride { expected_app->did_run_ = ACTIVE_NOTRUN; expected_app->set_days_since_last_active_ping(100); expected_app->set_days_since_last_roll_call(1); + expected_app->usage_stats_enable_ = TRISTATE_FALSE; } static void PopulateExpectedUninstalledApp(const CString& uninstalled_version, @@ -347,21 +352,21 @@ class AppManagerTestBase : public AppTestBaseWithRegistryOverride { ASSERT_TRUE(app_manager_); // Initialize the second bundle. - dummy_app_bundle_for_app_creation_ = model_->CreateAppBundle(is_machine_); - ASSERT_TRUE(dummy_app_bundle_for_app_creation_.get()); + test_app_bundle_for_app_creation_ = model_->CreateAppBundle(is_machine_); + ASSERT_TRUE(test_app_bundle_for_app_creation_.get()); - EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->put_displayName( + EXPECT_SUCCEEDED(test_app_bundle_for_app_creation_->put_displayName( CComBSTR(_T("My Bundle")))); - EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->put_displayLanguage( + EXPECT_SUCCEEDED(test_app_bundle_for_app_creation_->put_displayLanguage( CComBSTR(_T("en")))); - EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->put_installSource( + EXPECT_SUCCEEDED(test_app_bundle_for_app_creation_->put_installSource( CComBSTR(_T("unittest")))); // TODO(omaha3): Address with the TODO in AppBundleInitializedTest::SetUp(). if (is_machine_) { - SetAppBundleStateForUnitTest(dummy_app_bundle_for_app_creation_.get(), + SetAppBundleStateForUnitTest(test_app_bundle_for_app_creation_.get(), new fsm::AppBundleStateInitialized); } else { - EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->initialize()); + EXPECT_SUCCEEDED(test_app_bundle_for_app_creation_->initialize()); } EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app_)); @@ -1243,7 +1248,7 @@ class AppManagerTestBase : public AppTestBaseWithRegistryOverride { // A second bundle is necessary because the same bundle cannot have the same // app in it more than once and many of these tests create an app to populate // the registry and another to read it. - std::shared_ptr dummy_app_bundle_for_app_creation_; + std::shared_ptr test_app_bundle_for_app_creation_; const GUID guid1_; @@ -1485,8 +1490,6 @@ TEST_F(AppManagerTest, ConvertCommandLineToProductData_Succeeds) { args.is_crash_handler_disabled = true; // Not used. args.is_eula_required_set = true; args.is_eula_required_set = true; // Not used. - args.webplugin_urldomain = _T("http://nothing.google.com"); // Not used. - args.webplugin_args = _T("blah"); // Not used. args.install_source = _T("one_click"); args.code_red_metainstaller_path = _T("foo.exe"); // Not used. args.legacy_manifest_path = _T("bar.exe"); // Not used. @@ -2133,6 +2136,11 @@ TEST_F(AppManagerReadAppPersistentDataUserTest, EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_)); SetDisplayName(kDefaultAppName, expected_app); + + // This is an unregistered app, and |AppManager::GetAppUsageStatsEnabled| + // returns TRISTATE_NONE for unregistered apps. + EXPECT_SUCCEEDED(expected_app->put_usageStatsEnable(TRISTATE_NONE)); + EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE)); ValidateExpectedValues(*expected_app, *app_); } @@ -2153,6 +2161,11 @@ TEST_F(AppManagerReadAppPersistentDataMachineTest, EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_)); SetDisplayName(kDefaultAppName, expected_app); + + // This is an unregistered app, and |AppManager::GetAppUsageStatsEnabled| + // returns TRISTATE_NONE for unregistered apps. + EXPECT_SUCCEEDED(expected_app->put_usageStatsEnable(TRISTATE_NONE)); + EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE)); ValidateExpectedValues(*expected_app, *app_); } diff --git a/omaha/goopdate/app_state.cc b/omaha/goopdate/app_state.cc index 2801b9415..2272a3a04 100644 --- a/omaha/goopdate/app_state.cc +++ b/omaha/goopdate/app_state.cc @@ -117,7 +117,7 @@ void AppState::Cancel(App* app) { CString message; StringFormatter formatter(app->app_bundle()->display_language()); - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_CANCELED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_CANCELED, &message)); app->SetError(ErrorContext(hr), message); app->set_state_cancelled(state()); app->SetCurrentTimeAs(App::TIME_CANCELLED); @@ -155,7 +155,7 @@ void AppState::HandleInvalidStateTransition(App* app, const HRESULT hr = GOOPDATE_E_INVALID_STATE_TRANSITION; StringFormatter formatter(app->app_bundle()->display_language()); CString message; - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); AppState::Error(app, ErrorContext(hr, state_), message); } @@ -175,9 +175,9 @@ void AppState::HandleGroupPolicyError(App* app, HRESULT code) { StringFormatter formatter(app->app_bundle()->display_language()); CString error_message; - VERIFY1(SUCCEEDED(formatter.LoadString( + VERIFY_SUCCEEDED(formatter.LoadString( IDS_APP_INSTALL_DISABLED_BY_GROUP_POLICY, - &error_message))); + &error_message)); Error(app, ErrorContext(code), error_message); } diff --git a/omaha/goopdate/app_state_checking_for_update.cc b/omaha/goopdate/app_state_checking_for_update.cc index e2c77a006..e78b117a8 100644 --- a/omaha/goopdate/app_state_checking_for_update.cc +++ b/omaha/goopdate/app_state_checking_for_update.cc @@ -66,7 +66,7 @@ xml::UpdateResponseResult GetUpdateResponseResult( update_response_utils::IsOmahaUpdateAvailable(update_response)) { StringFormatter formatter(language); CString text; - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text)); update_response_result = std::make_pair(GOOPDATE_E_UPDATE_DEFERRED, text); } @@ -153,8 +153,8 @@ void AppStateCheckingForUpdate::HandleUpdateAvailable(App* app, const CString language = app->app_bundle()->display_language(); StringFormatter formatter(language); CString error_message; - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, - &error_message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, + &error_message)); Error(app, ErrorContext(hr), error_message); return; } @@ -205,9 +205,9 @@ void AppStateCheckingForUpdate::HandleNoUpdate(App* app, return; } - VERIFY1(SUCCEEDED(update_response_utils::BuildApp(update_response_, + VERIFY_SUCCEEDED(update_response_utils::BuildApp(update_response_, code, - app))); + app)); AppManager::Instance()->PersistSuccessfulUpdateCheckResponse(*app, false); app->SetNoUpdate(ErrorContext(S_OK), message); @@ -272,8 +272,8 @@ void AppStateCheckingForUpdate::PersistUpdateCheckValuesOnFailure(App* app) { AppManager& app_manager = *AppManager::Instance(); app->set_day_of_last_response(daynum); - VERIFY1(SUCCEEDED(app_manager.PersistUpdateCheckSuccessfullySent( - *app, daynum, daystart))); + VERIFY_SUCCEEDED(app_manager.PersistUpdateCheckSuccessfullySent( + *app, daynum, daystart)); } void AppStateCheckingForUpdate::PersistUpdateCheckSuccessfullySent( @@ -286,10 +286,10 @@ void AppStateCheckingForUpdate::PersistUpdateCheckSuccessfullySent( } AppManager& app_manager = *AppManager::Instance(); - VERIFY1(SUCCEEDED(app_manager.PersistUpdateCheckSuccessfullySent( + VERIFY_SUCCEEDED(app_manager.PersistUpdateCheckSuccessfullySent( app, update_response_->GetElapsedDaysSinceDatum(), - update_response_->GetElapsedSecondsSinceDayStart()))); + update_response_->GetElapsedSecondsSinceDayStart())); // Here we assume that some of the members in app object // (days_since_last_active_ping_, days_since_last_roll_call_, diff --git a/omaha/goopdate/app_state_init.cc b/omaha/goopdate/app_state_init.cc index 546bd1edc..f8c9ef360 100644 --- a/omaha/goopdate/app_state_init.cc +++ b/omaha/goopdate/app_state_init.cc @@ -35,6 +35,7 @@ void AppStateInit::QueueUpdateCheck(App* app) { // an important debug check to ensure that duplicate pings are not sent. ASSERT1(!::IsEqualGUID(kGoopdateGuid, app->app_guid()) || app->is_update()); + app->ResetInstallProgress(); AppManager::Instance()->ResetCurrentStateKey(app->app_guid_string()); ChangeState(app, new AppStateWaitingToCheckForUpdate); diff --git a/omaha/goopdate/app_state_install_complete.cc b/omaha/goopdate/app_state_install_complete.cc index 5620eda68..c2612c5f3 100644 --- a/omaha/goopdate/app_state_install_complete.cc +++ b/omaha/goopdate/app_state_install_complete.cc @@ -37,8 +37,8 @@ AppStateInstallComplete::AppStateInstallComplete(App* app) goopdate_utils::StartCrashHandler(is_machine); } - VERIFY1(SUCCEEDED(app->model()->PurgeAppLowerVersions( - app->app_guid_string(), app->next_version()->version()))); + VERIFY_SUCCEEDED(app->model()->PurgeAppLowerVersions( + app->app_guid_string(), app->next_version()->version())); } // Omaha installs and updates are two-step processes. Omaha is handled as a @@ -73,7 +73,7 @@ const PingEvent* AppStateInstallComplete::CreatePingEvent( return can_ping ? new PingEvent(event_type, GetCompletionResult(*app), error_code, - 0, + app->installer_result_extra_code1(), app->source_url_index(), app->GetUpdateCheckTimeMs(), app->GetDownloadTimeMs(), diff --git a/omaha/goopdate/app_state_waiting_to_check_for_update.cc b/omaha/goopdate/app_state_waiting_to_check_for_update.cc index 7bba1132f..c548b670f 100644 --- a/omaha/goopdate/app_state_waiting_to_check_for_update.cc +++ b/omaha/goopdate/app_state_waiting_to_check_for_update.cc @@ -64,10 +64,13 @@ void AppStateWaitingToCheckForUpdate::PreUpdateCheck( AppManager* app_manager(AppManager::Instance()); - VERIFY1(SUCCEEDED(app_manager->SynchronizeClientState(app->app_guid()))); + VERIFY_SUCCEEDED(app_manager->SynchronizeClientState(app->app_guid())); // Handle the normal flow and return. Abnormal cases are below. - if (app->is_eula_accepted()) { + // Offline installs do not need requests, but we add the app here to indicate + // to `Worker::DoUpdateCheck` that there were no validation issues with + // policies. + if (app->is_eula_accepted() || app->app_bundle()->is_offline_install()) { update_request_utils::BuildRequest(app, true, update_request); app->SetCurrentTimeAs(App::TIME_UPDATE_CHECK_START); ChangeState(app, new AppStateCheckingForUpdate); @@ -76,23 +79,13 @@ void AppStateWaitingToCheckForUpdate::PreUpdateCheck( // The app's EULA has not been accepted, so do not add this app to the update // check. This means bundle size does not always match the request size. - ASSERT1(app->app_guid() != kGoopdateGuid); - - // TODO(omaha3): Is there a better way to do this such that we don't need to - // know about offline installs here? - if (app->app_bundle()->is_offline_install()) { - // Offline installs do not need requests, so skip building the request. - ChangeState(app, new AppStateCheckingForUpdate); - return; - } - ASSERT1(app->is_update()); metric_worker_apps_not_updated_eula++; StringFormatter formatter(app->app_bundle()->display_language()); CString message; - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); Error(app, ErrorContext(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED), message); diff --git a/omaha/goopdate/app_state_waiting_to_install.cc b/omaha/goopdate/app_state_waiting_to_install.cc index ddd4ace01..7fd7f6fb9 100644 --- a/omaha/goopdate/app_state_waiting_to_install.cc +++ b/omaha/goopdate/app_state_waiting_to_install.cc @@ -59,6 +59,18 @@ void AppStateWaitingToInstall::Install( ASSERT1(app); ASSERT1(install_manager); + // For large files, verifying the hash of the file may take a several + // seconds of CPU time. The execution of the CopyAppVersionPackages function + // occurs under the model lock. That means that the controller can't respond + // to state queries initiate by the COM client, which means that state + // changes can't be observed while the hash verification occurs. While the + // hash of the file is checked, the UI appears to be stuck in a state, which + // may be different than this state. Calling ::Sleep here is a work around + // to increase the likelihood that a client polling for state changes detects + // this transition, and the UI displays a relevant text for the user. + constexpr int kWaitBeforeInstallMs = 500; + ::Sleep(kWaitBeforeInstallMs); + CString guid; HRESULT hr(GetGuid(&guid)); if (SUCCEEDED(hr)) { @@ -73,7 +85,7 @@ void AppStateWaitingToInstall::Install( if (FAILED(hr)) { StringFormatter formatter(app->app_bundle()->display_language()); CString message; - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); Error(app, ErrorContext(hr), message); } } diff --git a/omaha/goopdate/app_unittest.cc b/omaha/goopdate/app_unittest.cc index f3f3609a8..c875050ab 100644 --- a/omaha/goopdate/app_unittest.cc +++ b/omaha/goopdate/app_unittest.cc @@ -40,14 +40,11 @@ const TCHAR* const kAppId1 = APP_ID1 const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_ID1; const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_ID1; const TCHAR* const kAppId1ClientsKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\") APP_ID1; const TCHAR* const kGuid1ClientStateKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") APP_ID1; -const TCHAR* const kChromeClientStateKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") - PRODUCT_NAME _T("\\ClientState\\") CHROME_APP_ID; #define APP_ID2 _T("{EF3CACD4-89EB-46b7-B9BF-B16B15F08584}"); const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_ID2; @@ -151,7 +148,7 @@ class AppAutoUpdateTest : public AppManualUpdateTest { EXPECT_SUCCEEDED(app_bundle_->updateAllApps()); EXPECT_TRUE(app_bundle_->is_auto_update()); - app_= app_bundle_->GetApp(0); + app_ = app_bundle_->GetApp(0); ASSERT_TRUE(app_); } }; @@ -177,67 +174,67 @@ TEST_F(AppAutoUpdateTest, CheckGroupPolicy_NoPolicy) { } TEST_P(AppInstallTest, CheckGroupPolicy_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); EXPECT_EQ(IsDomain() ? GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY : S_OK, app_->CheckGroupPolicy()); } TEST_P(AppManualUpdateTest, CheckGroupPolicy_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } TEST_P(AppAutoUpdateTest, CheckGroupPolicy_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } TEST_P(AppInstallTest, CheckGroupPolicy_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } TEST_P(AppManualUpdateTest, CheckGroupPolicy_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); EXPECT_EQ(IsDomain() ? GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY : S_OK, app_->CheckGroupPolicy()); } TEST_P(AppAutoUpdateTest, CheckGroupPolicy_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); EXPECT_EQ(IsDomain() ? GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY : S_OK, app_->CheckGroupPolicy()); } TEST_P(AppInstallTest, CheckGroupPolicy_AutoUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } TEST_P(AppManualUpdateTest, CheckGroupPolicy_AutoUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } TEST_P(AppAutoUpdateTest, CheckGroupPolicy_AutoUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); EXPECT_EQ(IsDomain() ? GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY : S_OK, app_->CheckGroupPolicy()); } TEST_P(AppInstallTest, CheckGroupPolicy_ManualUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyAutomaticUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyAutomaticUpdatesOnly); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } TEST_P(AppManualUpdateTest, CheckGroupPolicy_ManualUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyAutomaticUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyAutomaticUpdatesOnly); EXPECT_EQ(IsDomain() ? GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL : S_OK, app_->CheckGroupPolicy()); } TEST_P(AppAutoUpdateTest, CheckGroupPolicy_ManualUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyAutomaticUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyAutomaticUpdatesOnly); EXPECT_SUCCEEDED(app_->CheckGroupPolicy()); } @@ -267,7 +264,7 @@ TEST_F(AppInstallTest, PostUpdateCheck_UpdateAvailable) { // Policy is not checked by this function. TEST_P(AppInstallTest, PostUpdateCheck_UpdateAvailable_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate); AddAppResponse(xml::response::kStatusOkValue, std::vector()); @@ -300,7 +297,7 @@ TEST_F(AppManualUpdateTest, PostUpdateCheck_UpdateAvailable) { // Policy is not checked by this function. TEST_P(AppManualUpdateTest, PostUpdateCheck_UpdateAvailable_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate); AddAppResponse(xml::response::kStatusOkValue, std::vector()); @@ -332,7 +329,7 @@ TEST_F(AppAutoUpdateTest, PostUpdateCheck_UpdateAvailable) { // Policy is not checked by this function. TEST_P(AppAutoUpdateTest, PostUpdateCheck_UpdateAvailable_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate); AddAppResponse(xml::response::kStatusOkValue, std::vector()); @@ -385,7 +382,7 @@ TEST_F(AppInstallTest, QueueDownload_NoPolicy) { } TEST_P(AppInstallTest, QueueDownload_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -400,7 +397,7 @@ TEST_P(AppInstallTest, QueueDownload_InstallDisabled) { TEST_P(AppInstallTest, QueueDownload_InstallDisabledForDifferentApp) { - SetPolicy(kInstallPolicyApp2, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp2, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -409,7 +406,7 @@ TEST_P(AppInstallTest, } TEST_P(AppInstallTest, QueueDownload_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -426,7 +423,7 @@ TEST_F(AppManualUpdateTest, QueueDownload_NoPolicy) { } TEST_P(AppManualUpdateTest, QueueDownload_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -441,7 +438,7 @@ TEST_P(AppManualUpdateTest, QueueDownload_AllUpdatesDisabled) { TEST_P(AppManualUpdateTest, QueueDownload_AllUpdatesDisabledForDifferentApp) { - SetPolicy(kUpdatePolicyApp2, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp2, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -450,7 +447,7 @@ TEST_P(AppManualUpdateTest, } TEST_P(AppManualUpdateTest, QueueDownload_AutoUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -459,7 +456,7 @@ TEST_P(AppManualUpdateTest, QueueDownload_AutoUpdatesDisabled) { } TEST_P(AppManualUpdateTest, QueueDownload_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -476,7 +473,7 @@ TEST_P(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabled_NoPolicy) { } TEST_P(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -490,7 +487,7 @@ TEST_P(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabled) { } TEST_P(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabledForDifferentApp) { - SetPolicy(kUpdatePolicyApp2, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp2, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -499,7 +496,7 @@ TEST_P(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabledForDifferentApp) { } TEST_P(AppAutoUpdateTest, QueueDownload_AutoUpdatesDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -513,7 +510,7 @@ TEST_P(AppAutoUpdateTest, QueueDownload_AutoUpdatesDisabled) { } TEST_P(AppAutoUpdateTest, QueueDownload_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable); app_->QueueDownload(); @@ -592,7 +589,7 @@ TEST_F(AppInstallTest, PreUpdateCheck_EulaNotAccepted_Offline) { app_->PreUpdateCheck(update_request.get()); EXPECT_EQ(STATE_CHECKING_FOR_UPDATE, app_->state()); EXPECT_EQ(S_OK, app_->error_code()); - EXPECT_TRUE(update_request->IsEmpty()) << _T("Should not add request."); + EXPECT_FALSE(update_request->IsEmpty()) << _T("Should add request."); } TEST_F(AppAutoUpdateTest, PreUpdateCheck_EulaNotAccepted) { @@ -613,7 +610,7 @@ TEST_F(AppAutoUpdateTest, PreUpdateCheck_EulaNotAccepted) { } TEST_P(AppInstallTest, PreUpdateCheck_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -633,7 +630,7 @@ TEST_P(AppInstallTest, PreUpdateCheck_InstallDisabled) { } TEST_P(AppManualUpdateTest, PreUpdateCheck_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -653,7 +650,7 @@ TEST_P(AppManualUpdateTest, PreUpdateCheck_InstallDisabled) { } TEST_P(AppAutoUpdateTest, PreUpdateCheck_InstallDisabled) { - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -673,7 +670,7 @@ TEST_P(AppAutoUpdateTest, PreUpdateCheck_InstallDisabled) { } TEST_P(AppInstallTest, PreUpdateCheck_InstallDisabledForDifferentApp) { - SetPolicy(kInstallPolicyApp2, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp2, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -691,7 +688,7 @@ TEST_P(AppInstallTest, PreUpdateCheck_InstallDisabledForDifferentApp) { } TEST_P(AppInstallTest, PreUpdateCheck_UpdateDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -709,7 +706,7 @@ TEST_P(AppInstallTest, PreUpdateCheck_UpdateDisabled) { } TEST_P(AppManualUpdateTest, PreUpdateCheck_UpdateDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -733,7 +730,7 @@ TEST_P(AppManualUpdateTest, PreUpdateCheck_UpdateDisabled) { } TEST_P(AppAutoUpdateTest, PreUpdateCheck_UpdateDisabled) { - SetPolicy(kUpdatePolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -753,7 +750,7 @@ TEST_P(AppAutoUpdateTest, PreUpdateCheck_UpdateDisabled) { } TEST_P(AppManualUpdateTest, PreUpdateCheck_UpdateDisabledForDifferentApp) { - SetPolicy(kUpdatePolicyApp2, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp2, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -773,7 +770,7 @@ TEST_P(AppManualUpdateTest, PreUpdateCheck_UpdateDisabledForDifferentApp) { } TEST_P(AppAutoUpdateTest, PreUpdateCheck_UpdateDisabledForDifferentApp) { - SetPolicy(kUpdatePolicyApp2, kPolicyDisabled); + SetEnrolledPolicy(kUpdatePolicyApp2, kPolicyDisabled); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -793,7 +790,7 @@ TEST_P(AppAutoUpdateTest, PreUpdateCheck_UpdateDisabledForDifferentApp) { } TEST_P(AppInstallTest, PreUpdateCheck_ManualUpdatesOnly) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -811,7 +808,7 @@ TEST_P(AppInstallTest, PreUpdateCheck_ManualUpdatesOnly) { } TEST_P(AppManualUpdateTest, PreUpdateCheck_ManualUpdatesOnly) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); @@ -831,7 +828,7 @@ TEST_P(AppManualUpdateTest, PreUpdateCheck_ManualUpdatesOnly) { } TEST_P(AppAutoUpdateTest, PreUpdateCheck_ManualUpdatesOnly) { - SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); + SetEnrolledPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly); SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate); EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); diff --git a/omaha/goopdate/app_unittest_base.h b/omaha/goopdate/app_unittest_base.h index c8d2d29c6..8a1e7de93 100644 --- a/omaha/goopdate/app_unittest_base.h +++ b/omaha/goopdate/app_unittest_base.h @@ -23,14 +23,14 @@ #include "omaha/base/app_util.h" #include "omaha/base/xml_utils.h" -#include "omaha/common/const_group_policy.h" -#include "omaha/goopdate/app_manager.h" +#include "omaha/common/config_manager.h" #include "omaha/goopdate/app_bundle_state_initialized.h" +#include "omaha/goopdate/app_manager.h" #include "omaha/goopdate/goopdate.h" #include "omaha/goopdate/model.h" #include "omaha/goopdate/resource_manager.h" -#include "omaha/goopdate/worker_mock.h" #include "omaha/goopdate/update_response_utils.h" +#include "omaha/goopdate/worker_mock.h" #include "omaha/testing/unit_test.h" using ::testing::Return; @@ -159,6 +159,8 @@ class AppTestBaseWithRegistryOverride RestoreRegistryHives(); RegKey::DeleteKey(hive_override_key_name_); + ConfigManager::DeleteInstance(); + AppTestBase::TearDown(); } @@ -166,14 +168,11 @@ class AppTestBaseWithRegistryOverride return GetParam(); } - void SetPolicy(const CString& policy, DWORD value) { + void SetEnrolledPolicy(const CString& policy, DWORD value) { EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain, IsDomain() ? 1UL : 0UL)); - - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - policy, - value)); + EXPECT_SUCCEEDED(SetPolicy(policy, value)); } CString hive_override_key_name_; diff --git a/omaha/goopdate/app_version.cc b/omaha/goopdate/app_version.cc index 21b145b4d..18125a2c5 100644 --- a/omaha/goopdate/app_version.cc +++ b/omaha/goopdate/app_version.cc @@ -21,7 +21,6 @@ #include "omaha/base/logging.h" #include "omaha/base/synchronized.h" #include "omaha/base/utils.h" -#include "omaha/goopdate/file_hash.h" #include "omaha/goopdate/model.h" namespace omaha { @@ -86,7 +85,11 @@ size_t AppVersion::GetNumberOfPackages() const { HRESULT AppVersion::AddPackage(const CString& filename, uint32 size, - const FileHash& hash) { + const CString& hash) { + if (hash.IsEmpty()) { + return E_INVALIDARG; + } + __mutexScope(model()->lock()); Package* package = new Package(this); package->SetFileInfo(filename, size, hash); @@ -145,7 +148,7 @@ STDMETHODIMP AppVersion::get_packageCount(long* count) { // NOLINT return E_FAIL; } - *count = static_cast(num_packages); + *count = static_cast(num_packages); // NOLINT return S_OK; } diff --git a/omaha/goopdate/app_version.h b/omaha/goopdate/app_version.h index 28dcf0831..bcf953e71 100644 --- a/omaha/goopdate/app_version.h +++ b/omaha/goopdate/app_version.h @@ -34,7 +34,6 @@ namespace omaha { class App; class AppBundle; class Package; -struct FileHash; class AppVersion : public ModelObject { public: @@ -64,7 +63,7 @@ class AppVersion : public ModelObject { // Adds a package to this app version. HRESULT AddPackage(const CString& filename, uint32 size, - const FileHash& expected_hash); + const CString& expected_hash); // Returns the list of download servers to use in order of preference. const std::vector& download_base_urls() const; @@ -74,7 +73,6 @@ class AppVersion : public ModelObject { HRESULT AddDownloadBaseUrl(const CString& server_url); private: - // product version "pv". CString version_; diff --git a/omaha/goopdate/app_version_unittest.cc b/omaha/goopdate/app_version_unittest.cc index e44b0091b..5d83ade53 100644 --- a/omaha/goopdate/app_version_unittest.cc +++ b/omaha/goopdate/app_version_unittest.cc @@ -33,7 +33,7 @@ namespace omaha { const TCHAR* const kTestId = _T("{8260D23D-D23B-427F-AF1A-2CE36E6F073B}"); -class IDummyUnknownImpl : public IUnknown { +class ITestUnknownImpl : public IUnknown { public: virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; @@ -52,10 +52,10 @@ class AppVersionTest : public testing::Test { }; TEST_F(AppVersionTest, TestReadOnly) { - IDummyUnknownImpl dummy_unknown; + ITestUnknownImpl test_unknown; std::unique_ptr app_version; EXPECT_SUCCEEDED(AppVersion::Create(&lock_, - &dummy_unknown, + &test_unknown, true, address(app_version))); @@ -88,10 +88,10 @@ TEST_F(AppVersionTest, TestReadOnly) { } TEST_F(AppVersionTest, TestReadWrite) { - IDummyUnknownImpl dummy_unknown; + ITestUnknownImpl test_unknown; std::unique_ptr app_version; EXPECT_SUCCEEDED(AppVersion::Create(&lock_, - &dummy_unknown, + &test_unknown, false, address(app_version))); diff --git a/omaha/goopdate/application_usage_data_unittest.cc b/omaha/goopdate/application_usage_data_unittest.cc index 82587c8c8..8321bef9b 100644 --- a/omaha/goopdate/application_usage_data_unittest.cc +++ b/omaha/goopdate/application_usage_data_unittest.cc @@ -26,17 +26,17 @@ namespace omaha { const TCHAR kAppDidRunValueName[] = _T("dr"); const TCHAR kHKCUClientStateKeyName[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}"); const TCHAR kMachineClientState[] = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}"); const TCHAR kLowIntegrityIEHKCU[] = _T("HKCU\\Software\\Microsoft\\Internet Explorer\\") _T("InternetRegistry\\REGISTRY\\USER\\"); const TCHAR kAppGuid[] = _T("{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}"); const TCHAR kRelativeClientState[] = - _T("Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}"); // TODO(omaha): Expected and actual are reversed throughout this file. Fix. diff --git a/omaha/goopdate/broker_class_factory.h b/omaha/goopdate/broker_class_factory.h index e0a1326c0..caeffb653 100644 --- a/omaha/goopdate/broker_class_factory.h +++ b/omaha/goopdate/broker_class_factory.h @@ -131,6 +131,7 @@ class ATL_NO_VTABLE BrokerClassFactoryRegistrar extern TCHAR kOnDemandMachineBrokerProgId[]; extern TCHAR kUpdate3WebMachineBrokerProgId[]; +extern TCHAR kPolicyStatusMachineBrokerProgId[]; // An OnDemand client CoCreates OnDemandMachineAppsClass, which // instantiates the class factory for the OnDemandMachineBroker typedef below. @@ -153,6 +154,18 @@ typedef BrokerClassFactoryRegistrar< kUpdate3WebMachineBrokerProgId> Update3WebMachineBroker; +// A Policy Status client CoCreates PolicyStatusMachineClass, which +// instantiates the class factory for the PolicyStatusMachineBroker typedef +// below. +// The class factory in turn passes the CreateInstance through to +// PolicyStatusMachineServiceClass. If the CreateInstance fails, it instantiates +// PolicyStatusMachineFallbackClass instead. +typedef BrokerClassFactoryRegistrar<__uuidof(PolicyStatusMachineServiceClass), + __uuidof(PolicyStatusMachineFallbackClass), + __uuidof(PolicyStatusMachineClass), + kPolicyStatusMachineBrokerProgId> + PolicyStatusMachineBroker; + } // namespace omaha #endif // OMAHA_GOOPDATE_BROKER_CLASS_FACTORY_H_ diff --git a/omaha/goopdate/build.scons b/omaha/goopdate/build.scons index 57c666158..b0da2c2b8 100644 --- a/omaha/goopdate/build.scons +++ b/omaha/goopdate/build.scons @@ -66,7 +66,7 @@ def BuildCOMForwarder(cmd_line_switch, prog_name='%s_unsigned' % signed_exe_name, source=com_forwarder_inputs, ) - signed_broker = com_forwarder_env.DualSignedBinary( + signed_broker = com_forwarder_env.SignedBinary( target='%s.exe' % signed_exe_name, source=unsigned_broker, ) @@ -76,7 +76,6 @@ def BuildCOMForwarder(cmd_line_switch, # Build the broker/legacy on-demand/WebPlugin forwarders. BuildCOMForwarder('/broker', 'GoogleUpdateBroker', False) BuildCOMForwarder('/ondemand', 'GoogleUpdateOnDemand', False) -BuildCOMForwarder('/pi', 'GoogleUpdateWebPlugin', True) def BuildCOMRegisterShell64(): com_register_shell_env = env.CloneAndMake64Bit() @@ -95,13 +94,11 @@ def BuildCOMRegisterShell64(): '$LIB_DIR/common_64.lib', '$LIB_DIR/logging_64.lib', '$LIB_DIR/goopdate_lib_64.lib', - '$LIB_DIR/security_64.lib', com_register_shell_env['atls_libs'][ com_register_shell_env.Bit('debug') ], com_register_shell_env['crt_libs'][ com_register_shell_env.Bit('debug')], - 'crypt32.lib', 'delayimp.lib', 'netapi32.lib', 'psapi.lib', @@ -119,14 +116,28 @@ def BuildCOMRegisterShell64(): ], ) + # Add an icon, a manifest, and a version to the exe. + # The resource file used is the same as the one for GoogleUpdate.exe. + # The version resource used is the same as the one for goopdate.dll. + signed_exe_name = 'GoogleUpdateComRegisterShell64' + com_register_shell_res = com_register_shell_env.RES( + '%s.res' % signed_exe_name, + '../google_update/resource.rc') + com_register_shell_env.Depends(com_register_shell_res, + '../google_update/GoogleUpdate.manifest') + com_register_shell_inputs = [ + com_register_shell_env.ComponentObject('%s.obj' % signed_exe_name, + 'com_register_shell.cc'), + com_register_shell_res, + '$OBJ_ROOT/goopdate/goopdate_version.res', + ] + unsigned_exe = com_register_shell_env.ComponentProgram( - prog_name='GoogleUpdateComRegisterShell64_unsigned', - source= [ - 'com_register_shell.cc', - ] + prog_name='%s_unsigned' % signed_exe_name, + source=com_register_shell_inputs ) - signed_exe = com_register_shell_env.DualSignedBinary( - target='GoogleUpdateComRegisterShell64.exe', + signed_exe = com_register_shell_env.SignedBinary( + target='%s.exe' % signed_exe_name, source=unsigned_exe, ) env.Replicate('$STAGING_DIR', signed_exe) @@ -142,7 +153,7 @@ def GenerateOmaha3IDLFile(): return env.Command( target='omaha3_idl.idl', source='$MAIN_DIR/goopdate/omaha3_idl.idl', - action=('python %s/tools/generate_omaha3_idl.py --idl_template_file ' + action=('python "%s/tools/generate_omaha3_idl.py" --idl_template_file ' '$SOURCE --idl_output_file $TARGET' % env['MAIN_DIR']) ) @@ -207,9 +218,6 @@ def BuildGoogleUpdateHandlerDll(handler_env, '/wd4426', '/wd5038', ], - CPPPATH = [ - '$OBJ_ROOT', # Needed for generated files. - ], LIBS = [ handler_env.GetMultiarchLibName('base'), handler_env.GetMultiarchLibName('goopdate_lib'), @@ -283,7 +291,7 @@ def BuildGoogleUpdateHandlerDll(handler_env, source=inputs, ) - signed_dll = handler_env.DualSignedBinary( + signed_dll = handler_env.SignedBinary( target='%s%s%s.dll' % (prefix, psname, suffix), source=unsigned_dll, ) @@ -309,10 +317,21 @@ if env.Bit('has_device_management'): proto_env.Append(PROTO_PATH = '$GOOGLE3') proto_env.Append(CPP_OUT = '$TARGET_ROOT/proto_files') proto_sources = [ + '$GOOGLE3/devtools/staticanalysis/pipeline/analyzers/' + + 'proto_best_practices/proto/optouts.proto', '$GOOGLE3/logs/proto/logs_annotations/logs_annotations.proto', + '$GOOGLE3/net/proto2/bridge/proto/message_set.proto', + '$GOOGLE3/net/proto2/proto/descriptor.proto', + '$GOOGLE3/privacy/private_membership/proto/private_membership.proto', + '$GOOGLE3/privacy/private_membership/rlwe/proto/private_membership_rlwe.proto', + '$GOOGLE3/storage/datapol/annotations/proto/semantic_annotations.proto', + '$GOOGLE3/storage/datapol/annotations/proto/retention_annotations.proto', + '$GOOGLE3/storage/datapol/annotations/proto/datapol_classification.proto', + '$GOOGLE3/third_party/rlwe/serialization.proto', '$GOOGLE3/wireless/android/enterprise/devicemanagement/proto/' + 'dm_api.proto', - '$GOOGLE3/net/proto2/proto/descriptor.proto', + '$GOOGLE3/wireless/android/enterprise/devicemanagement/proto/' + + 'omaha_settings.proto', ] proto_outputs = proto_env.CompileProtoBuf(proto_sources) @@ -321,12 +340,14 @@ if env.Bit('has_device_management'): '$GOOGLE3/third_party/protobuf/src') proto_env.Append( CCFLAGS=[ + '/wd4065', '/wd4100', '/wd4125', '/wd4127', '/wd4146', '/wd4242', '/wd4244', + '/wd4267', '/wd4309', '/wd4548', '/wd4577', @@ -337,7 +358,6 @@ if env.Bit('has_device_management'): '/wd4996', ], CPPPATH=[ - '$GOOGLE3', protobuf_src_dir, '$TARGET_ROOT/proto_files', ], @@ -415,6 +435,8 @@ gd_inputs = [ 'package.cc', 'package_cache.cc', 'ping_event_cancel.cc', + 'policy_status.cc', + 'policy_status_value.cc', 'process_launcher.cc', 'resource_manager.cc', 'update3web.cc', @@ -451,7 +473,6 @@ for omaha_version_info in env['omaha_versions_info']: temp_env.Append( CPPPATH = [ - '$MAIN_DIR/third_party/breakpad/src/', '$OBJ_ROOT', ], @@ -485,7 +506,6 @@ for omaha_version_info in env['omaha_versions_info']: 'delayimp.lib', 'imagehlp.lib', 'iphlpapi.lib', - 'msi.lib', 'msimg32.lib', 'mstask.lib', 'netapi32.lib', @@ -567,7 +587,7 @@ for omaha_version_info in env['omaha_versions_info']: source=inputs, ) - signed_dll = temp_env.DualSignedBinary( + signed_dll = temp_env.SignedBinary( target=prefix + 'goopdate.dll', source=unsigned_dll, ) @@ -637,7 +657,7 @@ unsigned_test_installer = test_installer_env.ComponentProgram( prog_name='TestOmahaExeInstaller_unsigned', source=test_installer_inputs, ) -signed_test_installer = test_installer_env.DualSignedBinary( +signed_test_installer = test_installer_env.SignedBinary( target='TestOmahaExeInstaller.exe', source=unsigned_test_installer, ) diff --git a/omaha/goopdate/cocreate_async.cc b/omaha/goopdate/cocreate_async.cc index 79854525a..02789537a 100644 --- a/omaha/goopdate/cocreate_async.cc +++ b/omaha/goopdate/cocreate_async.cc @@ -71,26 +71,31 @@ CoCreateAsyncStatus::CoCreateAsyncStatus() : is_done_(false), hr_(E_PENDING) { HRESULT CoCreateAsyncStatus::CreateOmahaMachineServerAsync( BSTR origin_url, BOOL create_elevated) { + // We AddRef here on behalf of the threadpool item below, since we want the + // object and the process to stick around until the newly created threadpool + // item below starts and also completes execution. The corresponding Release + // is done at the end of the threadpool proc. + AddRef(); + ScopeGuard addref_guard = MakeObjGuard(*this, &CoCreateAsyncStatus::Release); + // Create a thread pool work item for deferred execution of the CoCreate. The // thread pool owns this call back object. using CallBack = ThreadPoolCallBack2; - auto callback = std::make_unique(this, - &CoCreateAsyncStatus::CreateOmahaMachineServer, - origin_url, - create_elevated); - HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), - COINIT_MULTITHREADED, - WT_EXECUTELONGFUNCTION); + const CString, + BOOL>; + HRESULT hr = Goopdate::Instance().QueueUserWorkItem( + std::make_unique(this, + &CoCreateAsyncStatus::CreateOmahaMachineServer, + origin_url, + create_elevated), + COINIT_MULTITHREADED, + WT_EXECUTELONGFUNCTION); if (FAILED(hr)) { CORE_LOG(LE, (_T("[QueueUserWorkItem failed][0x%x]"), hr)); return hr; } - VERIFY1(thread_started_gate_.Wait(INFINITE)); - - callback.release(); + addref_guard.Dismiss(); return S_OK; } @@ -98,11 +103,8 @@ void CoCreateAsyncStatus::CreateOmahaMachineServer(const CString origin_url, BOOL create_elevated) { CORE_LOG(L3, (_T("[CoCreateAsyncStatus::CreateOmahaMachineServer][%s][%d]"), origin_url, create_elevated)); - AddRef(); ON_SCOPE_EXIT_OBJ(*this, &CoCreateAsyncStatus::Release); - VERIFY1(thread_started_gate_.Open()); - HRESULT hr = E_FAIL; CComPtr ptr; diff --git a/omaha/goopdate/cocreate_async.h b/omaha/goopdate/cocreate_async.h index cc2affeb4..0f6b987ac 100644 --- a/omaha/goopdate/cocreate_async.h +++ b/omaha/goopdate/cocreate_async.h @@ -94,8 +94,6 @@ class ATL_NO_VTABLE CoCreateAsyncStatus void SetCreateInstanceResults(const HRESULT& hr, const CComPtr& ptr); - Gate thread_started_gate_; - bool is_done_; HRESULT hr_; CComPtr ptr_; diff --git a/omaha/goopdate/code_red_check.cc b/omaha/goopdate/code_red_check.cc index e591c6e73..e20c155b6 100644 --- a/omaha/goopdate/code_red_check.cc +++ b/omaha/goopdate/code_red_check.cc @@ -18,6 +18,7 @@ #include "omaha/base/scoped_impersonation.h" #include "omaha/base/utils.h" #include "omaha/base/vista_utils.h" +#include "omaha/common/config_manager.h" #include "omaha/common/goopdate_utils.h" #include "omaha/goopdate/goopdate_metrics.h" #include "omaha/net/network_request.h" @@ -147,16 +148,19 @@ HRESULT DownloadCodeRedFileAsLoggedOnUser(const TCHAR* url, } } - const DWORD kMoveFlag = MOVEFILE_COPY_ALLOWED | - MOVEFILE_REPLACE_EXISTING | - MOVEFILE_WRITE_THROUGH; - if (!::MoveFileEx(download_target_path, - file_path, - kMoveFlag)) { + if (!::CopyFile(download_target_path, file_path, FALSE)) { hr = HRESULT_FROM_WIN32(::GetLastError()); } - ::DeleteFile(download_target_path); + { + scoped_impersonation impersonate_user(get(logged_on_user_token)); + HRESULT hr_impersonation = HRESULT_FROM_WIN32(impersonate_user.result()); + if (FAILED(hr_impersonation)) { + return hr_impersonation; + } + + ::DeleteFile(download_target_path); + } return hr; } @@ -198,6 +202,14 @@ HRESULT CodeRedDownloadCallback(const TCHAR* url, } // namespace HRESULT CheckForCodeRed(bool is_machine, const CString& omaha_version) { + bool is_period_overridden = false; + const int update_interval = + ConfigManager::Instance()->GetLastCheckPeriodSec(&is_period_overridden); + if (is_period_overridden && 0 == update_interval) { + OPT_LOG(L1, (_T("[GetLastCheckPeriodSec is 0][code red checks disabled]"))); + return HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY); + } + HRESULT hr = FixGoogleUpdate(kGoogleUpdateAppId, omaha_version, _T(""), // Omaha doesn't have a language. diff --git a/omaha/goopdate/com_proxy.h b/omaha/goopdate/com_proxy.h index 91e8a5953..7bfc8e792 100644 --- a/omaha/goopdate/com_proxy.h +++ b/omaha/goopdate/com_proxy.h @@ -62,6 +62,10 @@ const IID kIIDsToRegister[] = { __uuidof(ICoCreateAsync), __uuidof(ICoCreateAsyncStatus), __uuidof(ICredentialDialog), + __uuidof(IPolicyStatus), + __uuidof(IPolicyStatus2), + __uuidof(IPolicyStatus3), + __uuidof(IPolicyStatusValue), __uuidof(IProcessLauncher2), @@ -94,7 +98,7 @@ struct ComProxyMode { } } - static const TCHAR* const hk_root() { + static const TCHAR* hk_root() { return is_machine() ? _T("HKLM") : _T("HKCU"); } }; @@ -195,6 +199,7 @@ class ATL_NO_VTABLE ComProxy CComPtr proxy_manager_; CComPtr proxy_internal_unknown_; + private: DISALLOW_COPY_AND_ASSIGN(ComProxy); }; @@ -203,7 +208,7 @@ class StdMarshalInfo : public IStdMarshalInfo { explicit StdMarshalInfo(bool is_machine) : is_machine_(is_machine) { CORE_LOG(L6, (_T("[StdMarshalInfo::StdMarshalInfo][%d]"), is_machine)); - VERIFY1(SUCCEEDED(ComProxy::RegisterProxyStubs())); + VERIFY_SUCCEEDED(ComProxy::RegisterProxyStubs()); } // IStdMarshalInfo. @@ -225,4 +230,3 @@ class StdMarshalInfo : public IStdMarshalInfo { } // namespace omaha #endif // OMAHA_GOOPDATE_COM_PROXY_H_ - diff --git a/omaha/goopdate/com_register_shell.cc b/omaha/goopdate/com_register_shell.cc index a151dd82e..37cecdaea 100644 --- a/omaha/goopdate/com_register_shell.cc +++ b/omaha/goopdate/com_register_shell.cc @@ -17,14 +17,12 @@ #include #include #include -#include "base/basictypes.h" #include "omaha/base/app_util.h" #include "omaha/base/command_line_parser.h" #include "omaha/base/constants.h" #include "omaha/base/debug.h" #include "omaha/base/error.h" #include "omaha/base/logging.h" -#include "omaha/base/path.h" #include "omaha/base/scope_guard.h" #include "omaha/base/utils.h" #include "omaha/common/const_cmd_line.h" diff --git a/omaha/goopdate/crash.cc b/omaha/goopdate/crash.cc index 6978e092d..6c43184aa 100644 --- a/omaha/goopdate/crash.cc +++ b/omaha/goopdate/crash.cc @@ -145,10 +145,10 @@ HRESULT CrashReporter::Report(const CString& crash_filename, is_out_of_process, can_upload, crash_filename); - VERIFY1(SUCCEEDED(WriteToWindowsEventLog(EVENTLOG_ERROR_TYPE, + VERIFY_SUCCEEDED(WriteToWindowsEventLog(EVENTLOG_ERROR_TYPE, kCrashReportEventId, product_name, - event_text))); + event_text)); // Upload the crash. CString report_id; HRESULT hr = DoSendCrashReport(can_upload, @@ -222,7 +222,7 @@ HRESULT CrashReporter::DoSendCrashReport(bool can_upload, } CString product_name = ReadMapProductName(parameters); - VERIFY1(SUCCEEDED(SaveLastCrash(crash_filename, product_name))); + VERIFY_SUCCEEDED(SaveLastCrash(crash_filename, product_name)); HRESULT hr = S_OK; if (can_upload) { @@ -308,10 +308,10 @@ HRESULT CrashReporter::UploadCrash(bool is_out_of_process, event_type = EVENTLOG_WARNING_TYPE; SafeCStringFormat(&event_text, _T("Crash not uploaded. Error=0x%x."), hr); } - VERIFY1(SUCCEEDED(WriteToWindowsEventLog(event_type, + VERIFY_SUCCEEDED(WriteToWindowsEventLog(event_type, kCrashUploadEventId, product_name, - event_text))); + event_text)); UpdateCrashUploadMetrics(is_out_of_process, hr); diff --git a/omaha/goopdate/crash_unittest.cc b/omaha/goopdate/crash_unittest.cc index dd97979a3..9d77cac3c 100644 --- a/omaha/goopdate/crash_unittest.cc +++ b/omaha/goopdate/crash_unittest.cc @@ -14,8 +14,9 @@ // ======================================================================== #include +#include + #include "omaha/base/app_util.h" -#include "omaha/base/atl_regexp.h" #include "omaha/base/constants.h" #include "omaha/base/const_addresses.h" #include "omaha/base/const_object_names.h" @@ -152,7 +153,7 @@ TEST_F(CrashReporterTest, DISABLED_Report_OmahaCrash) { // Tests sending an out-of-process crash. // This test will write an entry with the source "Chrome" in the Event Log. -// TODO(omaha): This test is disabled because it hangs on Zerg machines with +// TODO(omaha): This test is disabled because it hangs on machines with // network connectivity issues. A google_breakpad::RESULT_FAILED causes a 1 hour // ::Sleep(). TEST_F(CrashReporterTest, DISABLED_Report_ProductCrash) { @@ -184,9 +185,8 @@ TEST_F(CrashReporterTest, DISABLED_Report_ProductCrash) { const CString strings = GetLastCrashEventStrings(); // Verify that the strings include the Id token. - AtlRE crash_id_regex(_T("Id={\\h+}.")); - CString crash_id; - EXPECT_TRUE(AtlRE::PartialMatch(strings, crash_id_regex, &crash_id)); + const std::wregex crash_id_regex {_T("Id=[[:xdigit:]]+\\.")}; + EXPECT_TRUE(std::regex_search(strings.GetString(), crash_id_regex)); // The crash artifacts should be deleted after the crash is reported. EXPECT_FALSE(File::Exists(crash_filename)); diff --git a/omaha/goopdate/cred_dialog.cc b/omaha/goopdate/cred_dialog.cc index ec6199028..90d97f0e3 100644 --- a/omaha/goopdate/cred_dialog.cc +++ b/omaha/goopdate/cred_dialog.cc @@ -19,6 +19,7 @@ #include "omaha/base/logging.h" #include "omaha/base/scoped_impersonation.h" #include "omaha/base/system_info.h" +#include "omaha/base/utils.h" #include "omaha/common/goopdate_utils.h" #include "omaha/client/resource.h" #include "omaha/goopdate/cred_dialog.h" @@ -86,7 +87,7 @@ DWORD CredentialDialogBase::DisplayDialog( LPCTSTR caption, CString* username_out, CString* password_out) { - scoped_library credui_lib(::LoadLibrary(L"credui.dll")); + scoped_library credui_lib(LoadSystemLibrary(_T("credui.dll"))); ASSERT1(credui_lib); if (!credui_lib) { CORE_LOG(L3, (_T("[CredUIPromptForCredentialsW not available]"))); diff --git a/omaha/goopdate/cred_dialog.h b/omaha/goopdate/cred_dialog.h index 6a9f4c925..639d8507f 100644 --- a/omaha/goopdate/cred_dialog.h +++ b/omaha/goopdate/cred_dialog.h @@ -86,7 +86,7 @@ class ATL_NO_VTABLE CredentialDialog REGMAP_ENTRY(_T("HKROOT"), T::hk_root()) REGMAP_ENTRY(_T("VERSION"), _T("1.0")) REGMAP_ENTRY(_T("PROGID"), T::prog_id()) - REGMAP_ENTRY(_T("DESCRIPTION"), _T("GoogleUpdate CredentialDialog")) + REGMAP_ENTRY(_T("DESCRIPTION"), MAIN_EXE_BASE_NAME _T(" CredentialDialog")) REGMAP_ENTRY(_T("CLSID"), T::class_id()) REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName) END_REGISTRY_MAP() @@ -101,18 +101,18 @@ class ATL_NO_VTABLE CredentialDialog struct CredentialDialogModeUser { static bool is_machine() { return false; } - static const TCHAR* const prog_id() { return kProgIDCredentialDialogUser; } + static const TCHAR* prog_id() { return kProgIDCredentialDialogUser; } static GUID class_id() { return __uuidof(CredentialDialogUserClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; } - static const TCHAR* const hk_root() { return _T("HKCU"); } + static const TCHAR* hk_root() { return _T("HKCU"); } }; struct CredentialDialogModeMachine { static bool is_machine() { return true; } - static const TCHAR* const prog_id() { return kProgIDCredentialDialogMachine; } + static const TCHAR* prog_id() { return kProgIDCredentialDialogMachine; } static GUID class_id() { return __uuidof(CredentialDialogMachineClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; } - static const TCHAR* const hk_root() { return _T("HKLM"); } + static const TCHAR* hk_root() { return _T("HKLM"); } }; typedef CredentialDialog CredentialDialogUser; diff --git a/omaha/goopdate/dm_client.cc b/omaha/goopdate/dm_client.cc index 0469ef60b..83971885a 100644 --- a/omaha/goopdate/dm_client.cc +++ b/omaha/goopdate/dm_client.cc @@ -26,6 +26,7 @@ #include "omaha/base/safe_format.h" #include "omaha/base/string.h" #include "omaha/base/system_info.h" +#include "omaha/base/utils.h" #include "omaha/common/config_manager.h" #include "omaha/common/goopdate_utils.h" #include "omaha/common/url_utils.h" @@ -53,9 +54,9 @@ RegistrationState GetRegistrationState(DmStorage* dm_storage) { // Log categories are used within this function as follows: // OPT: diagnostic messages to be included in the log file in release builds. // REPORT: error messages to be included in the Windows Event Log. -HRESULT RegisterIfNeeded(DmStorage* dm_storage) { +HRESULT RegisterIfNeeded(DmStorage* dm_storage, bool is_foreground) { ASSERT1(dm_storage); - OPT_LOG(L1, (_T("[DmClient::RegisterIfNeeded]"))); + OPT_LOG(L1, (_T("[DmClient::RegisterIfNeeded][%d]"), is_foreground)); // No work to be done if the process is not running as an administrator, since // we will not be able to persist anything. @@ -64,6 +65,11 @@ HRESULT RegisterIfNeeded(DmStorage* dm_storage) { return S_FALSE; } + if (dm_storage->IsInvalidDMToken()) { + OPT_LOG(L1, (_T("[Cannot register since DM Token is invalid]"))); + return E_FAIL; + } + // No work to be done if a DM token was found. CStringA dm_token = dm_storage->GetDmToken(); if (!dm_token.IsEmpty()) { @@ -85,21 +91,20 @@ HRESULT RegisterIfNeeded(DmStorage* dm_storage) { return E_FAIL; } - // RegisterWithRequest owns the SimpleRequest being created here. - HRESULT hr = internal::RegisterWithRequest(new SimpleRequest, - enrollment_token, - device_id, - &dm_token); - if (FAILED(hr)) { - return hr; - } - - hr = dm_storage->StoreDmToken(dm_token); - if (FAILED(hr)) { - REPORT_LOG(LE, (_T("[StoreDmToken failed][%#x]"), hr)); - return hr; + if (!is_foreground) { + // Waits a while before registration. We are reusing the AutoUpdateJitterMs + // for simplicity. + const int dm_jitter_ms(ConfigManager::Instance()->GetAutoUpdateJitterMs()); + if (dm_jitter_ms > 0) { + OPT_LOG(L1, (_T("[Applying DM Registration jitter][%d]"), dm_jitter_ms)); + ::Sleep(dm_jitter_ms); + } } + // RegisterWithRequest owns the SimpleRequest being created here. + HRESULT hr = internal::RegisterWithRequest(dm_storage, + std::make_unique(), + enrollment_token, device_id); OPT_LOG(L1, (_T("[Registration complete]"))); return S_OK; @@ -109,14 +114,19 @@ HRESULT RefreshPolicies() { // No work to be done if the process is not running as an administrator, since // we will not be able to persist anything. if (!::IsUserAnAdmin()) { - OPT_LOG(L1, (_T("[RefreshPolicies][Process not Admin, exiting early]"))); + REPORT_LOG(L1, (_T("[RefreshPolicies][Process not Admin, exiting early]"))); return S_FALSE; } DmStorage* const dm_storage = DmStorage::Instance(); + if (dm_storage->IsInvalidDMToken()) { + REPORT_LOG(L1, (_T("[Skipping RefreshPolicies as DMToken is invalid]"))); + return E_FAIL; + } + const CString dm_token = CString(dm_storage->GetDmToken()); if (dm_token.IsEmpty()) { - OPT_LOG(L1, (_T("[Skipping RefreshPolicies as there is no DMToken]"))); + REPORT_LOG(L1, (_T("[Skipping RefreshPolicies as there is no DMToken]"))); return S_FALSE; } @@ -126,40 +136,48 @@ HRESULT RefreshPolicies() { return E_FAIL; } - PolicyResponsesMap responses; + CachedPolicyInfo info; + HRESULT hr = dm_storage->ReadCachedPolicyInfoFile(&info); + if (FAILED(hr)) { + REPORT_LOG(LW, (_T("[ReadCachedPolicyInfoFile failed][%#x]"), hr)); + // Not fatal, continue. + } - // FetchPolicies owns the SimpleRequest being created here. - HRESULT hr = internal::FetchPolicies(new SimpleRequest, - dm_token, - device_id, - &responses); + PolicyResponses responses; + hr = internal::FetchPolicies(dm_storage, std::make_unique(), + dm_token, device_id, info, &responses); if (FAILED(hr)) { REPORT_LOG(LE, (_T("[FetchPolicies failed][%#x]"), hr)); return hr; } - const CPath policy_responses_dir( - ConfigManager::Instance()->GetPolicyResponsesDir()); - - hr = DmStorage::PersistPolicies(policy_responses_dir, responses); + hr = dm_storage->PersistPolicies(responses); if (FAILED(hr)) { REPORT_LOG(LE, (_T("[PersistPolicies failed][%#x]"), hr)); return hr; } - OPT_LOG(L1, (_T("[RefreshPolicies complete]"))); + CachedOmahaPolicy dm_policy; + hr = dm_storage->ReadCachedOmahaPolicy(&dm_policy); + if (FAILED(hr)) { + OPT_LOG(LE, (_T("[ReadCachedOmahaPolicy failed][%#x]"), hr)); + } else { + ConfigManager::Instance()->SetOmahaDMPolicies(dm_policy); + } + + REPORT_LOG(L1, (_T("[RefreshPolicies complete]"))); return S_OK; } namespace internal { -HRESULT RegisterWithRequest(HttpRequestInterface* http_request, +HRESULT RegisterWithRequest(DmStorage* dm_storage, + std::unique_ptr http_request, const CString& enrollment_token, - const CString& device_id, - CStringA* dm_token) { - ASSERT1(http_request); - ASSERT1(dm_token); + const CString& device_id) { + ASSERT1(dm_storage); + ASSERT1(http_request.get()); std::vector> query_params = { {_T("request"), _T("register_policy_agent")}, @@ -168,6 +186,7 @@ HRESULT RegisterWithRequest(HttpRequestInterface* http_request, // Make the request payload. CStringA payload = SerializeRegisterBrowserRequest( WideToUtf8(app_util::GetHostName()), + WideToUtf8(SystemInfo::GetSerialNumber()), CStringA("Windows"), internal::GetOsVersion()); if (payload.IsEmpty()) { @@ -177,31 +196,59 @@ HRESULT RegisterWithRequest(HttpRequestInterface* http_request, std::vector response; HRESULT hr = SendDeviceManagementRequest( - http_request, - payload, - internal::FormatEnrollmentTokenAuthorizationHeader(enrollment_token), - device_id, - std::move(query_params), - &response); + std::move(http_request), payload, + internal::FormatEnrollmentTokenAuthorizationHeader(enrollment_token), + device_id, std::move(query_params), &response); + if (FAILED(hr)) { REPORT_LOG(LE, (_T("[SendDeviceManagementRequest failed][%#x]"), hr)); + internal::HandleDMResponseError(dm_storage, hr, response); return hr; } - hr = ParseDeviceRegisterResponse(response, dm_token); + CStringA dm_token; + hr = ParseDeviceRegisterResponse(response, &dm_token); if (FAILED(hr)) { REPORT_LOG(LE, (_T("[ParseDeviceRegisterResponse failed][%#x]"), hr)); return hr; } + hr = dm_storage->StoreDmToken(dm_token); + if (FAILED(hr)) { + REPORT_LOG(LE, (_T("[StoreDmToken failed][%#x]"), hr)); + return hr; + } + return S_OK; } -HRESULT FetchPolicies(HttpRequestInterface* http_request, - const CString& dm_token, - const CString& device_id, - PolicyResponsesMap* responses) { - ASSERT1(http_request); +HRESULT SendPolicyValidationResultReportIfNeeded( + std::unique_ptr http_request, const CString& dm_token, + const CString& device_id, const PolicyValidationResult& validation_result) { + const CStringA payload = + SerializePolicyValidationReportRequest(validation_result); + if (payload.IsEmpty()) return S_OK; + + std::vector> query_params = { + {_T("request"), _T("policy_validation_report")}, + }; + + std::vector response; + HRESULT hr = SendDeviceManagementRequest( + std::move(http_request), payload, + FormatDMTokenAuthorizationHeader(dm_token), device_id, + std::move(query_params), &response); + REPORT_LOG(L1, (_T("[SendPolicyValidationResultReportIfNeeded][%#x]"), hr)); + return hr; +} + +HRESULT FetchPolicies(DmStorage* dm_storage, + std::unique_ptr http_request, + const CString& dm_token, const CString& device_id, + const CachedPolicyInfo& info, + PolicyResponses* responses) { + ASSERT1(dm_storage); + ASSERT1(http_request.get()); ASSERT1(!dm_token.IsEmpty()); ASSERT1(responses); @@ -210,7 +257,10 @@ HRESULT FetchPolicies(HttpRequestInterface* http_request, }; CStringA payload = SerializePolicyFetchRequest( - CStringA(kGoogleUpdateMachineLevelApps)); + WideToUtf8(app_util::GetHostName()), + WideToUtf8(SystemInfo::GetSerialNumber()), + CStringA(kGoogleUpdateMachineLevelApps), + info); if (payload.IsEmpty()) { REPORT_LOG(LE, (_T("[SerializePolicyFetchRequest failed]"))); return E_FAIL; @@ -218,20 +268,27 @@ HRESULT FetchPolicies(HttpRequestInterface* http_request, std::vector response; HRESULT hr = SendDeviceManagementRequest( - http_request, - payload, - FormatDMTokenAuthorizationHeader(dm_token), - device_id, - std::move(query_params), - &response); + std::move(http_request), payload, + FormatDMTokenAuthorizationHeader(dm_token), device_id, + std::move(query_params), &response); + if (FAILED(hr)) { REPORT_LOG(LE, (_T("[SendDeviceManagementRequest failed][%#x]"), hr)); + internal::HandleDMResponseError(dm_storage, hr, response); return hr; } - hr = ParseDevicePolicyResponse(response, responses); + std::vector validation_results; + hr = ParseDevicePolicyResponse(response, info, dm_token, device_id, responses, + &validation_results); + for (const PolicyValidationResult& validation_result : validation_results) { + SendPolicyValidationResultReportIfNeeded(std::make_unique(), + dm_token, device_id, + validation_result); + } + if (FAILED(hr)) { - REPORT_LOG(LE, (_T("[ParseDeviceRegisterResponse failed][%#x]"), hr)); + REPORT_LOG(LE, (_T("[ParseDevicePolicyResponse failed][%#x]"), hr)); return hr; } @@ -239,13 +296,11 @@ HRESULT FetchPolicies(HttpRequestInterface* http_request, } HRESULT SendDeviceManagementRequest( - HttpRequestInterface* http_request, - const CStringA& payload, - const CString& authorization_header, - const CString& device_id, + std::unique_ptr http_request, const CStringA& payload, + const CString& authorization_header, const CString& device_id, std::vector> query_params, std::vector* response) { - ASSERT1(http_request); + ASSERT1(http_request.get()); ASSERT1(response); // Get the network configuration. @@ -261,10 +316,11 @@ HRESULT SendDeviceManagementRequest( // Create a network request and configure its headers. std::unique_ptr request( new NetworkRequest(network_config->session())); + request->AddHeader(_T("Content-Type"), kProtobufContentType); request->AddHeader(_T("Authorization"), authorization_header); // Set it up - request->AddHttpRequest(http_request); + request->AddHttpRequest(http_request.release()); // Form the request URL with query params. CString url; @@ -292,13 +348,13 @@ HRESULT SendDeviceManagementRequest( } const int http_status_code = request->http_status_code(); - if (http_status_code != 200) { + if (http_status_code != HTTP_STATUS_OK) { REPORT_LOG(LE, (_T("[NetworkRequest::Post failed][status code %d]"), http_status_code)); CStringA error_message; hr = ParseDeviceManagementResponseError(*response, &error_message); if (SUCCEEDED(hr)) { - OPT_LOG(LE, (_T("[Server returned: %S]"), error_message)); + REPORT_LOG(LE, (_T("[Server returned: %S]"), error_message)); } return E_FAIL; } @@ -306,6 +362,33 @@ HRESULT SendDeviceManagementRequest( return S_OK; } +void HandleDMResponseError(DmStorage* dm_storage, HRESULT hr, + const std::vector& response) { + ASSERT1(dm_storage); + + if (hr != HRESULTFromHttpStatusCode(HTTP_STATUS_GONE)) { + REPORT_LOG(LE, (_T("Unexpected HTTP error from the DM Server[%#x]"), hr)); + return; + } + + // HTTP_STATUS_GONE implies that the device has been unenrolled. + // Update the DM token and delete cached policies. + if (ShouldDeleteDmToken(response)) { + REPORT_LOG(L1, (_T("Remove DM token based on server's response."))); + dm_storage->DeleteDmToken(); + } else { + REPORT_LOG(L1, (_T("Invalidate DM token based on server's response."))); + dm_storage->InvalidateDMToken(); + } + + DeleteBeforeOrAfterReboot(dm_storage->policy_responses_dir()); + + // Set the Omaha DM Policies to an empty CachedOmahaPolicy so that the + // currently-running process forgets about the policies loaded in + // GoopdateImpl::InitializeGoopdateAndLoadResources. + ConfigManager::Instance()->SetOmahaDMPolicies(CachedOmahaPolicy()); +} + CString GetAgent() { CString agent; SafeCStringFormat(&agent, _T("%s %s()"), kAppName, GetVersionString()); @@ -313,7 +396,7 @@ CString GetAgent() { } CString GetPlatform() { - const DWORD architecture = SystemInfo::GetProcessorArchitecture(); + const CString architecture = SystemInfo::GetArchitecture(); int major_version = 0; int minor_version = 0; @@ -327,11 +410,7 @@ CString GetPlatform() { CString platform; SafeCStringFormat(&platform, _T("Windows NT|%s|%d.%d.0"), - architecture == PROCESSOR_ARCHITECTURE_AMD64 ? - _T("x86_64") : - (architecture == PROCESSOR_ARCHITECTURE_INTEL ? - _T("x86") : - _T("")), + architecture == kArchAmd64 ? _T("x86_64") : architecture, major_version, minor_version); return platform; } diff --git a/omaha/goopdate/dm_client.h b/omaha/goopdate/dm_client.h index 896e32541..e43fe6b15 100644 --- a/omaha/goopdate/dm_client.h +++ b/omaha/goopdate/dm_client.h @@ -16,6 +16,7 @@ #define OMAHA_GOOPDATE_DM_CLIENT_H__ #include +#include #include #include #include @@ -32,6 +33,9 @@ namespace dm_client { // applications from the DMServer. const char kGoogleUpdateMachineLevelApps[] = "google/machine-level-apps"; +// The content-type for all protocol buffer requests. +const TCHAR kProtobufContentType[] = _T("application/protobuf"); + enum RegistrationState { // This client appears to not be managed. In particular, neither a device // management token nor an enrollment token can be found. @@ -51,33 +55,44 @@ RegistrationState GetRegistrationState(DmStorage* dm_storage); // Returns S_OK if registration takes place and succeeds, S_FALSE if // registration was not needed (either it has already been done, or no // enrollment token is found), or a failure HRESULT in case of error. -HRESULT RegisterIfNeeded(DmStorage* dm_storage); +// If |is_foreground| is false, RegisterIfNeeded applies a wait before running +// the registration, since the DM server is rate-limited. The wait is a random +// value in the range [0, 60000] milisecond. +// (up to one minute). +HRESULT RegisterIfNeeded(DmStorage* dm_storage, bool is_foreground); // Retrieve and persist locally the policies from the Device Management Server. HRESULT RefreshPolicies(); namespace internal { -HRESULT RegisterWithRequest(HttpRequestInterface* http_request, +HRESULT RegisterWithRequest(DmStorage* dm_storage, + std::unique_ptr http_request, const CString& enrollment_token, - const CString& device_id, - CStringA* dm_token); + const CString& device_id); + +// Sends policy validation result back to DM Server. +HRESULT SendPolicyValidationResultReportIfNeeded( + std::unique_ptr http_request, const CString& dm_token, + const CString& device_id, const PolicyValidationResult& validation_result); // Fetch policies from the DMServer. The policies are returned in |responses| // containing elements in the following format: // {policy_type}=>{SerializeToString-PolicyFetchResponse}. -HRESULT FetchPolicies(HttpRequestInterface* http_request, - const CString& dm_token, - const CString& device_id, - PolicyResponsesMap* responses); +HRESULT FetchPolicies(DmStorage* dm_storage, + std::unique_ptr http_request, + const CString& dm_token, const CString& device_id, + const CachedPolicyInfo& info, PolicyResponses* responses); HRESULT SendDeviceManagementRequest( - HttpRequestInterface* http_request, - const CStringA& payload, - const CString& authorization_header, - const CString& device_id, + std::unique_ptr http_request, const CStringA& payload, + const CString& authorization_header, const CString& device_id, std::vector> query_params, std::vector* response); + +void HandleDMResponseError(DmStorage* dm_storage, HRESULT hr, + const std::vector& response); + CString GetAgent(); CString GetPlatform(); CStringA GetOsVersion(); diff --git a/omaha/goopdate/dm_client_unittest.cc b/omaha/goopdate/dm_client_unittest.cc index 26bb241d1..eb3cc34d0 100644 --- a/omaha/goopdate/dm_client_unittest.cc +++ b/omaha/goopdate/dm_client_unittest.cc @@ -14,6 +14,9 @@ #include "omaha/goopdate/dm_client.h" +#include +#include +#include #include #include #include @@ -21,7 +24,13 @@ #include #include "base/basictypes.h" +#include "crypto/rsa_private_key.h" +#include "crypto/signature_creator.h" #include "gtest/gtest-matchers.h" +#include "omaha/base/app_util.h" +#include "omaha/base/constants.h" +#include "omaha/base/error.h" +#include "omaha/base/path.h" #include "omaha/base/scope_guard.h" #include "omaha/base/string.h" #include "omaha/common/config_manager.h" @@ -30,6 +39,7 @@ #include "omaha/net/http_request.h" #include "omaha/testing/unit_test.h" #include "wireless/android/enterprise/devicemanagement/proto/dm_api.pb.h" +#include "wireless/android/enterprise/devicemanagement/proto/omaha_settings.pb.h" using ::testing::_; using ::testing::AllArgs; @@ -47,6 +57,133 @@ namespace dm_client { namespace { +// Signing private key data (for testing only) in DER-encoded PKCS8 format. +const uint8_t kSigningPrivateKey[] = { + 0x30, 0x82, 0x01, 0x55, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x01, 0x3f, 0x30, 0x82, 0x01, 0x3b, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, + 0xd9, 0xcd, 0xca, 0xcd, 0xc3, 0xea, 0xbe, 0x72, 0x79, 0x1c, 0x29, 0x37, + 0x39, 0x99, 0x1f, 0xd4, 0xb3, 0x0e, 0xf0, 0x7b, 0x78, 0x77, 0x0e, 0x05, + 0x3b, 0x65, 0x34, 0x12, 0x62, 0xaf, 0xa6, 0x8d, 0x33, 0xce, 0x78, 0xf8, + 0x47, 0x05, 0x1d, 0x98, 0xaa, 0x1b, 0x1f, 0x50, 0x05, 0x5b, 0x3c, 0x19, + 0x3f, 0x80, 0x83, 0x63, 0x63, 0x3a, 0xec, 0xcb, 0x2e, 0x90, 0x4f, 0xf5, + 0x26, 0x76, 0xf1, 0xd5, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x64, + 0x29, 0xc2, 0xd9, 0x6b, 0xfe, 0xf9, 0x84, 0x75, 0x73, 0xe0, 0xf4, 0x77, + 0xb5, 0x96, 0xb0, 0xdf, 0x83, 0xc0, 0x4e, 0x57, 0xf1, 0x10, 0x6e, 0x91, + 0x89, 0x12, 0x30, 0x5e, 0x57, 0xff, 0x14, 0x59, 0x5f, 0x18, 0x86, 0x4e, + 0x4b, 0x17, 0x56, 0xfc, 0x8d, 0x40, 0xdd, 0x74, 0x65, 0xd3, 0xff, 0x67, + 0x64, 0xcb, 0x9c, 0xb4, 0x14, 0x8a, 0x06, 0xb7, 0x13, 0x45, 0x94, 0x16, + 0x7d, 0x3f, 0xe1, 0x02, 0x21, 0x00, 0xf6, 0x0f, 0x31, 0x6d, 0x06, 0xcc, + 0x3b, 0xa0, 0x44, 0x1f, 0xf5, 0xc2, 0x45, 0x2b, 0x10, 0x6c, 0xf9, 0x6f, + 0x8f, 0x87, 0x3d, 0xc0, 0x3b, 0x55, 0x13, 0x37, 0x80, 0xcd, 0x9f, 0xe1, + 0xb7, 0xd9, 0x02, 0x21, 0x00, 0xe2, 0x9a, 0x5f, 0xbf, 0x95, 0x74, 0xb5, + 0x7a, 0x6a, 0xa6, 0x97, 0xbd, 0x75, 0x8c, 0x97, 0x18, 0x24, 0xd6, 0x09, + 0xcd, 0xdc, 0xb5, 0x94, 0xbf, 0xe2, 0x78, 0xaa, 0x20, 0x47, 0x9f, 0x68, + 0x5d, 0x02, 0x21, 0x00, 0xaf, 0x8f, 0x97, 0x8c, 0x5a, 0xd5, 0x4d, 0x95, + 0xc4, 0x05, 0xa9, 0xab, 0xba, 0xfe, 0x46, 0xf1, 0xf9, 0xe7, 0x07, 0x59, + 0x4f, 0x4d, 0xe1, 0x07, 0x8a, 0x76, 0x87, 0x88, 0x2f, 0x13, 0x35, 0xc1, + 0x02, 0x20, 0x24, 0xc3, 0xd9, 0x2f, 0x13, 0x47, 0x99, 0x3e, 0x20, 0x59, + 0xa1, 0x1a, 0xeb, 0x1c, 0x81, 0x53, 0x38, 0x7e, 0xc5, 0x9e, 0x71, 0xe5, + 0xc0, 0x19, 0x95, 0xdb, 0xef, 0xf6, 0x46, 0xc8, 0x95, 0x3d, 0x02, 0x21, + 0x00, 0xaa, 0xb1, 0xff, 0x8a, 0xa2, 0xb2, 0x2b, 0xef, 0x9a, 0x83, 0x3f, + 0xc5, 0xbc, 0xd4, 0x6a, 0x07, 0xe8, 0xc7, 0x0b, 0x2e, 0xd4, 0x0f, 0xf8, + 0x98, 0x68, 0xe1, 0x04, 0xa8, 0x92, 0xd0, 0x10, 0xaa, +}; + +// SHA256 signature of the public key corresponding to private key +// kSigningPrivateKey for "example.com" domain signed with +// kPolicyVerificationKey. +const uint8_t kSigningPublicKeySignature[] = { + 0x97, 0xEB, 0x13, 0xE6, 0x6C, 0xE2, 0x7A, 0x2F, 0xC6, 0x6E, 0x68, 0x8F, + 0xED, 0x5B, 0x51, 0x08, 0x27, 0xF0, 0xA5, 0x97, 0x20, 0xEE, 0xE2, 0x9B, + 0x5B, 0x63, 0xA5, 0x9C, 0xAE, 0x41, 0xFD, 0x34, 0xC4, 0x2E, 0xEB, 0x63, + 0x10, 0x80, 0x0C, 0x74, 0x77, 0x6E, 0x34, 0x1C, 0x1B, 0x3B, 0x8E, 0x2A, + 0x3A, 0x7F, 0xF9, 0x73, 0xB6, 0x2B, 0xB6, 0x45, 0xDB, 0x05, 0xE8, 0x5A, + 0x68, 0x36, 0x05, 0x3C, 0x62, 0x3A, 0x6C, 0x64, 0xDB, 0x0E, 0x61, 0xBD, + 0x29, 0x1C, 0x61, 0x4B, 0xE0, 0xDA, 0x07, 0xBA, 0x29, 0x81, 0xF0, 0x90, + 0x58, 0xB8, 0xBB, 0xF4, 0x69, 0xFF, 0x8F, 0x2B, 0x4A, 0x2D, 0x98, 0x51, + 0x37, 0xF5, 0x52, 0xCB, 0xE3, 0xC4, 0x6D, 0xEC, 0xEA, 0x32, 0x2D, 0xDD, + 0xD7, 0xFC, 0x43, 0xC6, 0x54, 0xE1, 0xC1, 0x66, 0x43, 0x37, 0x09, 0xE1, + 0xBF, 0xD1, 0x11, 0xFC, 0xDB, 0xBF, 0xDF, 0x66, 0x53, 0x8F, 0x38, 0x2D, + 0xAA, 0x89, 0xD2, 0x9F, 0x60, 0x90, 0xB7, 0x05, 0xC2, 0x20, 0x82, 0xE6, + 0xE0, 0x57, 0x55, 0xFF, 0x5F, 0xC1, 0x76, 0x66, 0x46, 0xF8, 0x67, 0xB8, + 0x8B, 0x81, 0x53, 0xA9, 0x8B, 0x48, 0x9E, 0x2A, 0xF9, 0x60, 0x57, 0xBA, + 0xD7, 0x52, 0x97, 0x53, 0xF0, 0x2F, 0x78, 0x68, 0x50, 0x18, 0x12, 0x00, + 0x5E, 0x8E, 0x2A, 0x62, 0x0D, 0x48, 0xA9, 0xB5, 0x6B, 0xBC, 0xA0, 0x52, + 0x53, 0xD7, 0x65, 0x23, 0xA4, 0xA5, 0xF5, 0x32, 0x49, 0x2D, 0xB2, 0x77, + 0x2C, 0x66, 0x97, 0xBA, 0x58, 0xE0, 0x16, 0x1C, 0x8C, 0x02, 0x5D, 0xE0, + 0x73, 0x2E, 0xDF, 0xB4, 0x2F, 0x4C, 0xA2, 0x11, 0x26, 0xC1, 0xAF, 0xAC, + 0x73, 0xBC, 0xB6, 0x98, 0xE0, 0x20, 0x61, 0x0E, 0x52, 0x4A, 0x6C, 0x80, + 0xB5, 0x0C, 0x10, 0x80, 0x09, 0x17, 0xF4, 0x9D, 0xFE, 0xB5, 0xFC, 0x63, + 0x9A, 0x80, 0x3F, 0x76, +}; + +// New signing private key data (for testing only) in DER-encoded PKCS8 format. +const uint8_t kNewSigningPrivateKey[] = { + 0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, + 0x99, 0x98, 0x6b, 0x79, 0x5d, 0x38, 0x33, 0x79, 0x27, 0x0a, 0x2e, 0xb0, + 0x89, 0xba, 0xf8, 0xf6, 0x80, 0xde, 0xb0, 0x79, 0xf2, 0xd4, 0x6d, 0xf7, + 0x3c, 0xa3, 0x97, 0xf6, 0x4a, 0x3c, 0xa5, 0xcc, 0x40, 0x8a, 0xef, 0x59, + 0xaa, 0xc2, 0x82, 0x8f, 0xbc, 0x0d, 0x5b, 0x63, 0xc6, 0xaa, 0x72, 0xe2, + 0xf3, 0x57, 0xdd, 0x74, 0x00, 0xb0, 0x42, 0xd6, 0x27, 0xe7, 0x17, 0x61, + 0x0a, 0xdc, 0xc1, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x34, + 0xcf, 0xc9, 0xb4, 0x73, 0x2f, 0x0d, 0xd3, 0xcc, 0x6e, 0x9d, 0xdb, 0x29, + 0xa0, 0x56, 0x56, 0x3b, 0xbd, 0x56, 0x24, 0xb8, 0x2f, 0xfe, 0x97, 0x92, + 0x0c, 0x16, 0x06, 0x23, 0x44, 0x73, 0x25, 0x1d, 0x65, 0xf4, 0xda, 0x77, + 0xe7, 0x91, 0x2e, 0x91, 0x05, 0x10, 0xc1, 0x1b, 0x39, 0x5e, 0xb2, 0xf7, + 0xbd, 0x14, 0x19, 0xcb, 0x6b, 0xc3, 0xa9, 0xe8, 0x91, 0xf7, 0xa7, 0xa9, + 0x90, 0x08, 0x51, 0x02, 0x21, 0x00, 0xcc, 0x9e, 0x03, 0x54, 0x8f, 0x24, + 0xde, 0x90, 0x25, 0xec, 0x21, 0xaf, 0xe6, 0x27, 0x2a, 0x16, 0x42, 0x74, + 0xda, 0xf8, 0x84, 0xc4, 0x8c, 0x1e, 0x86, 0x12, 0x04, 0x5c, 0x17, 0x01, + 0xea, 0x9d, 0x02, 0x21, 0x00, 0xc0, 0x2a, 0x6c, 0xe9, 0xa1, 0x1a, 0x41, + 0x11, 0x94, 0x50, 0xf7, 0x1a, 0xd3, 0xbc, 0xf3, 0xa2, 0xf8, 0x46, 0xbc, + 0x26, 0x77, 0x78, 0xef, 0xc0, 0x54, 0xec, 0x22, 0x3f, 0x2c, 0x57, 0xe0, + 0xa3, 0x02, 0x20, 0x31, 0xf2, 0xc8, 0xa1, 0x55, 0xa8, 0x0c, 0x64, 0x67, + 0xbd, 0x72, 0xa3, 0xbb, 0xad, 0x07, 0xcb, 0x13, 0x41, 0xef, 0x4a, 0x07, + 0x2e, 0xeb, 0x7d, 0x70, 0x00, 0xe9, 0xeb, 0x88, 0xfa, 0x40, 0xc9, 0x02, + 0x20, 0x3a, 0xe0, 0xc4, 0xde, 0x10, 0x6e, 0x6a, 0xe1, 0x68, 0x00, 0x26, + 0xb6, 0x21, 0x8a, 0x13, 0x5c, 0x2b, 0x96, 0x00, 0xb0, 0x08, 0x8b, 0x15, + 0x6a, 0x68, 0x9a, 0xb1, 0x23, 0x8a, 0x02, 0xa2, 0xe1, 0x02, 0x21, 0x00, + 0xa3, 0xf2, 0x2d, 0x55, 0xc1, 0x6d, 0x40, 0xfa, 0x1d, 0xf7, 0xba, 0x86, + 0xef, 0x50, 0x98, 0xfc, 0xee, 0x09, 0xcc, 0xe7, 0x22, 0xb9, 0x4e, 0x80, + 0x32, 0x1a, 0x6b, 0xb3, 0x5f, 0x35, 0xbd, 0xf3, +}; + +// SHA256 signature of the public key corresponding to private key +// kNewSigningPrivateKey for "example.com" domain signed with +// kPolicyVerificationKey. +const uint8_t kNewSigningPublicKeySignature[] = { + 0x70, 0xED, 0x27, 0x42, 0x34, 0x69, 0xB6, 0x47, 0x9E, 0x7C, 0xA0, 0xF0, + 0xE5, 0x0A, 0x49, 0x49, 0x00, 0xDA, 0xBC, 0x70, 0x01, 0xC5, 0x4B, 0xDB, + 0x47, 0xD5, 0xAF, 0xA1, 0xAD, 0xB7, 0xE4, 0xE1, 0xBD, 0x5A, 0x1C, 0x35, + 0x44, 0x5A, 0xAA, 0xDB, 0x27, 0xBA, 0xA4, 0xA9, 0xC8, 0xDD, 0xEC, 0xD6, + 0xEB, 0xFE, 0xDB, 0xE0, 0x03, 0x5C, 0xA6, 0x2E, 0x5A, 0xEC, 0x75, 0x79, + 0xB8, 0x5F, 0x0A, 0xEE, 0x05, 0xB2, 0x61, 0xDC, 0x58, 0xF0, 0xD1, 0xCB, + 0x7B, 0x2A, 0xDB, 0xC1, 0x7C, 0x60, 0xE6, 0x3E, 0x87, 0x02, 0x61, 0xE6, + 0x90, 0xFD, 0x54, 0x65, 0xC7, 0xFF, 0x74, 0x09, 0xD6, 0xAA, 0x8E, 0xDC, + 0x5B, 0xC8, 0x38, 0x0C, 0x84, 0x0E, 0x84, 0x2E, 0x37, 0x2A, 0x4B, 0xDE, + 0x31, 0x82, 0x76, 0x1E, 0x77, 0xA5, 0xC1, 0xD5, 0xED, 0xFF, 0xBC, 0xEA, + 0x91, 0xB7, 0xBC, 0xFF, 0x76, 0x23, 0xE2, 0x78, 0x63, 0x01, 0x47, 0x80, + 0x47, 0x1F, 0x3A, 0x49, 0xBF, 0x0D, 0xCF, 0x27, 0x70, 0x92, 0xBB, 0xEA, + 0xB3, 0x92, 0x70, 0xFF, 0x1E, 0x4B, 0x1B, 0xE0, 0x4E, 0x0C, 0x4C, 0x6B, + 0x5D, 0x77, 0x06, 0xBB, 0xFB, 0x9B, 0x0E, 0x55, 0xB8, 0x8A, 0xF2, 0x45, + 0xA9, 0xF3, 0x54, 0x3D, 0x0C, 0xAC, 0xA8, 0x15, 0xD2, 0x31, 0x8D, 0x97, + 0x08, 0x73, 0xC9, 0x0F, 0x1D, 0xDE, 0x10, 0x22, 0xC6, 0x55, 0x53, 0x7F, + 0x7C, 0x50, 0x16, 0x5A, 0x08, 0xCC, 0x1C, 0x53, 0x9B, 0x02, 0xB8, 0x80, + 0xB7, 0x46, 0xF5, 0xF1, 0xC7, 0x3D, 0x36, 0xBD, 0x26, 0x02, 0xDE, 0x10, + 0xAB, 0x5A, 0x03, 0xCD, 0x67, 0x00, 0x1C, 0x23, 0xC7, 0x13, 0xEE, 0x5D, + 0xAF, 0xC5, 0x1F, 0xE3, 0xA0, 0x54, 0xAC, 0xC2, 0xC9, 0x44, 0xD4, 0x4A, + 0x09, 0x8E, 0xEB, 0xAE, 0xCA, 0x08, 0x8A, 0x7F, 0x41, 0x7B, 0xD8, 0x2C, + 0xDD, 0x6F, 0x80, 0xC3, +}; + +const char kDomain[] = "example.com"; +const char kUsername[] = "username@example.com"; +const char kDmToken[] = "dm_token"; +const TCHAR kDeviceId[] = _T("device_id"); + // A Google Mock matcher that returns true if a string contains a valid // URL to the device management server with the required query parameters. class IsValidRequestUrlMatcher @@ -175,6 +312,84 @@ class IsRegisterBrowserRequestMatcher } }; +// A Google Mock matcher that returns true if a buffer contains a valid +// serialized PolicyValidationReportRequest message. Field values are checked +// against the expected values when possible. +class IsPolicyValidationReportRequestMatcher + : public ::testing::MatcherInterface< + const ::testing::tuple&> { + public: + explicit IsPolicyValidationReportRequestMatcher( + const PolicyValidationResult& expected_validation_result) + : expected_validation_result_(expected_validation_result) {} + virtual bool MatchAndExplain( + const ::testing::tuple& buffer, + ::testing::MatchResultListener* listener) const { + enterprise_management::DeviceManagementRequest request; + if (!request.ParseFromArray(::testing::get<0>(buffer), + static_cast(::testing::get<1>(buffer)))) { + *listener << "parse failure"; + return false; + } + if (!request.has_policy_validation_report_request()) { + *listener << "missing policy_validation_report_request"; + return false; + } + const enterprise_management::PolicyValidationReportRequest& + error_report_request = request.policy_validation_report_request(); + if (!error_report_request.has_validation_result_type()) { + *listener << "unexpected error_report_request.validation_result_type"; + return false; + } + if (!error_report_request.has_policy_type() || + error_report_request.policy_type() != + expected_validation_result_.policy_type) { + *listener << "unexpected error_report_request.policy_type"; + return false; + } + if (!error_report_request.has_policy_token() || + error_report_request.policy_token() != + expected_validation_result_.policy_token) { + *listener << "missing error_report_request.policy_token"; + return false; + } + if (expected_validation_result_.issues.size() != + error_report_request.policy_value_validation_issues_size()) { + *listener << "unexpected number of issues in error_report_request"; + return false; + } + + for (size_t i = 0; i < expected_validation_result_.issues.size(); ++i) { + auto expected_issue = expected_validation_result_.issues[i]; + auto issue_in_request = + error_report_request.policy_value_validation_issues(i); + if (!issue_in_request.has_policy_name() || + issue_in_request.policy_name() != expected_issue.policy_name) { + *listener << "unexpected issue policy name"; + return false; + } + if (!issue_in_request.has_severity()) { + *listener << "unexpected issue severity"; + return false; + } + if (!issue_in_request.has_debug_message() || + issue_in_request.debug_message() != expected_issue.message) { + *listener << "unexpected issue message"; + return false; + } + } + + return true; + } + + virtual void DescribeTo(std::ostream* os) const { + *os << "buffer contains a valid serialized PolicyValidationReportRequest"; + } + + private: + const PolicyValidationResult expected_validation_result_; +}; + // A Google Mock matcher that returns true if a buffer contains a valid // serialized DevicePolicyRequest message. While the presence of each field // in the request is checked, the exact value of each is not. @@ -230,6 +445,15 @@ IsRegisterBrowserRequest() { return ::testing::MakeMatcher(new IsRegisterBrowserRequestMatcher); } +// Returns an IsPolicyValidationReportRequest matcher, which takes a tuple of a +// pointer to a buffer and a buffer size. +::testing::Matcher&> +IsPolicyValidationReportRequest( + const PolicyValidationResult& expected_validation_result) { + return ::testing::MakeMatcher( + new IsPolicyValidationReportRequestMatcher(expected_validation_result)); +} + // Returns an IsFetchPoliciesRequest matcher, which takes a tuple of a pointer // to a buffer and a buffer size. ::testing::Matcher& > @@ -293,7 +517,200 @@ class DmClientRequestTest : public ::testing::Test { ON_CALL(**request, GetResponse()).WillByDefault(Return(response)); } + // Populates |request| with a mock HttpRequest that behaves as if the server + // returned a 410 HTTP response. + // Note: always wrap calls to this with ASSERT_NO_FATAL_FAILURE. + void MakeGoneHttpRequest(bool delete_dm_token, MockHttpRequest** request) { + *request = new ::testing::NiceMock(); + + // The server responds with 410. + ON_CALL(**request, GetHttpStatusCode()) + .WillByDefault(Return(HTTP_STATUS_GONE)); + + // And response data guiding how to update DM token. + std::vector response; + ASSERT_NO_FATAL_FAILURE( + MakeHTTPGoneResponseBody(delete_dm_token, &response)); + ON_CALL(**request, GetResponse()).WillByDefault(Return(response)); + } + + struct KeyInfo { + const std::unique_ptr& signing_private_key; + const std::string& signing_public_key; + const std::string& signing_public_key_signature; + const std::unique_ptr& cached_signing_private_key; + }; + + struct FetchPoliciesInput { + const std::unique_ptr& key_info; + const PolicyResponses& responses; + }; + + void RunFetchPolicies(const std::unique_ptr& key_info, + CachedPolicyInfo* info) { + ASSERT_TRUE(info); + + PolicyResponsesMap expected_responses = { + {"google/chrome/machine-level-user", "test-data-chrome"}, + {"google/drive/machine-level-user", "test-data-drive"}, + {"google/earth/machine-level-user", "test-data-earth"}, + }; + + enterprise_management::PublicKeyVerificationData signed_data; + signed_data.set_new_public_key(key_info->signing_public_key); + signed_data.set_domain(kDomain); + + PolicyResponses input = {expected_responses, + signed_data.SerializeAsString()}; + FetchPoliciesInput fetch_input = {key_info, input}; + MockHttpRequest* mock_http_request = nullptr; + ASSERT_NO_FATAL_FAILURE(MakeSuccessHttpRequest(fetch_input, + &mock_http_request)); + + const std::vector> query_params = { + {_T("request"), _T("policy")}, + {_T("agent"), internal::GetAgent()}, + {_T("apptype"), _T("Chrome")}, + {_T("deviceid"), kDeviceId}, + {_T("platform"), internal::GetPlatform()}, + }; + + // Expect the proper URL with query params. + EXPECT_CALL(*mock_http_request, + set_url(IsValidRequestUrl(std::move(query_params)))); + + // Expect that the request headers contain the DMToken. + EXPECT_CALL(*mock_http_request, + set_additional_headers( + CStringHasSubstr(_T("Content-Type: application/protobuf") + _T("\r\nAuthorization: GoogleDMToken ") + _T("token=dm_token")))); + + // Expect that the body of the request contains a well-formed fetch policies + // request. + EXPECT_CALL(*mock_http_request, set_request_buffer(_, _)) + .With(AllArgs(IsFetchPoliciesRequest())); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + DmStorage::Instance()->StoreDmToken(kDmToken); + + // Fetch Policies should succeed, providing the expected PolicyResponses. + PolicyResponses responses; + ASSERT_HRESULT_SUCCEEDED(internal::FetchPolicies( + DmStorage::Instance(), + std::move(std::unique_ptr(mock_http_request)), + CString(kDmToken), kDeviceId, *info, &responses)); + ASSERT_TRUE(!responses.policy_info.empty()); + ASSERT_HRESULT_SUCCEEDED(GetCachedPolicyInfo(responses.policy_info, info)); + + EXPECT_EQ(expected_responses.size(), responses.responses.size()); + for (const auto& expected_response : expected_responses) { + enterprise_management::PolicyFetchResponse response; + const std::string& string_response = + responses.responses[expected_response.first.c_str()]; + EXPECT_TRUE(response.ParseFromString(string_response)); + + enterprise_management::PolicyData policy_data; + EXPECT_TRUE(policy_data.ParseFromString(response.policy_data())); + EXPECT_TRUE(policy_data.IsInitialized()); + EXPECT_TRUE(policy_data.has_policy_type()); + + EXPECT_STREQ(expected_response.first.c_str(), + policy_data.policy_type().c_str()); + EXPECT_STREQ(expected_response.second.c_str(), + policy_data.policy_value().c_str()); + } + } + + void DecodeOmahaPolicies() { + wireless_android_enterprise_devicemanagement::ApplicationSettings app; + app.set_app_guid(CStringA(kChromeAppId)); + wireless_android_enterprise_devicemanagement::OmahaSettingsClientProto + omaha_settings; + auto repeated_app_settings = omaha_settings.mutable_application_settings(); + repeated_app_settings->Add(std::move(app)); + + enterprise_management::PolicyData policy_data; + policy_data.set_policy_value(omaha_settings.SerializeAsString()); + + enterprise_management::PolicyFetchResponse response; + response.set_policy_data(policy_data.SerializeAsString()); + + CachedOmahaPolicy op; + ASSERT_HRESULT_SUCCEEDED(GetCachedOmahaPolicy(response.SerializeAsString(), + &op)); + ASSERT_EQ(op.application_settings.size(), 1); + ASSERT_TRUE(::IsEqualGUID(op.application_settings.begin()->first, + StringToGuid(kChromeAppId))); + } + + std::unique_ptr CreateKey( + const uint8_t* private_key, + size_t private_key_length, + std::string* rsa_public_key) { + std::vector input(private_key, private_key + private_key_length); + std::unique_ptr rsa_private_key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input)); + + if (rsa_public_key) { + std::vector public_key; + VERIFY1(rsa_private_key->ExportPublicKey(&public_key)); + + *rsa_public_key = std::string( + reinterpret_cast(public_key.data()), + public_key.size()); + } + + return std::move(rsa_private_key); + } + private: + // Produces |key|'s signature over |data| and stores it in |signature|. + void SignData(const std::string& data, + crypto::RSAPrivateKey* key, + std::string* signature) { + std::unique_ptr signature_creator( + crypto::SignatureCreator::Create(key, CALG_SHA1)); + ASSERT_TRUE(signature_creator->Update( + reinterpret_cast(data.c_str()), data.size())); + std::vector signature_bytes; + ASSERT_TRUE(signature_creator->Final(&signature_bytes)); + signature->assign(reinterpret_cast(signature_bytes.data()), + signature_bytes.size()); + } + + void SignPolicyResponse(enterprise_management::PolicyFetchResponse* response, + const std::unique_ptr& key_info) { + ASSERT_TRUE(response); + ASSERT_TRUE(key_info->signing_private_key.get()); + + // Add the new public key and the corresponding verification key signature + // to the policy response. + response->set_new_public_key(key_info->signing_public_key); + response->set_new_public_key_verification_data_signature( + key_info->signing_public_key_signature); + + enterprise_management::PublicKeyVerificationData signed_data; + signed_data.set_new_public_key(key_info->signing_public_key); + signed_data.set_domain(kDomain); + response->set_new_public_key_verification_data( + signed_data.SerializeAsString()); + + // Add the PolicyData signature to the policy response. + SignData(response->policy_data(), + key_info->signing_private_key.get(), + response->mutable_policy_data_signature()); + + if (key_info->cached_signing_private_key.get()) { + // Use the cached private key to sign the new public key and add the + // signature to the policy response. + SignData(response->new_public_key(), + key_info->cached_signing_private_key.get(), + response->mutable_new_public_key_signature()); + } + } + // Populates |body| with a valid serialized DeviceRegisterResponse. // Note: always wrap calls to this with ASSERT_NO_FATAL_FAILURE. void MakeSuccessResponseBody(const char* dm_token, std::vector* body) { @@ -307,8 +724,11 @@ class DmClientRequestTest : public ::testing::Test { // Populates |body| with a valid serialized DevicePolicyResponse. // Note: always wrap calls to this with ASSERT_NO_FATAL_FAILURE. - void MakeSuccessResponseBody(const PolicyResponsesMap& responses, + void MakeSuccessResponseBody(const FetchPoliciesInput& fetch_input, std::vector* body) { + const PolicyResponses& input = fetch_input.responses; + const PolicyResponsesMap& responses = input.responses; + enterprise_management::DeviceManagementResponse dm_response; for (const auto& response : responses) { @@ -317,7 +737,12 @@ class DmClientRequestTest : public ::testing::Test { enterprise_management::PolicyData policy_data; policy_data.set_policy_type(response.first); policy_data.set_policy_value(response.second); + policy_data.set_username(kUsername); + policy_data.set_request_token(kDmToken); + policy_data.set_device_id(CStringA(kDeviceId)); + policy_data.set_timestamp(time(NULL)); policy_response->set_policy_data(policy_data.SerializeAsString()); + SignPolicyResponse(policy_response, fetch_input.key_info); } std::string response_string; @@ -325,24 +750,35 @@ class DmClientRequestTest : public ::testing::Test { body->assign(response_string.begin(), response_string.end()); } + // Populates |body| with a serialized DevicePolicyResponse for HTTP Gone. + // Note: always wrap calls to this with ASSERT_NO_FATAL_FAILURE. + void MakeHTTPGoneResponseBody(bool delete_dm_token, + std::vector* body) { + if (!delete_dm_token) return; + + enterprise_management::DeviceManagementResponse dm_response; + dm_response.add_error_detail( + enterprise_management::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN); + std::string response_string; + ASSERT_TRUE(dm_response.SerializeToString(&response_string)); + body->assign(response_string.begin(), response_string.end()); + } + DISALLOW_COPY_AND_ASSIGN(DmClientRequestTest); }; // Test that DmClient can send a reasonable RegisterBrowserRequest and handle a // corresponding DeviceRegisterResponse. TEST_F(DmClientRequestTest, RegisterWithRequest) { - static const char kDmToken[] = "dm_token"; - static const TCHAR kDeviceId[] = _T("device_id"); - MockHttpRequest* mock_http_request = nullptr; ASSERT_NO_FATAL_FAILURE(MakeSuccessHttpRequest(kDmToken, &mock_http_request)); - std::vector> query_params = { - {_T("request"), _T("register_policy_agent")}, - {_T("agent"), internal::GetAgent()}, - {_T("apptype"), _T("Chrome")}, - {_T("deviceid"), kDeviceId}, - {_T("platform"), internal::GetPlatform()}, + const std::vector> query_params = { + {_T("request"), _T("register_policy_agent")}, + {_T("agent"), internal::GetAgent()}, + {_T("apptype"), _T("Chrome")}, + {_T("deviceid"), kDeviceId}, + {_T("platform"), internal::GetPlatform()}, }; // Expect the proper URL with query params. @@ -352,8 +788,10 @@ TEST_F(DmClientRequestTest, RegisterWithRequest) { // Expect that the request headers contain the enrollment token. EXPECT_CALL(*mock_http_request, set_additional_headers( - CStringHasSubstr(_T("Authorization: GoogleEnrollmentToken ") - _T("token=enrollment_token")))); + CStringHasSubstr(_T("Content-Type: application/protobuf\r\n") + _T("Authorization: GoogleEnrollmentToken ") + _T("token=57FEBE8F-48D0-487B-A788-") + _T("CF1019DCD452")))); // Expect that the body of the request contains a well-formed register browser // request. @@ -361,75 +799,242 @@ TEST_F(DmClientRequestTest, RegisterWithRequest) { .With(AllArgs(IsRegisterBrowserRequest())); // Registration should succeed, providing the expected DMToken. - CStringA dm_token; - ASSERT_HRESULT_SUCCEEDED(internal::RegisterWithRequest(mock_http_request, - _T("enrollment_token"), - kDeviceId, - &dm_token)); - EXPECT_STREQ(dm_token.GetString(), kDmToken); + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + + // Test successful registration. + ASSERT_HRESULT_SUCCEEDED(internal::RegisterWithRequest( + DmStorage::Instance(), + std::move(std::unique_ptr(mock_http_request)), + _T("57FEBE8F-48D0-487B-A788-CF1019DCD452"), kDeviceId)); + EXPECT_EQ(DmStorage::Instance()->GetDmToken(), kDmToken); + + // Test DM Token deletion. + MockHttpRequest* mock_gone_request = nullptr; + ASSERT_NO_FATAL_FAILURE(MakeGoneHttpRequest(true, &mock_gone_request)); + ASSERT_EQ( + internal::RegisterWithRequest( + DmStorage::Instance(), + std::move(std::unique_ptr(mock_gone_request)), + _T("57FEBE8F-48D0-487B-A788-CF1019DCD452"), kDeviceId), + HRESULTFromHttpStatusCode(HTTP_STATUS_GONE)); + EXPECT_TRUE(DmStorage::Instance()->GetDmToken().IsEmpty()); + + // Test DM Token invalidation. + mock_gone_request = nullptr; + ASSERT_NO_FATAL_FAILURE(MakeGoneHttpRequest(false, &mock_gone_request)); + ASSERT_EQ( + internal::RegisterWithRequest( + DmStorage::Instance(), + std::move(std::unique_ptr(mock_gone_request)), + _T("57FEBE8F-48D0-487B-A788-CF1019DCD452"), kDeviceId), + HRESULTFromHttpStatusCode(HTTP_STATUS_GONE)); + EXPECT_TRUE(DmStorage::Instance()->IsInvalidDMToken()); } -// Test that DmClient can send a reasonable DevicePolicyRequest and handle a -// corresponding DevicePolicyResponse. -TEST_F(DmClientRequestTest, FetchPolicies) { - static const TCHAR kDeviceId[] = _T("device_id"); - - PolicyResponsesMap expected_responses = { - {"google/chrome/machine-level-user", "test-data-chrome"}, - {"google/drive/machine-level-user", "test-data-drive"}, - {"google/earth/machine-level-user", "test-data-earth"}, - }; +TEST_F(DmClientRequestTest, SendPolicyValidationResultReportIfNeeded) { + PolicyValidationResult validation_result; + validation_result.policy_type = "google/chrome/machine-level-user"; + validation_result.policy_token = "some_token"; + validation_result.status = + PolicyValidationResult::Status::kValidationBadSignature; + validation_result.issues.push_back( + {"test_policy1", PolicyValueValidationIssue::Severity::kError, + "test_policy1 value has error"}); + validation_result.issues.push_back( + {"test_policy2", PolicyValueValidationIssue::Severity::kWarning, + "test_policy2 value has warning"}); MockHttpRequest* mock_http_request = nullptr; - ASSERT_NO_FATAL_FAILURE(MakeSuccessHttpRequest(expected_responses, - &mock_http_request)); - - std::vector> query_params = { - {_T("request"), _T("policy")}, - {_T("agent"), internal::GetAgent()}, - {_T("apptype"), _T("Chrome")}, - {_T("deviceid"), kDeviceId}, - {_T("platform"), internal::GetPlatform()}, + ASSERT_NO_FATAL_FAILURE(MakeSuccessHttpRequest("", &mock_http_request)); + + const std::vector> query_params = { + {_T("request"), _T("policy_validation_report")}, + {_T("agent"), internal::GetAgent()}, + {_T("apptype"), _T("Chrome")}, + {_T("deviceid"), kDeviceId}, + {_T("platform"), internal::GetPlatform()}, }; - - // Expect the proper URL with query params. EXPECT_CALL(*mock_http_request, set_url(IsValidRequestUrl(std::move(query_params)))); // Expect that the request headers contain the DMToken. - EXPECT_CALL(*mock_http_request, - set_additional_headers( - CStringHasSubstr(_T("Authorization: GoogleDMToken ") - _T("token=dm_token")))); + EXPECT_CALL(*mock_http_request, set_additional_headers(CStringHasSubstr( + _T("Content-Type: application/protobuf") + _T("\r\nAuthorization: GoogleDMToken ") + _T("token=dm_token")))); - // Expect that the body of the request contains a well-formed fetch policies - // request. + // Expect that the body of the request contains a well-formed policy + // validation result report request. EXPECT_CALL(*mock_http_request, set_request_buffer(_, _)) - .With(AllArgs(IsFetchPoliciesRequest())); + .With(AllArgs(IsPolicyValidationReportRequest(validation_result))); - // Fetch Policies should succeed, providing the expected PolicyResponsesMap. - PolicyResponsesMap responses; - ASSERT_HRESULT_SUCCEEDED(internal::FetchPolicies(mock_http_request, - _T("dm_token"), - kDeviceId, - &responses)); + internal::SendPolicyValidationResultReportIfNeeded( + std::move(std::unique_ptr(mock_http_request)), + CString(kDmToken), CString(kDeviceId), validation_result); +} - EXPECT_EQ(expected_responses.size(), responses.size()); - for (const auto& expected_response : expected_responses) { - enterprise_management::PolicyFetchResponse response; - EXPECT_TRUE(response.ParseFromString( - responses[expected_response.first.c_str()])); +// Test that DmClient can send a reasonable DevicePolicyRequest and handle a +// corresponding DevicePolicyResponse. +TEST_F(DmClientRequestTest, FetchPolicies) { + std::string signing_public_key; + const std::unique_ptr signing_private_key( + std::move(CreateKey(kSigningPrivateKey, + sizeof(kSigningPrivateKey), + &signing_public_key))); + const std::string signing_public_key_signature( + reinterpret_cast(kSigningPublicKeySignature), + sizeof(kSigningPublicKeySignature)); + const std::unique_ptr signing_key_info( + std::make_unique(KeyInfo{signing_private_key, + signing_public_key, + signing_public_key_signature, + nullptr})); + + std::string new_signing_public_key; + const std::unique_ptr new_signing_private_key( + std::move(CreateKey(kNewSigningPrivateKey, + sizeof(kNewSigningPrivateKey), + &new_signing_public_key))); + const std::string new_signing_public_key_signature( + reinterpret_cast(kNewSigningPublicKeySignature), + sizeof(kNewSigningPublicKeySignature)); + const std::unique_ptr new_signing_key_info( + std::make_unique(KeyInfo{new_signing_private_key, + new_signing_public_key, + new_signing_public_key_signature, + signing_private_key})); + + CachedPolicyInfo info; + + // The mock server will return PolicyData signed with kSigningPrivateKey, and + // a new public key corresponding to kSigningPrivateKey, signed with the + // hard-coded verification key. + RunFetchPolicies(signing_key_info, &info); + + // |info| was initialized in the previous run with the signing public key. Now + // the mock server will return PolicyData signed with kNewSigningPrivateKey, + // and a new public key corresponding to kNewSigningPrivateKey, signed with + // both the hard-coded verification key as well as with the previous private + // key kSigningPrivateKey. + RunFetchPolicies(new_signing_key_info, &info); + + // Test DM Token invalidation. + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + DmStorage::Instance()->StoreDmToken(kDmToken); + MockHttpRequest* mock_gone_request = nullptr; + ASSERT_NO_FATAL_FAILURE(MakeGoneHttpRequest(false, &mock_gone_request)); + + // Fetch Policies should fail. + PolicyResponses responses; + ASSERT_EQ( + internal::FetchPolicies( + DmStorage::Instance(), + std::move(std::unique_ptr(mock_gone_request)), + CString(kDmToken), kDeviceId, info, &responses), + HRESULTFromHttpStatusCode(HTTP_STATUS_GONE)); + EXPECT_TRUE(DmStorage::Instance()->IsInvalidDMToken()); +} - enterprise_management::PolicyData policy_data; - EXPECT_TRUE(policy_data.ParseFromString(response.policy_data())); - EXPECT_TRUE(policy_data.IsInitialized()); - EXPECT_TRUE(policy_data.has_policy_type()); - - EXPECT_STREQ(expected_response.first.c_str(), - policy_data.policy_type().c_str()); - EXPECT_STREQ(expected_response.second.c_str(), - policy_data.policy_value().c_str()); - } +// Test that DmClient can delete DM token per server request. +TEST_F(DmClientRequestTest, DmTokenDeletionInPolicyFetch) { + std::string signing_public_key; + const std::unique_ptr signing_private_key( + std::move(CreateKey(kSigningPrivateKey, sizeof(kSigningPrivateKey), + &signing_public_key))); + const std::string signing_public_key_signature( + reinterpret_cast(kSigningPublicKeySignature), + sizeof(kSigningPublicKeySignature)); + const std::unique_ptr signing_key_info(std::make_unique( + KeyInfo{signing_private_key, signing_public_key, + signing_public_key_signature, nullptr})); + + std::string new_signing_public_key; + const std::unique_ptr new_signing_private_key( + std::move(CreateKey(kNewSigningPrivateKey, sizeof(kNewSigningPrivateKey), + &new_signing_public_key))); + const std::string new_signing_public_key_signature( + reinterpret_cast(kNewSigningPublicKeySignature), + sizeof(kNewSigningPublicKeySignature)); + const std::unique_ptr new_signing_key_info(std::make_unique( + KeyInfo{new_signing_private_key, new_signing_public_key, + new_signing_public_key_signature, signing_private_key})); + + CachedPolicyInfo info; + + // The mock server will return PolicyData signed with kSigningPrivateKey, and + // a new public key corresponding to kSigningPrivateKey, signed with the + // hard-coded verification key. + RunFetchPolicies(signing_key_info, &info); + + // |info| was initialized in the previous run with the signing public key. Now + // the mock server will return PolicyData signed with kNewSigningPrivateKey, + // and a new public key corresponding to kNewSigningPrivateKey, signed with + // both the hard-coded verification key as well as with the previous private + // key kSigningPrivateKey. + RunFetchPolicies(new_signing_key_info, &info); + + // Test DM Token deletion. + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + DmStorage::Instance()->StoreDmToken(kDmToken); + MockHttpRequest* mock_gone_request = nullptr; + ASSERT_NO_FATAL_FAILURE(MakeGoneHttpRequest(true, &mock_gone_request)); + // Fetch Policies should fail. + PolicyResponses responses; + ASSERT_EQ( + internal::FetchPolicies( + DmStorage::Instance(), + std::move(std::unique_ptr(mock_gone_request)), + CString(kDmToken), kDeviceId, info, &responses), + HRESULTFromHttpStatusCode(HTTP_STATUS_GONE)); + EXPECT_TRUE(DmStorage::Instance()->GetDmToken().IsEmpty()); +} + +// Test that we are able to successfully encode and then decode a +// protobuf OmahaSettingsClientProto into a CachedOmahaPolicy instance. +TEST_F(DmClientRequestTest, DecodePolicies) { + DecodeOmahaPolicies(); +} + +TEST_F(DmClientRequestTest, HandleDMResponseError) { + const CPath policy_responses_dir = CPath(ConcatenatePath( + app_util::GetCurrentModuleDirectory(), + _T("Policies"))); + + std::unique_ptr dm_storage = + DmStorage::CreateTestInstance(policy_responses_dir, CString()); + EXPECT_HRESULT_SUCCEEDED(dm_storage->StoreDmToken("dm_token")); + + PolicyResponsesMap responses = { + {"google/chrome/machine-level-user", "test-data-chr"}, + {"google/earth/machine-level-user", + "test-data-earth-foo-bar-baz-foo-bar-baz-foo-bar-baz"}, + }; + + const PolicyResponses expected_responses = {responses, "expected data"}; + ASSERT_HRESULT_SUCCEEDED(dm_storage->PersistPolicies(expected_responses)); + + std::vector response; + EXPECT_TRUE(policy_responses_dir.FileExists()); + EXPECT_TRUE(dm_storage->IsValidDMToken()); + internal::HandleDMResponseError( + dm_storage.get(), HRESULTFromHttpStatusCode(HTTP_STATUS_GONE), response); + EXPECT_TRUE(dm_storage->IsInvalidDMToken()); + EXPECT_FALSE(policy_responses_dir.FileExists()); + + ASSERT_HRESULT_SUCCEEDED(dm_storage->PersistPolicies(expected_responses)); + enterprise_management::DeviceManagementResponse dm_response; + dm_response.add_error_detail( + enterprise_management::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN); + std::string response_string; + ASSERT_TRUE(dm_response.SerializeToString(&response_string)); + response.assign(response_string.begin(), response_string.end()); + internal::HandleDMResponseError( + dm_storage.get(), HRESULTFromHttpStatusCode(HTTP_STATUS_GONE), response); + EXPECT_TRUE(dm_storage->GetDmToken().IsEmpty()); + EXPECT_HRESULT_SUCCEEDED(dm_storage->DeleteDmToken()); } class DmClientRegistryTest : public RegistryProtectedTest { @@ -445,7 +1050,8 @@ TEST_F(DmClientRegistryTest, GetRegistrationState) { // Enrollment token without device management token. { - EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(_T("enrollment_token"))); + EXPECT_HRESULT_SUCCEEDED( + DmStorage::CreateInstance(_T("57FEBE8F-48D0-487B-A788-CF1019DCD452"))); ON_SCOPE_EXIT(DmStorage::DeleteInstance); EXPECT_EQ(GetRegistrationState(DmStorage::Instance()), kRegistrationPending); @@ -454,7 +1060,8 @@ TEST_F(DmClientRegistryTest, GetRegistrationState) { // Enrollment token and device management token. ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("dm_token")); { - EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(_T("enrollment_token"))); + EXPECT_HRESULT_SUCCEEDED( + DmStorage::CreateInstance(_T("57FEBE8F-48D0-487B-A788-CF1019DCD452"))); ON_SCOPE_EXIT(DmStorage::DeleteInstance); EXPECT_EQ(GetRegistrationState(DmStorage::Instance()), kRegistered); } @@ -465,6 +1072,48 @@ TEST_F(DmClientRegistryTest, GetRegistrationState) { ON_SCOPE_EXIT(DmStorage::DeleteInstance); EXPECT_EQ(GetRegistrationState(DmStorage::Instance()), kRegistered); } + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("")); +} + +TEST_F(DmClientRegistryTest, RegisterIfNeeded) { + // Invalid DM token exists. + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken(kInvalidTokenValue)); + { + EXPECT_HRESULT_SUCCEEDED( + DmStorage::CreateInstance(_T("57FEBE8F-48D0-487B-A788-CF1019DCD452"))); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + EXPECT_EQ(RegisterIfNeeded(DmStorage::Instance(), true), E_FAIL); + } + + // Valid DM token exists. + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("dm_token")); + { + EXPECT_HRESULT_SUCCEEDED( + DmStorage::CreateInstance(_T("57FEBE8F-48D0-487B-A788-CF1019DCD452"))); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + EXPECT_EQ(RegisterIfNeeded(DmStorage::Instance(), true), S_FALSE); + } + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("")); +} + +TEST_F(DmClientRegistryTest, RefreshPolicies) { + // Invalid DM token exists. + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken(kInvalidTokenValue)); + { + EXPECT_HRESULT_SUCCEEDED( + DmStorage::CreateInstance(_T("57FEBE8F-48D0-487B-A788-CF1019DCD452"))); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + EXPECT_EQ(RefreshPolicies(), E_FAIL); + } + + // No DM token. + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("")); + { + EXPECT_HRESULT_SUCCEEDED( + DmStorage::CreateInstance(_T("57FEBE8F-48D0-487B-A788-CF1019DCD452"))); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + EXPECT_EQ(RefreshPolicies(), S_FALSE); + } } TEST(DmClientTest, GetAgent) { diff --git a/omaha/goopdate/dm_messages.cc b/omaha/goopdate/dm_messages.cc index b322bc486..3526c292c 100644 --- a/omaha/goopdate/dm_messages.cc +++ b/omaha/goopdate/dm_messages.cc @@ -14,17 +14,85 @@ #include "omaha/goopdate/dm_messages.h" +#include + +#include +#include #include #include +#include +#include "crypto/signature_verifier_win.h" #include "omaha/base/debug.h" #include "omaha/base/logging.h" +#include "omaha/common/const_group_policy.h" #include "wireless/android/enterprise/devicemanagement/proto/dm_api.pb.h" +#include "wireless/android/enterprise/devicemanagement/proto/omaha_settings.pb.h" namespace omaha { namespace { +namespace edm = ::wireless_android_enterprise_devicemanagement; + +// Request signed policy blobs. kPolicyVerificationKeyHash and +// kPolicyVerificationKey need to be kept in sync with the corresponding values +// in Chromium's cloud_policy_constants.cc. +constexpr char kPolicyVerificationKeyHash[] = "1:356l7w"; +constexpr uint8_t kPolicyVerificationKey[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, + 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xA7, 0xB3, 0xF9, + 0x0D, 0xC7, 0xC7, 0x8D, 0x84, 0x3D, 0x4B, 0x80, 0xDD, 0x9A, 0x2F, 0xF8, + 0x69, 0xD4, 0xD1, 0x14, 0x5A, 0xCA, 0x04, 0x4B, 0x1C, 0xBC, 0x28, 0xEB, + 0x5E, 0x10, 0x01, 0x36, 0xFD, 0x81, 0xEB, 0xE4, 0x3C, 0x16, 0x40, 0xA5, + 0x8A, 0xE6, 0x08, 0xEE, 0xEF, 0x39, 0x1F, 0x6B, 0x10, 0x29, 0x50, 0x84, + 0xCE, 0xEE, 0x33, 0x5C, 0x48, 0x4A, 0x33, 0xB0, 0xC8, 0x8A, 0x66, 0x0D, + 0x10, 0x11, 0x9D, 0x6B, 0x55, 0x4C, 0x9A, 0x62, 0x40, 0x9A, 0xE2, 0xCA, + 0x21, 0x01, 0x1F, 0x10, 0x1E, 0x7B, 0xC6, 0x89, 0x94, 0xDA, 0x39, 0x69, + 0xBE, 0x27, 0x28, 0x50, 0x5E, 0xA2, 0x55, 0xB9, 0x12, 0x3C, 0x79, 0x6E, + 0xDF, 0x24, 0xBF, 0x34, 0x88, 0xF2, 0x5E, 0xD0, 0xC4, 0x06, 0xEE, 0x95, + 0x6D, 0xC2, 0x14, 0xBF, 0x51, 0x7E, 0x3F, 0x55, 0x10, 0x85, 0xCE, 0x33, + 0x8F, 0x02, 0x87, 0xFC, 0xD2, 0xDD, 0x42, 0xAF, 0x59, 0xBB, 0x69, 0x3D, + 0xBC, 0x77, 0x4B, 0x3F, 0xC7, 0x22, 0x0D, 0x5F, 0x72, 0xC7, 0x36, 0xB6, + 0x98, 0x3D, 0x03, 0xCD, 0x2F, 0x68, 0x61, 0xEE, 0xF4, 0x5A, 0xF5, 0x07, + 0xAE, 0xAE, 0x79, 0xD1, 0x1A, 0xB2, 0x38, 0xE0, 0xAB, 0x60, 0x5C, 0x0C, + 0x14, 0xFE, 0x44, 0x67, 0x2C, 0x8A, 0x08, 0x51, 0x9C, 0xCD, 0x3D, 0xDB, + 0x13, 0x04, 0x57, 0xC5, 0x85, 0xB6, 0x2A, 0x0F, 0x02, 0x46, 0x0D, 0x2D, + 0xCA, 0xE3, 0x3F, 0x84, 0x9E, 0x8B, 0x8A, 0x5F, 0xFC, 0x4D, 0xAA, 0xBE, + 0xBD, 0xE6, 0x64, 0x9F, 0x26, 0x9A, 0x2B, 0x97, 0x69, 0xA9, 0xBA, 0x0B, + 0xBD, 0x48, 0xE4, 0x81, 0x6B, 0xD4, 0x4B, 0x78, 0xE6, 0xAF, 0x95, 0x66, + 0xC1, 0x23, 0xDA, 0x23, 0x45, 0x36, 0x6E, 0x25, 0xF3, 0xC7, 0xC0, 0x61, + 0xFC, 0xEC, 0x66, 0x9D, 0x31, 0xD4, 0xD6, 0xB6, 0x36, 0xE3, 0x7F, 0x81, + 0x87, 0x02, 0x03, 0x01, 0x00, 0x01}; + +constexpr std::array kInstallPolicyValidValues = { + kPolicyDisabled, kPolicyEnabled, kPolicyEnabledMachineOnly}; + +constexpr std::array kUpdatePolicyValidValues = { + kPolicyDisabled, kPolicyEnabled, kPolicyManualUpdatesOnly, + kPolicyAutomaticUpdatesOnly}; + +constexpr std::array kProxyModeValidValues = { + kProxyModeDirect, kProxyModeAutoDetect, kProxyModePacScript, + kProxyModeFixedServers, kProxyModeSystem, +}; + +template +bool Contains(const Container& container, const Value& value) { + return std::find(std::begin(container), std::end(container), value) != + std::end(container); +} + +template +bool ContainsStringNoCase(const Container& container, + const CString& string_to_find) { + return std::find_if(std::begin(container), std::end(container), + [&](const auto& value) { + return string_to_find.CompareNoCase(value) == 0; + }) != std::end(container); +} + void SerializeToCStringA(const ::google::protobuf_opensource::Message& message, CStringA* output) { ASSERT1(output); @@ -34,36 +102,734 @@ void SerializeToCStringA(const ::google::protobuf_opensource::Message& message, return; } ::google::protobuf_opensource::uint8* buffer = - reinterpret_cast<::google::protobuf_opensource::uint8*>( - output->GetBufferSetLength(static_cast(byte_size))); + reinterpret_cast<::google::protobuf_opensource::uint8*>( + output->GetBufferSetLength(static_cast(byte_size))); ::google::protobuf_opensource::uint8* end = - message.SerializeWithCachedSizesToArray(buffer); + message.SerializeWithCachedSizesToArray(buffer); output->ReleaseBufferSetLength(end - buffer); } +std::string GetPolicyVerificationKey() { + return std::string(reinterpret_cast(kPolicyVerificationKey), + sizeof(kPolicyVerificationKey)); +} + +bool VerifySignature(const std::string& data, const std::string& key, + const std::string& signature, ALG_ID algorithm_id) { + crypto::SignatureVerifierWin verifier; + if (!verifier.VerifyInit( + algorithm_id, reinterpret_cast(signature.data()), + signature.size(), reinterpret_cast(key.data()), + key.size())) { + REPORT_LOG(LE, (_T("[VerifySignature][Invalid signature/key]"))); + return false; + } + + verifier.VerifyUpdate(reinterpret_cast(data.data()), + data.size()); + + return verifier.VerifyFinal(); +} + +// Verifies that the |new_public_key_verification_data_signature| verifies with +// the hardcoded |GetPolicyVerificationKey()| for the |new_public_key| in +// |fetch_response|. +bool CheckNewPublicKeyVerificationSignature( + const enterprise_management::PolicyFetchResponse& fetch_response) { + if (!fetch_response.has_new_public_key_verification_data() || + !fetch_response.has_new_public_key_verification_data_signature()) { + REPORT_LOG( + LE, + (_T("[CheckNewPublicKeyVerificationSignature]") + _T("[Policy missing new_public_key_verification_data or signature]"))); + return false; + } + + if (!VerifySignature( + fetch_response.new_public_key_verification_data(), + GetPolicyVerificationKey(), + fetch_response.new_public_key_verification_data_signature(), + CALG_SHA_256)) { + REPORT_LOG(LE, (_T("[CheckNewPublicKeyVerificationSignature]") + _T("[Signature verification failed]"))); + return false; + } + + return true; +} + +// We expect to return the cached public key for policy data validation, unless +// there is a new public key in the response, in which case we first validate +// the new public key and then return the new public key for the policy data +// validation. +bool ValidateNewPublicKey( + const enterprise_management::PolicyFetchResponse& fetch_response, + const std::string& cached_public_key, std::string* signature_key, + PolicyValidationResult* validation_result) { + ASSERT1(signature_key); + if (!fetch_response.has_new_public_key_verification_data()) { + if (cached_public_key.empty()) { + REPORT_LOG(LE, (_T("[ValidateNewPublicKey]") + _T("[No public key cached or in response]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationBadSignature; + return false; + } + + *signature_key = cached_public_key; + return true; + } + + // Validate new_public_key() against the hard-coded verification key. + if (!CheckNewPublicKeyVerificationSignature(fetch_response)) { + validation_result->status = + PolicyValidationResult::Status::kValidationBadKeyVerificationSignature; + return false; + } + + enterprise_management::PublicKeyVerificationData public_key_data; + + if (!public_key_data.ParseFromString( + fetch_response.new_public_key_verification_data())) { + REPORT_LOG(LE, (_T("[ValidateNewPublicKey][Failed to deserialize key]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationPayloadParseError; + return false; + } + + // Also validate new_public_key() against the cached_public_key, if the + // latter exists. + if (!cached_public_key.empty()) { + if (!fetch_response.has_new_public_key_signature() || + !VerifySignature(public_key_data.new_public_key(), cached_public_key, + fetch_response.new_public_key_signature(), + CALG_SHA1)) { + REPORT_LOG(LE, (_T("[ValidateNewPublicKey]") + _T("[Verification against cached public key failed]"))); + validation_result->status = PolicyValidationResult::Status:: + kValidationBadKeyVerificationSignature; + return false; + } + } + + // Now that the new public key has been successfully verified, we rotate to + // use it for future policy data validation. + *signature_key = public_key_data.new_public_key(); + + return true; +} + +bool ValidateDMToken(const enterprise_management::PolicyData& policy_data, + const CString& expected_dm_token, + PolicyValidationResult* validation_result) { + if (!policy_data.has_request_token()) { + REPORT_LOG(LW, (_T("[ValidateDMToken][No DMToken in PolicyData]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationBadDMToken; + return false; + } + + CString received_token(policy_data.request_token().c_str()); + if (expected_dm_token.CompareNoCase(received_token)) { + REPORT_LOG(LE, (_T("[ValidateDMToken][Unexpected DMToken]") + _T("[Expected][%s][Got][%s]"), + expected_dm_token, received_token)); + validation_result->status = + PolicyValidationResult::Status::kValidationBadDMToken; + return false; + } + + return true; +} + +bool ValidateDeviceId(const enterprise_management::PolicyData& policy_data, + const CString& expected_device_id, + PolicyValidationResult* validation_result) { + if (!policy_data.has_device_id()) { + REPORT_LOG(LW, (_T("[ValidateDeviceId][No Device Id in PolicyData]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationBadDeviceID; + return false; + } + + CString received_id(policy_data.device_id().c_str()); + if (expected_device_id.CompareNoCase(received_id)) { + REPORT_LOG(LE, (_T("[ValidateDeviceId][Unexpected Device Id]") + _T("[Expected][%s][Got][%s]"), + expected_device_id, received_id)); + validation_result->status = + PolicyValidationResult::Status::kValidationBadDeviceID; + return false; + } + + return true; +} + +bool ValidateTimestamp(const enterprise_management::PolicyData& policy_data, + const int64_t cached_timestamp, + PolicyValidationResult* validation_result) { + if (!policy_data.has_timestamp()) { + REPORT_LOG(LW, (_T("[ValidateTimestamp][No timestamp in PolicyData]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationBadTimestamp; + return false; + } + + if (policy_data.timestamp() < cached_timestamp) { + REPORT_LOG(LE, (_T("[ValidateTimestamp]") + _T("[Unexpected timestamp older than cached timestamp]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationBadTimestamp; + return false; + } + + return true; +} + +std::string PolicyTypeFromResponse( + const enterprise_management::PolicyFetchResponse& response) { + enterprise_management::PolicyData policy_data; + if (!policy_data.ParseFromString(response.policy_data()) || + !policy_data.IsInitialized() || !policy_data.has_policy_type()) { + return std::string(); + } + + return policy_data.policy_type(); +} + +bool ValidatePolicySignature( + const enterprise_management::PolicyFetchResponse& fetch_response, + const std::string& signature_key, + PolicyValidationResult* validation_result) { + ASSERT1(validation_result); + if (!fetch_response.has_policy_data_signature()) { + validation_result->status = + PolicyValidationResult::Status::kValidationBadSignature; + return false; + } + + if (!VerifySignature(fetch_response.policy_data(), signature_key, + fetch_response.policy_data_signature(), CALG_SHA1)) { + validation_result->status = + PolicyValidationResult::Status::kValidationBadSignature; + return false; + } + return true; +} + +enterprise_management::PolicyValidationReportRequest::ValidationResultType +TranslatePolicyValidationResult(PolicyValidationResult::Status status) { + using Report = enterprise_management::PolicyValidationReportRequest; + static const std::map + kValidationStatusMap = { + {PolicyValidationResult::Status::kValidationOK, + Report::VALIDATION_RESULT_TYPE_SUCCESS}, + {PolicyValidationResult::Status::kValidationBadInitialSignature, + Report::VALIDATION_RESULT_TYPE_BAD_INITIAL_SIGNATURE}, + {PolicyValidationResult::Status::kValidationBadSignature, + Report::VALIDATION_RESULT_TYPE_BAD_SIGNATURE}, + {PolicyValidationResult::Status::kValidationErrorCodePresent, + Report::VALIDATION_RESULT_TYPE_ERROR_CODE_PRESENT}, + {PolicyValidationResult::Status::kValidationPayloadParseError, + Report::VALIDATION_RESULT_TYPE_PAYLOAD_PARSE_ERROR}, + {PolicyValidationResult::Status::kValidationWrongPolicyType, + Report::VALIDATION_RESULT_TYPE_WRONG_POLICY_TYPE}, + {PolicyValidationResult::Status::kValidationWrongSettingsEntityID, + Report::VALIDATION_RESULT_TYPE_WRONG_SETTINGS_ENTITY_ID}, + {PolicyValidationResult::Status::kValidationBadTimestamp, + Report::VALIDATION_RESULT_TYPE_BAD_TIMESTAMP}, + {PolicyValidationResult::Status::kValidationBadDMToken, + Report::VALIDATION_RESULT_TYPE_BAD_DM_TOKEN}, + {PolicyValidationResult::Status::kValidationBadDeviceID, + Report::VALIDATION_RESULT_TYPE_BAD_DEVICE_ID}, + {PolicyValidationResult::Status::kValidationBadUser, + Report::VALIDATION_RESULT_TYPE_BAD_USER}, + {PolicyValidationResult::Status::kValidationPolicyParseError, + Report::VALIDATION_RESULT_TYPE_POLICY_PARSE_ERROR}, + {PolicyValidationResult::Status:: + kValidationBadKeyVerificationSignature, + Report::VALIDATION_RESULT_TYPE_BAD_KEY_VERIFICATION_SIGNATURE}, + {PolicyValidationResult::Status::kValidationValueWarning, + Report::VALIDATION_RESULT_TYPE_VALUE_WARNING}, + {PolicyValidationResult::Status::kValidationValueError, + Report::VALIDATION_RESULT_TYPE_VALUE_ERROR}, + }; + + auto mapped_status = kValidationStatusMap.find(status); + return mapped_status == kValidationStatusMap.end() + ? Report::VALIDATION_RESULT_TYPE_ERROR_UNSPECIFIED + : mapped_status->second; +} + +enterprise_management::PolicyValueValidationIssue::ValueValidationIssueSeverity +TranslatePolicyValidationResultSeverity( + PolicyValueValidationIssue::Severity severity) { + using Issue = enterprise_management::PolicyValueValidationIssue; + switch (severity) { + case PolicyValueValidationIssue::Severity::kWarning: + return Issue::VALUE_VALIDATION_ISSUE_SEVERITY_WARNING; + case PolicyValueValidationIssue::Severity::kError: + return Issue::VALUE_VALIDATION_ISSUE_SEVERITY_ERROR; + default: + return Issue::VALUE_VALIDATION_ISSUE_SEVERITY_UNSPECIFIED; + } +} + +bool ExtractOmahaSettingsFromPolicyResponse( + const enterprise_management::PolicyFetchResponse& response, + edm::OmahaSettingsClientProto* omaha_settings, + PolicyValidationResult* validation_result) { + ASSERT1(omaha_settings); + ASSERT1(validation_result); + + enterprise_management::PolicyData policy_data; + if (!policy_data.ParseFromString(response.policy_data()) || + !policy_data.has_policy_value()) { + validation_result->status = + PolicyValidationResult::Status::kValidationPayloadParseError; + return false; + } + + if (!omaha_settings->ParseFromString(policy_data.policy_value())) { + validation_result->status = + PolicyValidationResult::Status::kValidationPolicyParseError; + return false; + } + + return true; +} + +void ValidateAutoUpdateCheckPeriodPolicy( + const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + if (omaha_settings.has_auto_update_check_period_minutes() && + (omaha_settings.auto_update_check_period_minutes() < 0 || + omaha_settings.auto_update_check_period_minutes() > + kMaxAutoUpdateCheckPeriodMinutes)) { + validation_result->issues.emplace_back( + "auto_update_check_period_minutes", + PolicyValueValidationIssue::Severity::kError, + "Value out of range (0 - " + + std::to_string(kMaxAutoUpdateCheckPeriodMinutes) + "): " + + std::to_string(omaha_settings.auto_update_check_period_minutes())); + } +} + +void ValidateDownloadPreferencePolicy( + const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + if (!omaha_settings.has_download_preference()) return; + + const CString download_preference( + omaha_settings.download_preference().c_str()); + if (download_preference.CompareNoCase(kDownloadPreferenceCacheable) != 0) { + validation_result->issues.emplace_back( + "download_preference", PolicyValueValidationIssue::Severity::kWarning, + "Unrecognized download preference: " + + omaha_settings.download_preference()); + } +} + +void ValidateUpdatesSuppressedPolicies( + const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + if (!omaha_settings.has_updates_suppressed()) return; + + if (omaha_settings.updates_suppressed().start_hour() < 0 || + omaha_settings.updates_suppressed().start_hour() >= 24) { + validation_result->issues.emplace_back( + "updates_suppressed.start_hour", + PolicyValueValidationIssue::Severity::kError, + "Value out of range(0 - 23) : " + + std::to_string(omaha_settings.updates_suppressed().start_hour())); + } + if (omaha_settings.updates_suppressed().start_minute() < 0 || + omaha_settings.updates_suppressed().start_minute() >= 60) { + validation_result->issues.emplace_back( + "updates_suppressed.start_minute", + PolicyValueValidationIssue::Severity::kError, + "Value out of range(0 - 59) : " + + std::to_string(omaha_settings.updates_suppressed().start_minute())); + } + if (omaha_settings.updates_suppressed().duration_min() < 0 || + omaha_settings.updates_suppressed().duration_min() > + kMaxUpdatesSuppressedDurationMin) { + validation_result->issues.emplace_back( + "updates_suppressed.duration_min", + PolicyValueValidationIssue::Severity::kError, + "Value out of range(0 - " + + std::to_string(kMaxUpdatesSuppressedDurationMin) + ") : " + + std::to_string(omaha_settings.updates_suppressed().duration_min())); + } +} + +void ValidateProxyPolicies(const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + if (omaha_settings.has_proxy_mode()) { + const CString proxy_mode(omaha_settings.proxy_mode().c_str()); + if (!ContainsStringNoCase(kProxyModeValidValues, proxy_mode)) { + validation_result->issues.emplace_back( + "proxy_mode", PolicyValueValidationIssue::Severity::kError, + "Unrecognized proxy mode: " + omaha_settings.proxy_mode()); + } + } + + if (omaha_settings.has_proxy_server()) { + if (!omaha_settings.has_proxy_mode()) { + validation_result->issues.emplace_back( + "proxy_server", PolicyValueValidationIssue::Severity::kWarning, + "Proxy server setting is ignored because proxy mode is not set."); + } else { + const CString proxy_mode(omaha_settings.proxy_mode().c_str()); + if (proxy_mode.CompareNoCase(kProxyModeFixedServers) != 0) { + validation_result->issues.emplace_back( + "proxy_server", PolicyValueValidationIssue::Severity::kWarning, + "Proxy server setting [" + omaha_settings.proxy_server() + + "] is ignored because proxy mode is not " + "fixed_servers"); + } + } + } + + if (omaha_settings.has_proxy_pac_url()) { + if (!omaha_settings.has_proxy_mode()) { + validation_result->issues.emplace_back( + "proxy_pac_url", PolicyValueValidationIssue::Severity::kWarning, + "Proxy Pac URL setting is ignored because proxy mode is not " + "set."); + } else { + const CString proxy_mode(omaha_settings.proxy_mode().c_str()); + if (proxy_mode.CompareNoCase(kProxyModePacScript) != 0) { + validation_result->issues.emplace_back( + "proxy_pac_url", PolicyValueValidationIssue::Severity::kWarning, + "Proxy Pac URL setting [" + omaha_settings.proxy_pac_url() + + "] is ignored because proxy mode is not " + "pac_script"); + } + } + } +} + +void ValidateInstallDefaultPolicy( + const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + if (omaha_settings.has_install_default() && + !Contains(kInstallPolicyValidValues, omaha_settings.install_default())) { + validation_result->issues.emplace_back( + "install_default", PolicyValueValidationIssue::Severity::kError, + "Invalid install default value: " + + std::to_string(omaha_settings.install_default())); + } +} + +void ValidateUpdateDefaultPolicy( + const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + if (omaha_settings.has_update_default() && + !Contains(kUpdatePolicyValidValues, omaha_settings.update_default())) { + validation_result->issues.emplace_back( + "update_default", PolicyValueValidationIssue::Severity::kError, + "Invalid update default value: " + + std::to_string(omaha_settings.update_default())); + } +} + +void ValidateGlobalPolicies( + const edm::OmahaSettingsClientProto& omaha_settings, + PolicyValidationResult* validation_result) { + ValidateAutoUpdateCheckPeriodPolicy(omaha_settings, validation_result); + ValidateDownloadPreferencePolicy(omaha_settings, validation_result); + ValidateUpdatesSuppressedPolicies(omaha_settings, validation_result); + ValidateProxyPolicies(omaha_settings, validation_result); + ValidateInstallDefaultPolicy(omaha_settings, validation_result); + ValidateUpdateDefaultPolicy(omaha_settings, validation_result); +} + +void ValidateAppInstallPolicy(const edm::ApplicationSettings& app_settings, + PolicyValidationResult* validation_result) { + if (app_settings.has_install() && + !Contains(kInstallPolicyValidValues, app_settings.install())) { + validation_result->issues.emplace_back( + "install", PolicyValueValidationIssue::Severity::kError, + app_settings.app_guid() + " invalid install policy: " + + std::to_string(app_settings.install())); + } +} + +void ValidateAppUpdatePolicy(const edm::ApplicationSettings& app_settings, + PolicyValidationResult* validation_result) { + if (app_settings.has_update() && + !Contains(kUpdatePolicyValidValues, app_settings.update())) { + validation_result->issues.emplace_back( + "update", PolicyValueValidationIssue::Severity::kError, + app_settings.app_guid() + + " invalid update policy: " + std::to_string(app_settings.update())); + } +} + +void ValidateAppTargetChannelPolicy( + const edm::ApplicationSettings& app_settings, + PolicyValidationResult* validation_result) { + if (app_settings.has_target_channel() && + app_settings.target_channel().empty()) { + validation_result->issues.emplace_back( + "target_channel", PolicyValueValidationIssue::Severity::kWarning, + app_settings.app_guid() + " empty policy value"); + } +} + +void ValidateAppTargetVersionPrefixPolicy( + const edm::ApplicationSettings& app_settings, + PolicyValidationResult* validation_result) { + if (app_settings.has_target_version_prefix() && + app_settings.target_version_prefix().empty()) { + validation_result->issues.emplace_back( + "target_version_prefix", PolicyValueValidationIssue::Severity::kWarning, + app_settings.app_guid() + " empty policy value"); + } +} + +void ValidateAppPolicies(const edm::ApplicationSettings& app_settings, + PolicyValidationResult* validation_result) { + if (!app_settings.has_app_guid()) return; + + GUID app_guid = {}; + CString app_guid_str(app_settings.app_guid().c_str()); + if (FAILED(StringToGuidSafe(app_guid_str, &app_guid))) return; + + ValidateAppInstallPolicy(app_settings, validation_result); + ValidateAppUpdatePolicy(app_settings, validation_result); + ValidateAppTargetChannelPolicy(app_settings, validation_result); + ValidateAppTargetVersionPrefixPolicy(app_settings, validation_result); +} + } // namespace +bool ValidateOmahaPolicyResponse( + const enterprise_management::PolicyFetchResponse& response, + PolicyValidationResult* validation_result) { + ASSERT1(validation_result); + + edm::OmahaSettingsClientProto omaha_settings; + if (!ExtractOmahaSettingsFromPolicyResponse(response, &omaha_settings, + validation_result)) { + REPORT_LOG(LE, (_T("[ExtractOmahaSettingsFromPolicyResponse]: %s"), + validation_result->ToString())); + return false; + } + + ValidateGlobalPolicies(omaha_settings, validation_result); + + const auto& repeated_app_settings = omaha_settings.application_settings(); + for (const auto& app_settings : repeated_app_settings) + ValidateAppPolicies(app_settings, validation_result); + + REPORT_LOG(L1, (_T("[ValidateOmahaPolicyResponse]: %s"), + validation_result->ToString())); + return !validation_result->HasErrorIssue(); +} + +bool ValidatePolicyFetchResponse( + const enterprise_management::PolicyFetchResponse& fetch_response, + const CString& expected_dm_token, const CString& expected_device_id, + const CachedPolicyInfo& info, PolicyValidationResult* validation_result) { + enterprise_management::PolicyData fetch_policy_data; + if (!fetch_policy_data.ParseFromString(fetch_response.policy_data())) { + REPORT_LOG(LW, (_T("[ValidatePolicyFetchResponse][Invalid PolicyData]"))); + validation_result->status = + PolicyValidationResult::Status::kValidationPolicyParseError; + return false; + } + + if (fetch_policy_data.has_policy_token()) + validation_result->policy_token = fetch_policy_data.policy_token(); + + if (!ValidateDMToken(fetch_policy_data, expected_dm_token, + validation_result) || + !ValidateDeviceId(fetch_policy_data, expected_device_id, + validation_result) || + !ValidateTimestamp(fetch_policy_data, info.timestamp, + validation_result)) { + return false; + } + + std::string signature_key; + if (!ValidateNewPublicKey(fetch_response, info.key, &signature_key, + validation_result)) + return false; + + validation_result->policy_type = PolicyTypeFromResponse(fetch_response); + if (validation_result->policy_type.empty()) { + REPORT_LOG(LW, (_T("[ValidatePolicyFetchResponse][Missing PolicyType]") + _T("[%d]"), + info.version)); + validation_result->status = + PolicyValidationResult::Status::kValidationWrongPolicyType; + return false; + } + + if (!ValidatePolicySignature(fetch_response, signature_key, + validation_result)) { + REPORT_LOG(LE, (_T("[ValidatePolicySignature]") + _T("[Failed to verify the signature for policy type %s]"), + validation_result->policy_type.c_str())); + return false; + } + + if (validation_result->policy_type == kGoogleUpdatePolicyType && + !ValidateOmahaPolicyResponse(fetch_response, validation_result)) { + return false; + } + return true; +} + +HRESULT GetCachedPolicyInfo(const std::string& raw_response, + CachedPolicyInfo* info) { + ASSERT1(info); + + *info = {}; + + enterprise_management::PolicyFetchResponse response; + enterprise_management::PolicyData policy_data; + enterprise_management::PublicKeyVerificationData verification_data; + if (raw_response.empty() || !response.ParseFromString(raw_response) || + !policy_data.ParseFromString(response.policy_data()) || + !policy_data.has_timestamp() || + !response.has_new_public_key_verification_data() || + !verification_data.ParseFromString( + response.new_public_key_verification_data())) { + return E_UNEXPECTED; + } + + info->key = verification_data.new_public_key(); + if (verification_data.has_new_public_key_version()) { + info->is_version_valid = true; + info->version = verification_data.new_public_key_version(); + } + info->timestamp = policy_data.timestamp(); + + return S_OK; +} + +HRESULT GetCachedOmahaPolicy(const std::string& raw_response, + CachedOmahaPolicy* info) { + ASSERT1(info); + + info->is_managed = false; + info->is_initialized = false; + + enterprise_management::PolicyFetchResponse response; + enterprise_management::PolicyData policy_data; + edm::OmahaSettingsClientProto omaha_settings; + if (raw_response.empty() || !response.ParseFromString(raw_response) || + !policy_data.ParseFromString(response.policy_data()) || + !policy_data.has_policy_value() || + !omaha_settings.ParseFromString(policy_data.policy_value())) { + return E_UNEXPECTED; + } + + info->is_managed = true; + info->is_initialized = true; + + if (omaha_settings.has_auto_update_check_period_minutes()) { + info->auto_update_check_period_minutes = + omaha_settings.auto_update_check_period_minutes(); + } + if (omaha_settings.has_download_preference()) { + info->download_preference = + CString(omaha_settings.download_preference().c_str()); + } + if (omaha_settings.has_updates_suppressed()) { + info->updates_suppressed.start_hour = + omaha_settings.updates_suppressed().start_hour(); + info->updates_suppressed.start_minute = + omaha_settings.updates_suppressed().start_minute(); + info->updates_suppressed.duration_min = + omaha_settings.updates_suppressed().duration_min(); + } + if (omaha_settings.has_proxy_mode()) { + info->proxy_mode = CString(omaha_settings.proxy_mode().c_str()); + } + if (omaha_settings.has_proxy_server()) { + info->proxy_server = CString(omaha_settings.proxy_server().c_str()); + } + if (omaha_settings.has_proxy_pac_url()) { + info->proxy_pac_url = CString(omaha_settings.proxy_pac_url().c_str()); + } + if (omaha_settings.has_install_default()) { + info->install_default = omaha_settings.install_default(); + } + if (omaha_settings.has_update_default()) { + info->update_default = omaha_settings.update_default(); + } + + const auto& repeated_app_settings = omaha_settings.application_settings(); + + for (const auto& app_settings_proto : repeated_app_settings) { + if (!app_settings_proto.has_app_guid()) { + continue; + } + + GUID app_guid; + if (FAILED(StringToGuidSafe(CString(app_settings_proto.app_guid().c_str()), + &app_guid))) { + continue; + } + + ApplicationSettings app_settings; + app_settings.install = app_settings_proto.has_install() + ? app_settings_proto.install() + : info->install_default; + app_settings.update = app_settings_proto.has_update() + ? app_settings_proto.update() + : info->update_default; + + if (app_settings_proto.has_target_channel()) { + app_settings.target_channel = + CString(app_settings_proto.target_channel().c_str()); + } + if (app_settings_proto.has_target_version_prefix()) { + app_settings.target_version_prefix = + CString(app_settings_proto.target_version_prefix().c_str()); + } + if (app_settings_proto.has_rollback_to_target_version()) { + app_settings.rollback_to_target_version = + !!app_settings_proto.rollback_to_target_version(); + } + + info->application_settings.insert(std::make_pair(app_guid, app_settings)); + } + + return S_OK; +} + CStringA SerializeRegisterBrowserRequest(const CStringA& machine_name, + const CStringA& serial_number, const CStringA& os_platform, const CStringA& os_version) { enterprise_management::DeviceManagementRequest dm_request; ::enterprise_management::RegisterBrowserRequest* request = - dm_request.mutable_register_browser_request(); + dm_request.mutable_register_browser_request(); request->set_machine_name(machine_name, machine_name.GetLength()); request->set_os_platform(os_platform, os_platform.GetLength()); request->set_os_version(os_version, os_version.GetLength()); + ::enterprise_management::BrowserDeviceIdentifier* device_identifier = + request->mutable_browser_device_identifier(); + device_identifier->set_computer_name(machine_name); + device_identifier->set_serial_number(serial_number); + CStringA result; SerializeToCStringA(dm_request, &result); return result; } -CStringA SerializePolicyFetchRequest(const CStringA& policy_type) { - // Request signed policy blobs. kPolicyVerificationKeyHash needs to be kept in - // sync with the corresponding value in Chromium's cloud_policy_constants.cc. - static constexpr char kPolicyVerificationKeyHash[] = "1:356l7w"; - +CStringA SerializePolicyFetchRequest(const CStringA& machine_name, + const CStringA& serial_number, + const CStringA& policy_type, + const CachedPolicyInfo& info) { enterprise_management::DeviceManagementRequest policy_request; enterprise_management::PolicyFetchRequest* policy_fetch_request = @@ -73,6 +839,65 @@ CStringA SerializePolicyFetchRequest(const CStringA& policy_type) { enterprise_management::PolicyFetchRequest::SHA1_RSA); policy_fetch_request->set_verification_key_hash(kPolicyVerificationKeyHash); + if (info.is_version_valid) { + policy_fetch_request->set_public_key_version(info.version); + } + + ::enterprise_management::BrowserDeviceIdentifier* device_identifier = + policy_fetch_request->mutable_browser_device_identifier(); + device_identifier->set_computer_name(machine_name); + device_identifier->set_serial_number(serial_number); + + CStringA result; + SerializeToCStringA(policy_request, &result); + return result; +} + +CStringA SerializePolicyValidationReportRequest( + const PolicyValidationResult& validation_result) { + PolicyValidationResult::Status aggregated_status = validation_result.status; + + if (aggregated_status == PolicyValidationResult::Status::kValidationOK) { + for (const PolicyValueValidationIssue& issue : validation_result.issues) { + if (issue.severity == PolicyValueValidationIssue::Severity::kError) { + aggregated_status = + PolicyValidationResult::Status::kValidationValueError; + break; + } else if (issue.severity == + PolicyValueValidationIssue::Severity::kWarning) { + aggregated_status = + PolicyValidationResult::Status::kValidationValueWarning; + } + } + } + + if (aggregated_status == PolicyValidationResult::Status::kValidationOK) { + return CStringA(); + } + + enterprise_management::DeviceManagementRequest policy_request; + + enterprise_management::PolicyValidationReportRequest* + policy_validation_report_request = + policy_request.mutable_policy_validation_report_request(); + policy_validation_report_request->set_validation_result_type( + TranslatePolicyValidationResult(aggregated_status)); + policy_validation_report_request->set_policy_type( + validation_result.policy_type); + policy_validation_report_request->set_policy_token( + validation_result.policy_token); + + for (const PolicyValueValidationIssue& issue : validation_result.issues) { + enterprise_management::PolicyValueValidationIssue* + policy_value_validation_issue = + policy_validation_report_request + ->add_policy_value_validation_issues(); + policy_value_validation_issue->set_policy_name(issue.policy_name); + policy_value_validation_issue->set_severity( + TranslatePolicyValidationResultSeverity(issue.severity)); + policy_value_validation_issue->set_debug_message(issue.message); + } + CStringA result; SerializeToCStringA(policy_request, &result); return result; @@ -110,16 +935,20 @@ HRESULT ParseDeviceRegisterResponse(const std::vector& response, return S_OK; } -HRESULT ParseDevicePolicyResponse(const std::vector& dm_response_array, - PolicyResponsesMap* response_map) { - ASSERT1(response_map); - enterprise_management::DeviceManagementResponse dm_response; +HRESULT ParseDevicePolicyResponse( + const std::vector& dm_response_array, const CachedPolicyInfo& info, + const CString& dm_token, const CString& device_id, + PolicyResponses* responses_out, + std::vector* validation_results) { + ASSERT1(responses_out); + ASSERT1(validation_results); if (dm_response_array.size() > static_cast(std::numeric_limits::max())) { return E_FAIL; } + enterprise_management::DeviceManagementResponse dm_response; if (!dm_response.ParseFromArray(dm_response_array.data(), static_cast(dm_response_array.size()))) { return E_FAIL; @@ -130,38 +959,50 @@ HRESULT ParseDevicePolicyResponse(const std::vector& dm_response_array, return E_FAIL; } - const enterprise_management::DevicePolicyResponse& policy_response = - dm_response.policy_response(); PolicyResponsesMap responses; - for (int i = 0; i < policy_response.responses_size(); ++i) { + bool should_update_cached_info = true; + for (int i = 0; i < dm_response.policy_response().responses_size(); ++i) { + PolicyValidationResult validation_result; const enterprise_management::PolicyFetchResponse& response = - policy_response.responses(i); - enterprise_management::PolicyData policy_data; - if (!policy_data.ParseFromString(response.policy_data()) || - !policy_data.IsInitialized() || - !policy_data.has_policy_type()) { - OPT_LOG(LW, (_T("Ignoring invalid PolicyData"))); + dm_response.policy_response().responses(i); + if (!ValidatePolicyFetchResponse(response, dm_token, device_id, info, + &validation_result)) { + validation_results->push_back(validation_result); continue; } - const std::string& type = policy_data.policy_type(); - if (responses.find(type) != responses.end()) { - OPT_LOG(LW, (_T("Duplicate PolicyFetchResponse for type: %S"), - type.c_str())); + if (should_update_cached_info && + response.has_new_public_key_verification_data()) { + // Cache this policy response for future validations. At the moment, we + // only use the public key information within the cached policy response. + std::string policy_info; + if (!response.SerializeToString(&policy_info)) { + return E_UNEXPECTED; + } + + responses_out->policy_info = std::move(policy_info); + should_update_cached_info = false; + } + + std::string policy_type = PolicyTypeFromResponse(response); + ASSERT1(!policy_type.empty()); + if (responses.find(policy_type) != responses.end()) { + REPORT_LOG(LW, (_T("Duplicate PolicyFetchResponse for type: %S"), + policy_type.c_str())); continue; } std::string policy_fetch_response; if (!response.SerializeToString(&policy_fetch_response)) { - OPT_LOG(LW, (_T("Failed to serialize response for type: %S"), - type.c_str())); + REPORT_LOG(LW, (_T("Failed to serialize response for type: %S"), + policy_type.c_str())); continue; } - responses[type] = std::move(policy_fetch_response); + responses[policy_type] = std::move(policy_fetch_response); } - *response_map = std::move(responses); + responses_out->responses = std::move(responses); return S_OK; } @@ -192,4 +1033,19 @@ HRESULT ParseDeviceManagementResponseError(const std::vector& response, return S_OK; } +bool ShouldDeleteDmToken(const std::vector& response) { + enterprise_management::DeviceManagementResponse dm_response; + if (response.size() > std::numeric_limits::max() || + !dm_response.ParseFromArray(response.data(), + static_cast(response.size()))) { + return false; + } + + return std::find(dm_response.error_detail().begin(), + dm_response.error_detail().end(), + enterprise_management:: + CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN) != + dm_response.error_detail().end(); +} + } // namespace omaha diff --git a/omaha/goopdate/dm_messages.h b/omaha/goopdate/dm_messages.h index 4f2a27488..73bfc10f1 100644 --- a/omaha/goopdate/dm_messages.h +++ b/omaha/goopdate/dm_messages.h @@ -16,22 +16,238 @@ #define OMAHA_GOOPDATE_DM_MESSAGES_H__ #include +#include + +#include +#include +#include #include #include #include #include "base/basictypes.h" +#include "omaha/base/safe_format.h" +#include "omaha/base/utils.h" +#include "omaha/common/const_group_policy.h" + +namespace enterprise_management { +class PolicyFetchResponse; +} // namespace enterprise_management namespace omaha { +// The policy type for Omaha policy settings. +constexpr char kGoogleUpdatePolicyType[] = "google/machine-level-omaha"; + +struct PolicyValueValidationIssue { + enum class Severity { kWarning, kError }; + std::string policy_name; + Severity severity = Severity::kWarning; + std::string message; + + PolicyValueValidationIssue(const std::string& policy_name, Severity severity, + const std::string& message) + : policy_name(policy_name), severity(severity), message(message) {} + + CString ToString() const { + CString result; + SafeCStringAppendFormat(&result, + _T("[PolicyValueValidationIssue][policy_name][%s]") + _T("[severity][%d][message][%s]"), + CString(policy_name.c_str()), severity, + CString(message.c_str())); + return result; + } +}; + +struct PolicyValidationResult { + enum class Status { + // Indicates successful validation. + kValidationOK, + // Bad signature on the initial key. + kValidationBadInitialSignature, + // Bad signature. + kValidationBadSignature, + // Policy blob contains error code. + kValidationErrorCodePresent, + // Policy payload failed to decode. + kValidationPayloadParseError, + // Unexpected policy type. + kValidationWrongPolicyType, + // Unexpected settings entity id. + kValidationWrongSettingsEntityID, + // Timestamp is missing or is older than expected. + kValidationBadTimestamp, + // DM token is empty or doesn't match. + kValidationBadDMToken, + // Device id is empty or doesn't match. + kValidationBadDeviceID, + // User id doesn't match. + kValidationBadUser, + // Policy payload protobuf parse error. + kValidationPolicyParseError, + // Policy key signature could not be verified using the hard-coded + // verification key. + kValidationBadKeyVerificationSignature, + // Policy value validation raised warning(s). + kValidationValueWarning, + // Policy value validation failed with error(s). + kValidationValueError, + }; + + std::string policy_type; + std::string policy_token; + + Status status = Status::kValidationOK; + std::vector issues; + + bool HasErrorIssue() const { + return std::any_of(issues.begin(), issues.end(), [](const auto& issue) { + return issue.severity == + PolicyValueValidationIssue::Severity::kError; + }); + } + + CString ToString() const { + CString result; + SafeCStringAppendFormat(&result, + _T("[PolicyValidationResult][status][%d]") + _T("[policy_type][%s][policy_token][%s]"), + status, CString(policy_type.c_str()), + CString(policy_token.c_str())); + + for (const PolicyValueValidationIssue& issue : issues) { + SafeCStringAppendFormat(&result, _T("\n\t[%s]"), issue.ToString()); + } + return result; + } +}; + // Maps policy types to their corresponding serialized PolicyFetchResponses. using PolicyResponsesMap = std::map; +struct PolicyResponses { + PolicyResponsesMap responses; + std::string policy_info; +}; + +struct CachedPolicyInfo { + std::string key; + bool is_version_valid = false; + int32_t version = -1; + int64_t timestamp = 0; +}; + +struct UpdatesSuppressed { + int64_t start_hour = -1; + int64_t start_minute = -1; + int64_t duration_min = -1; +}; + +struct ApplicationSettings { + int install = -1; + int update = -1; + CString target_channel; + CString target_version_prefix; + int rollback_to_target_version = -1; + + CString ToString() const { + CString result(_T("[ApplicationSettings]")); + SafeCStringAppendFormat(&result, _T("[install][%d]"), install); + SafeCStringAppendFormat(&result, _T("[update][%d]"), update); + SafeCStringAppendFormat(&result, _T("[target_channel][%s]"), + target_channel); + SafeCStringAppendFormat(&result, _T("[target_version_prefix][%s]"), + target_version_prefix); + SafeCStringAppendFormat(&result, _T("[rollback_to_target_version][%d]"), + rollback_to_target_version); + return result; + } +}; + +struct GUIDCompare { + bool operator()(const GUID& left, const GUID& right) const { + return std::memcmp(&left, &right, sizeof(left)) < 0; + } +}; + +struct CachedOmahaPolicy { + bool is_managed = false; + bool is_initialized = false; + + int64_t auto_update_check_period_minutes = -1; + CString download_preference; + int64_t cache_size_limit = -1; + int64_t cache_life_limit = -1; + UpdatesSuppressed updates_suppressed; + CString proxy_mode; + CString proxy_server; + CString proxy_pac_url; + int install_default = -1; + int update_default = -1; + + std::map application_settings; + + CString ToString() const { + CString result(_T("[CachedOmahaPolicy]")); + SafeCStringAppendFormat(&result, _T("[is_initialized][%d]"), + is_initialized); + SafeCStringAppendFormat(&result, _T("[is_managed][%d]"), is_managed); + SafeCStringAppendFormat( + &result, _T("[auto_update_check_period_minutes][%" _T(PRId64) "]"), + auto_update_check_period_minutes); + SafeCStringAppendFormat(&result, _T("[download_preference][%s]"), + download_preference); + SafeCStringAppendFormat(&result, _T("[cache_size_limit][%" _T(PRId64) "]"), + cache_size_limit); + SafeCStringAppendFormat(&result, _T("[cache_life_limit][%" _T(PRId64) "]"), + cache_life_limit); + SafeCStringAppendFormat( + &result, + _T("[updates_suppressed]") _T( + "[%" _T(PRId64) "][%" _T(PRId64) "][%" _T(PRId64) "]"), + updates_suppressed.start_hour, updates_suppressed.start_minute, + updates_suppressed.duration_min); + SafeCStringAppendFormat(&result, _T("[proxy_mode][%s]"), proxy_mode); + SafeCStringAppendFormat(&result, _T("[proxy_server][%s]"), proxy_server); + SafeCStringAppendFormat(&result, _T("[proxy_pac_url][%s]"), proxy_pac_url); + SafeCStringAppendFormat(&result, _T("[install_default][%d]"), + install_default); + SafeCStringAppendFormat(&result, _T("[update_default][%d]"), + update_default); + + for (auto elem : application_settings) { + SafeCStringAppendFormat(&result, _T("[application_settings][%s][%s]"), + GuidToString(elem.first), elem.second.ToString()); + } + + return result; + } +}; + +bool ValidateOmahaPolicyResponse( + const enterprise_management::PolicyFetchResponse& response, + PolicyValidationResult* validation_result); + +HRESULT GetCachedPolicyInfo(const std::string& raw_response, + CachedPolicyInfo* info); + +// Interprets the OmahaSettingsClientProto within the PolicyData and populates +// the |info| with that information. +HRESULT GetCachedOmahaPolicy(const std::string& raw_response, + CachedOmahaPolicy* info); CStringA SerializeRegisterBrowserRequest(const CStringA& machine_name, + const CStringA& serial_number, const CStringA& os_platform, const CStringA& os_version); -CStringA SerializePolicyFetchRequest(const CStringA& policy_type); +CStringA SerializePolicyFetchRequest(const CStringA& machine_name, + const CStringA& serial_number, + const CStringA& policy_type, + const CachedPolicyInfo& info); + +CStringA SerializePolicyValidationReportRequest( + const PolicyValidationResult& validation_result); HRESULT ParseDeviceRegisterResponse(const std::vector& response, CStringA* dm_token); @@ -39,12 +255,19 @@ HRESULT ParseDeviceRegisterResponse(const std::vector& response, // Parses the policies from the DMServer, and return the PolicyFetchResponses in // |responses|. |responses| contains elements in the following format: // {policy_type}=>{SerializeToString-PolicyFetchResponse}. -HRESULT ParseDevicePolicyResponse(const std::vector& dm_response_array, - PolicyResponsesMap* response_map); +HRESULT ParseDevicePolicyResponse( + const std::vector& dm_response_array, const CachedPolicyInfo& info, + const CString& dm_token, const CString& device_id, + PolicyResponses* responses_out, + std::vector* validation_results); HRESULT ParseDeviceManagementResponseError(const std::vector& response, CStringA* error_message); +// Determines whether the DMToken is expected to be deleted based on the +// DMServer response contents. +bool ShouldDeleteDmToken(const std::vector& response); + } // namespace omaha #endif // OMAHA_GOOPDATE_DM_MESSAGES_H__ diff --git a/omaha/goopdate/dm_messages_unittest.cc b/omaha/goopdate/dm_messages_unittest.cc new file mode 100644 index 000000000..07acf51fb --- /dev/null +++ b/omaha/goopdate/dm_messages_unittest.cc @@ -0,0 +1,253 @@ +// Copyright 2021 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "omaha/goopdate/dm_messages.h" + +#include "omaha/testing/unit_test.h" +#include "wireless/android/enterprise/devicemanagement/proto/dm_api.pb.h" +#include "wireless/android/enterprise/devicemanagement/proto/omaha_settings.pb.h" + +namespace omaha { + +class DmMessagesTest : public ::testing::Test { + protected: + void FillFetchResponseWithValidOmahaPolicy( + enterprise_management::PolicyFetchResponse* response) { + wireless_android_enterprise_devicemanagement::OmahaSettingsClientProto + omaha_settings; + + omaha_settings.set_auto_update_check_period_minutes(111); + omaha_settings.set_download_preference( + CStringA(kDownloadPreferenceCacheable)); + omaha_settings.mutable_updates_suppressed()->set_start_hour(8); + omaha_settings.mutable_updates_suppressed()->set_start_minute(8); + omaha_settings.mutable_updates_suppressed()->set_duration_min(47); + omaha_settings.set_proxy_mode(CStringA("PAC_script")); + omaha_settings.set_proxy_pac_url("foo.c/proxy.pa"); + omaha_settings.set_install_default( + wireless_android_enterprise_devicemanagement::INSTALL_DEFAULT_DISABLED); + omaha_settings.set_update_default( + wireless_android_enterprise_devicemanagement::MANUAL_UPDATES_ONLY); + + wireless_android_enterprise_devicemanagement::ApplicationSettings app; + app.set_app_guid(CStringA(kChromeAppId)); + + app.set_install( + wireless_android_enterprise_devicemanagement::INSTALL_DISABLED); + app.set_update( + wireless_android_enterprise_devicemanagement::AUTOMATIC_UPDATES_ONLY); + app.set_target_version_prefix("3.6.55"); + app.set_rollback_to_target_version( + wireless_android_enterprise_devicemanagement:: + ROLLBACK_TO_TARGET_VERSION_ENABLED); + app.set_target_channel("beta"); + + auto repeated_app_settings = omaha_settings.mutable_application_settings(); + repeated_app_settings->Add(std::move(app)); + + enterprise_management::PolicyData policy_data; + policy_data.set_policy_value(omaha_settings.SerializeAsString()); + + response->set_policy_data(policy_data.SerializeAsString()); + } + + void FillFetchResponseWithErroneousOmahaPolicy( + enterprise_management::PolicyFetchResponse* response) { + wireless_android_enterprise_devicemanagement::OmahaSettingsClientProto + omaha_settings; + + omaha_settings.set_auto_update_check_period_minutes(43201); + omaha_settings.set_download_preference("InvalidDownloadPreference"); + omaha_settings.mutable_updates_suppressed()->set_start_hour(25); + omaha_settings.mutable_updates_suppressed()->set_start_minute(-1); + omaha_settings.mutable_updates_suppressed()->set_duration_min(1000); + omaha_settings.set_proxy_mode("weird_proxy_mode"); + omaha_settings.set_proxy_server("unexpected_proxy"); + omaha_settings.set_proxy_pac_url("foo.c/proxy.pa"); + omaha_settings.set_install_default( + wireless_android_enterprise_devicemanagement::INSTALL_DEFAULT_DISABLED); + omaha_settings.set_update_default( + wireless_android_enterprise_devicemanagement::MANUAL_UPDATES_ONLY); + + wireless_android_enterprise_devicemanagement::ApplicationSettings app; + app.set_app_guid(CStringA(kChromeAppId)); + + app.set_install( + wireless_android_enterprise_devicemanagement::INSTALL_DISABLED); + app.set_update( + wireless_android_enterprise_devicemanagement::AUTOMATIC_UPDATES_ONLY); + app.set_target_channel(""); + app.set_target_version_prefix(""); + + auto repeated_app_settings = omaha_settings.mutable_application_settings(); + repeated_app_settings->Add(std::move(app)); + + enterprise_management::PolicyData policy_data; + policy_data.set_policy_value(omaha_settings.SerializeAsString()); + + response->set_policy_data(policy_data.SerializeAsString()); + } +}; + +TEST_F(DmMessagesTest, ValidateOmahaPolicyResponse_RejectPolicyWithoutData) { + enterprise_management::PolicyFetchResponse response; + PolicyValidationResult validation_result; + EXPECT_FALSE(ValidateOmahaPolicyResponse(response, &validation_result)); + EXPECT_EQ(validation_result.status, + PolicyValidationResult::Status::kValidationPayloadParseError); +} + +TEST_F(DmMessagesTest, ValidateOmahaPolicyResponse_RejectNonOmahaPolicy) { + enterprise_management::PolicyFetchResponse response; + enterprise_management::PolicyData policy_data; + policy_data.set_policy_value("non-omaha-policy-data"); + response.set_policy_data(policy_data.SerializeAsString()); + PolicyValidationResult validation_result; + EXPECT_FALSE(ValidateOmahaPolicyResponse(response, &validation_result)); + EXPECT_EQ(validation_result.status, + PolicyValidationResult::Status::kValidationPolicyParseError); + EXPECT_EQ(validation_result.issues.size(), 0); +} + +TEST_F(DmMessagesTest, ValidateOmahaPolicyResponse_Success) { + enterprise_management::PolicyFetchResponse response; + FillFetchResponseWithValidOmahaPolicy(&response); + PolicyValidationResult validation_result; + EXPECT_TRUE(ValidateOmahaPolicyResponse(response, &validation_result)); + EXPECT_EQ(validation_result.status, + PolicyValidationResult::Status::kValidationOK); + EXPECT_EQ(validation_result.issues.size(), 0); +} + +TEST_F(DmMessagesTest, ValidateOmahaPolicyResponse_ErrorPolicyValues) { + enterprise_management::PolicyFetchResponse response; + FillFetchResponseWithErroneousOmahaPolicy(&response); + PolicyValidationResult validation_result; + EXPECT_FALSE(ValidateOmahaPolicyResponse(response, &validation_result)); + EXPECT_EQ(validation_result.status, + PolicyValidationResult::Status::kValidationOK); + EXPECT_EQ(validation_result.issues.size(), 10); + + // auto_update_check_period_minutes + EXPECT_STREQ(validation_result.issues[0].policy_name.c_str(), + "auto_update_check_period_minutes"); + EXPECT_EQ(validation_result.issues[0].severity, + PolicyValueValidationIssue::Severity::kError); + EXPECT_STREQ(validation_result.issues[0].message.c_str(), + "Value out of range (0 - 43200): 43201"); + + // download_preference + EXPECT_STREQ(validation_result.issues[1].policy_name.c_str(), + "download_preference"); + EXPECT_EQ(validation_result.issues[1].severity, + PolicyValueValidationIssue::Severity::kWarning); + EXPECT_STREQ(validation_result.issues[1].message.c_str(), + "Unrecognized download preference: InvalidDownloadPreference"); + + // updates_suppressed.start_hour + EXPECT_STREQ(validation_result.issues[2].policy_name.c_str(), + "updates_suppressed.start_hour"); + EXPECT_EQ(validation_result.issues[2].severity, + PolicyValueValidationIssue::Severity::kError); + EXPECT_STREQ(validation_result.issues[2].message.c_str(), + "Value out of range(0 - 23) : 25"); + + // updates_suppressed.start_minute + EXPECT_STREQ(validation_result.issues[3].policy_name.c_str(), + "updates_suppressed.start_minute"); + EXPECT_EQ(validation_result.issues[3].severity, + PolicyValueValidationIssue::Severity::kError); + EXPECT_STREQ(validation_result.issues[3].message.c_str(), + "Value out of range(0 - 59) : -1"); + + // updates_suppressed.duration_min + EXPECT_STREQ(validation_result.issues[4].policy_name.c_str(), + "updates_suppressed.duration_min"); + EXPECT_EQ(validation_result.issues[4].severity, + PolicyValueValidationIssue::Severity::kError); + EXPECT_STREQ(validation_result.issues[4].message.c_str(), + "Value out of range(0 - 960) : 1000"); + + // proxy_mode + EXPECT_STREQ(validation_result.issues[5].policy_name.c_str(), "proxy_mode"); + EXPECT_EQ(validation_result.issues[5].severity, + PolicyValueValidationIssue::Severity::kError); + EXPECT_STREQ(validation_result.issues[5].message.c_str(), + "Unrecognized proxy mode: weird_proxy_mode"); + + // proxy_server + EXPECT_STREQ(validation_result.issues[6].policy_name.c_str(), "proxy_server"); + EXPECT_EQ(validation_result.issues[6].severity, + PolicyValueValidationIssue::Severity::kWarning); + EXPECT_STREQ(validation_result.issues[6].message.c_str(), + "Proxy server setting [unexpected_proxy] is ignored because " + "proxy mode is not fixed_servers"); + + // proxy_pac_url + EXPECT_STREQ(validation_result.issues[7].policy_name.c_str(), + "proxy_pac_url"); + EXPECT_EQ(validation_result.issues[7].severity, + PolicyValueValidationIssue::Severity::kWarning); + EXPECT_STREQ(validation_result.issues[7].message.c_str(), + "Proxy Pac URL setting [foo.c/proxy.pa] is ignored because " + "proxy mode is not pac_script"); + + // target_channel + EXPECT_STREQ(validation_result.issues[8].policy_name.c_str(), + "target_channel"); + EXPECT_EQ(validation_result.issues[8].severity, + PolicyValueValidationIssue::Severity::kWarning); + EXPECT_STREQ(validation_result.issues[8].message.c_str(), + "{8A69D345-D564-463C-AFF1-A69D9E530F96} empty policy value"); + + // target_version_prefix + EXPECT_STREQ(validation_result.issues[9].policy_name.c_str(), + "target_version_prefix"); + EXPECT_EQ(validation_result.issues[9].severity, + PolicyValueValidationIssue::Severity::kWarning); + EXPECT_STREQ(validation_result.issues[9].message.c_str(), + "{8A69D345-D564-463C-AFF1-A69D9E530F96} empty policy value"); +} + +TEST_F(DmMessagesTest, ShouldDeleteDmToken) { + EXPECT_FALSE(ShouldDeleteDmToken(std::vector())); + + std::vector response; + std::string response_string("unparseable string"); + response.assign(response_string.begin(), response_string.end()); + EXPECT_FALSE(ShouldDeleteDmToken(response)); + + enterprise_management::DeviceManagementResponse dm_response; + enterprise_management::PolicyFetchResponse* policy_response = + dm_response.mutable_policy_response()->add_responses(); + enterprise_management::PolicyData policy_data; + policy_data.set_policy_type("test_policy_type"); + policy_data.set_policy_value("test policy value"); + policy_data.set_username("user"); + policy_data.set_request_token("TestToken"); + policy_data.set_device_id(CStringA("TestDeviceId")); + policy_data.set_timestamp(time(NULL)); + policy_response->set_policy_data(policy_data.SerializeAsString()); + ASSERT_TRUE(dm_response.SerializeToString(&response_string)); + response.assign(response_string.begin(), response_string.end()); + EXPECT_FALSE(ShouldDeleteDmToken(response)); + + dm_response.add_error_detail( + enterprise_management::CBCM_DELETION_POLICY_PREFERENCE_DELETE_TOKEN); + ASSERT_TRUE(dm_response.SerializeToString(&response_string)); + response.assign(response_string.begin(), response_string.end()); + EXPECT_TRUE(ShouldDeleteDmToken(response)); +} + +} // namespace omaha diff --git a/omaha/goopdate/dm_storage.cc b/omaha/goopdate/dm_storage.cc index f5a4a7789..bdcb81334 100644 --- a/omaha/goopdate/dm_storage.cc +++ b/omaha/goopdate/dm_storage.cc @@ -14,6 +14,7 @@ #include "omaha/goopdate/dm_storage.h" +#include #include #include "omaha/base/const_utils.h" @@ -43,7 +44,7 @@ CString LoadEnrollmentTokenFromInstall() { kGoogleUpdateAppId), kRegValueCloudManagementEnrollmentToken, &value); - return SUCCEEDED(hr) ? value : CString(); + return (SUCCEEDED(hr) && IsUuid(value)) ? value : CString(); } // Returns an enrollment token provisioned to the computer via Group Policy, or @@ -62,7 +63,7 @@ CString LoadEnrollmentTokenFromLegacyPolicy() { HRESULT hr = RegKey::GetValue(kRegKeyLegacyGroupPolicy, kRegValueCloudManagementEnrollmentTokenPolicy, &value); - return SUCCEEDED(hr) ? value : CString(); + return (SUCCEEDED(hr) && IsUuid(value)) ? value : CString(); } // Returns an enrollment token provisioned to the computer via Group Policy for @@ -74,7 +75,7 @@ CString LoadEnrollmentTokenFromOldLegacyPolicy() { kRegKeyLegacyGroupPolicy, kRegValueMachineLevelUserCloudPolicyEnrollmentToken, &value); - return SUCCEEDED(hr) ? value : CString(); + return (SUCCEEDED(hr) && IsUuid(value)) ? value : CString(); } #endif // defined(HAS_LEGACY_DM_CLIENT) @@ -118,8 +119,15 @@ HRESULT StoreDmTokenInKey(const CStringA& dm_token, const TCHAR* path) { return hr; } -HRESULT DeleteObsoletePolicies(const CPath& policy_responses_dir, - const std::set& policy_types_base64) { +struct IgnoreCaseCompare { + bool operator() (const CString& a, const CString& b) const { + return a.CompareNoCase(b) < 0; + } +}; + +HRESULT DeleteObsoletePolicies( + const CPath& policy_responses_dir, + const std::set& policy_types_base64) { std::vector files; HRESULT hr = FindFiles(policy_responses_dir, _T("*"), &files); if (FAILED(hr)) { @@ -129,6 +137,7 @@ HRESULT DeleteObsoletePolicies(const CPath& policy_responses_dir, for (const auto& file : files) { if (file == _T(".") || file == _T("..") || + !file.CompareNoCase(kCachedPolicyInfoFileName) || policy_types_base64.count(file)) { continue; } @@ -136,12 +145,38 @@ HRESULT DeleteObsoletePolicies(const CPath& policy_responses_dir, CPath path = policy_responses_dir; VERIFY1(path.Append(file)); REPORT_LOG(L1, (_T("[DeleteObsoletePolicies][Deleting][%s]"), path)); - VERIFY1(SUCCEEDED(DeleteBeforeOrAfterReboot(path))); + VERIFY_SUCCEEDED(DeleteBeforeOrAfterReboot(path)); } return S_OK; } +HRESULT WriteToFile(const CPath& filename, const char* buf, const size_t len) { + ASSERT1(buf); + ASSERT1(len); + + File file; + HRESULT hr = file.Open(filename, true, false); + if (FAILED(hr)) { + REPORT_LOG(LW, (_T("[WriteToFile][Failed Open][%s][%#x]"), filename, hr)); + return hr; + } + + uint32_t bytes_written = 0; + hr = file.WriteAt(0, + reinterpret_cast(buf), + static_cast(len), + 0, + &bytes_written); + if (FAILED(hr)) { + REPORT_LOG(LW, (_T("[WriteToFile][Failed Write][%s][%#x]"), filename, hr)); + return hr; + } + + ASSERT1(bytes_written == len); + return file.SetLength(bytes_written, false); +} + } // namespace DmStorage* DmStorage::instance_ = NULL; @@ -151,7 +186,8 @@ DmStorage* DmStorage::instance_ = NULL; HRESULT DmStorage::CreateInstance(const CString& enrollment_token) { ASSERT1(!instance_); - DmStorage* instance = new DmStorage(enrollment_token); + DmStorage* instance = new DmStorage( + ConfigManager::Instance()->GetPolicyResponsesDir(), enrollment_token); if (!instance) { return E_OUTOFMEMORY; } @@ -203,6 +239,35 @@ CStringA DmStorage::GetDmToken() { return dm_token_; } +bool DmStorage::IsValidDMToken() { + if (IsInvalidDMToken()) { + return false; + } + + return !GetDmToken().IsEmpty(); +} + +HRESULT DmStorage::InvalidateDMToken() { + return StoreDmToken(kInvalidTokenValue); +} + +HRESULT DmStorage::DeleteDmToken() { + HRESULT hr = RegKey::DeleteKey(kRegKeyCompanyEnrollment, true); + + if (SUCCEEDED(hr)) { + dm_token_ = CStringA(); + dm_token_source_ = kDmTokenSourceNone; +#if defined(HAS_LEGACY_DM_CLIENT) + hr = RegKey::DeleteKey(kRegKeyLegacyEnrollment, true); +#endif + } + return hr; +} + +bool DmStorage::IsInvalidDMToken() { + return GetDmToken() == kInvalidTokenValue; +} + HRESULT DmStorage::StoreDmToken(const CStringA& dm_token) { HRESULT hr = StoreDmTokenInKey(dm_token, kRegKeyCompanyEnrollment); if (SUCCEEDED(hr)) { @@ -222,11 +287,28 @@ CString DmStorage::GetDeviceId() { return device_id_; } -HRESULT DmStorage::PersistPolicies(const CPath& policy_responses_dir, - const PolicyResponsesMap& responses) { - std::set policy_types_base64; +HRESULT DmStorage::PersistPolicies(const PolicyResponses& responses) { + HRESULT hr = CreateDir(policy_responses_dir_, NULL); + if (FAILED(hr)) { + REPORT_LOG(LE, (_T("[PersistPolicies][Failed to create folder][%s][%#x]"), + policy_responses_dir_, hr)); + return hr; + } - for (const auto& response : responses) { + if (!responses.policy_info.empty()) { + CPath policy_info_file(policy_responses_dir_); + policy_info_file.Append(kCachedPolicyInfoFileName); + hr = WriteToFile(policy_info_file, responses.policy_info.c_str(), + responses.policy_info.length()); + if (FAILED(hr)) { + REPORT_LOG(LW, (_T("[PersistPolicies][WriteToFile failed][%s][%#x]"), + policy_info_file, hr)); + } + } + + std::set policy_types_base64; + + for (const auto& response : responses.responses) { CStringA encoded_policy_response_dirname; Base64Escape(response.first.c_str(), static_cast(response.first.length()), @@ -235,9 +317,9 @@ HRESULT DmStorage::PersistPolicies(const CPath& policy_responses_dir, CString dirname(encoded_policy_response_dirname); policy_types_base64.emplace(dirname); - CPath policy_response_dir(policy_responses_dir); + CPath policy_response_dir(policy_responses_dir_); policy_response_dir.Append(dirname); - HRESULT hr = CreateDir(policy_response_dir, NULL); + hr = CreateDir(policy_response_dir, NULL); if (FAILED(hr)) { REPORT_LOG(LW, (_T("[PersistPolicies][Failed to create dir][%s][%#x]"), policy_response_dir, hr)); @@ -247,39 +329,126 @@ HRESULT DmStorage::PersistPolicies(const CPath& policy_responses_dir, CPath policy_response_file(policy_response_dir); policy_response_file.Append(kPolicyResponseFileName); - File file; - hr = file.Open(policy_response_file, true, false); + const char* policy_fetch_response = response.second.c_str(); + const size_t len = response.second.length(); + hr = WriteToFile(policy_response_file, policy_fetch_response, len); if (FAILED(hr)) { - REPORT_LOG(LW, (_T("[PersistPolicies][Failed to open][%s][%#x]"), + REPORT_LOG(LW, (_T("[PersistPolicies][WriteToFile failed][%s][%#x]"), policy_response_file, hr)); continue; } + } - uint32 bytes_written = 0; - hr = file.WriteAt(0, - reinterpret_cast(response.second.c_str()), - static_cast(response.second.length()), - 0, - &bytes_written); - if (FAILED(hr)) { - REPORT_LOG(LW, (_T("[PersistPolicies][Failed to write][%s][%#x]"), - policy_response_file, hr)); - continue; + VERIFY_SUCCEEDED( + DeleteObsoletePolicies(policy_responses_dir_, policy_types_base64)); + return S_OK; +} + +HRESULT DmStorage::ReadCachedPolicyInfoFile(CachedPolicyInfo* info) { + ASSERT1(info); + + if (!IsValidDMToken()) { + REPORT_LOG(L1, (_T("[Skip ReadCachedPolicyInfoFile DMToken not valid]"))); + return E_FAIL; + } + + CPath policy_info_file(policy_responses_dir_); + policy_info_file.Append(kCachedPolicyInfoFileName); + + if (!File::Exists(policy_info_file)) { + return S_FALSE; + } + + std::vector data; + HRESULT hr = ReadEntireFileShareMode(policy_info_file, + 0, + FILE_SHARE_READ, + &data); + if (FAILED(hr)) { + REPORT_LOG(LE, (_T("[ReadCachedPolicyInfoFile][Read failed][%s][%#x]"), + policy_info_file, hr)); + return hr; + } + + std::string raw_response(reinterpret_cast(&data[0]), + data.size()); + hr = GetCachedPolicyInfo(raw_response, info); + if (FAILED(hr)) { + REPORT_LOG(LE, (_T("[ReadCachedPolicyInfoFile]") + _T("[GetCachedPolicyInfo failed][%S][%#x]"), + raw_response.c_str(), hr)); + return hr; + } + + return S_OK; +} + +HRESULT DmStorage::ReadCachedOmahaPolicy(CachedOmahaPolicy* info) { + ASSERT1(info); + + if (!IsValidDMToken()) { + REPORT_LOG(L1, (_T("[Skip ReadCachedOmahaPolicy DMToken not valid]"))); + + if (File::Exists(policy_responses_dir_)) { + REPORT_LOG(L1, (_T("[ReadCachedOmahaPolicy: deleting policy cache]"))); + DeleteBeforeOrAfterReboot(policy_responses_dir_); } - ASSERT1(bytes_written == response.second.length()); - VERIFY1(SUCCEEDED(file.SetLength(bytes_written, false))); + return E_FAIL; + } + + CStringA encoded_policy_response_dirname; + Base64Escape(kGoogleUpdatePolicyType, + arraysize(kGoogleUpdatePolicyType) - 1, + &encoded_policy_response_dirname, + true); + + CString dirname(encoded_policy_response_dirname); + CPath policy_response_file(policy_responses_dir_); + policy_response_file.Append(dirname); + policy_response_file.Append(kPolicyResponseFileName); + if (!File::Exists(policy_response_file)) { + return S_FALSE; + } + + std::vector data; + HRESULT hr = ReadEntireFileShareMode(policy_response_file, + 0, + FILE_SHARE_READ, + &data); + if (FAILED(hr)) { + REPORT_LOG(LE, (_T("[ReadCachedOmahaPolicy][Read failed][%s][%#x]"), + policy_response_file, hr)); + return hr; + } + + std::string raw_response(reinterpret_cast(&data[0]), + data.size()); + hr = GetCachedOmahaPolicy(raw_response, info); + if (FAILED(hr)) { + REPORT_LOG(LE, (_T("[ReadCachedOmahaPolicy]") + _T("[GetCachedOmahaPolicy failed][%S][%#x]"), + raw_response.c_str(), hr)); + return hr; } - VERIFY1(SUCCEEDED(DeleteObsoletePolicies(policy_responses_dir, - policy_types_base64))); return S_OK; } -DmStorage::DmStorage(const CString& runtime_enrollment_token) - : runtime_enrollment_token_(runtime_enrollment_token), +DmStorage::DmStorage(const CPath& policy_responses_dir, + const CString& runtime_enrollment_token) + : policy_responses_dir_(policy_responses_dir), + runtime_enrollment_token_(IsUuid(runtime_enrollment_token) + ? runtime_enrollment_token + : CString()), enrollment_token_source_(kETokenSourceNone), dm_token_source_(kDmTokenSourceNone) { + if (!runtime_enrollment_token.IsEmpty() && + !IsUuid(runtime_enrollment_token)) { + REPORT_LOG(LE, (_T("[DmStorage::DmStorage]") + _T("[runtime_enrollment_token is not a guid][%s]"), + runtime_enrollment_token)); + } } void DmStorage::LoadEnrollmentTokenFromStorage() { @@ -343,4 +512,11 @@ void DmStorage::LoadDeviceIdFromStorage() { } } +std::unique_ptr DmStorage::CreateTestInstance( + const CPath& policy_cache_dir, const CString& enrollment_token) { + // Cannot use std::make_unique here because the constructor is private. + return std::unique_ptr( + new DmStorage(policy_cache_dir, enrollment_token)); +} + } // namespace omaha diff --git a/omaha/goopdate/dm_storage.h b/omaha/goopdate/dm_storage.h index 97f7910d3..a069094d8 100644 --- a/omaha/goopdate/dm_storage.h +++ b/omaha/goopdate/dm_storage.h @@ -25,10 +25,21 @@ namespace omaha { +// This DM Token value is written into the registry if the server asks the +// client to invalidate the DM Token. +constexpr char kInvalidTokenValue[] = "INVALID_DM_TOKEN"; + // This is the standard name for the file that PersistPolicies() uses for each // {policy_type} that it receives from the DMServer. const TCHAR kPolicyResponseFileName[] = _T("PolicyFetchResponse"); +// This is the standard name for the file that PersistPolicies() uses to store +// a PolicyFetchResponse received from the DMServer during the previous request. +// The data within the PolicyFetchResponse, such as the public key, version, and +// timestamp are used for subsequent requests and validations of DMServer +// responses. +const TCHAR kCachedPolicyInfoFileName[] = _T("CachedPolicyInfo"); + // A handler for storage related to cloud-based device management of Omaha. This // class provides access to an enrollment token, a device management token, and // a device identifier. @@ -62,6 +73,8 @@ class DmStorage { return enrollment_token_source_; } + CPath policy_responses_dir() const { return policy_responses_dir_; } + // Writes the instance's enrollment token if it was provided at runtime into // Omaha's ClientState key so that it is available for subsequent runs. // Returns S_FALSE if the instance's enrollment token was not provided at @@ -72,6 +85,19 @@ class DmStorage { // one. Returns an empty string if no device management token is found. CStringA GetDmToken(); + // Returns true if the DM Token is valid, where valid is defined as non-blank + // and not invalidated. + bool IsValidDMToken(); + + // Writes |kInvalidTokenValue| into the registry. + HRESULT InvalidateDMToken(); + + // Deletes DM token from the registry. + HRESULT DeleteDmToken(); + + // Returns true if the DM Token has been invalidated. + bool IsInvalidDMToken(); + // Writes |dm_token| into the registry. HRESULT StoreDmToken(const CStringA& dm_token); @@ -84,9 +110,14 @@ class DmStorage { // "PolicyFetchResponse", where the file contents are // {SerializeToString-PolicyFetchResponse}}. // - // Each PolicyFetchResponse file is opened in exclusive mode. If we are unable - // to open or write to this file, the caller is expected to try again later. - // For instance, if UA is calling us, UA will retry at the next UA interval. + // Also, if |responses.policy_info| has data, we persist the data + // (a PolicyFetchResponse) into a file with a fixed file name of + // "CachedPolicyInfo". The PolicyFetchResponse (the new public key, version, + // and timestamp) is used in subsequent policy fetches. + // + // Each file is opened in exclusive mode. If we are unable to open or write to + // files, the caller is expected to try again later. For instance, if UA is + // calling us, UA will retry at the next UA interval. // // Client applications could use ::FindFirstChangeNotificationW on the // subdirectory corresponding to their respective policy_type to watch for @@ -94,13 +125,26 @@ class DmStorage { // To minimize the number of notifications for existing PolicyFetchResponse // files, the files are first modified in-place if the response includes them, // and then the files that do not have a corresponding response are deleted. - static HRESULT PersistPolicies(const CPath& policy_responses_dir, - const PolicyResponsesMap& responses); + HRESULT PersistPolicies(const PolicyResponses& responses); + + // Returns the public key information within the PolicyFetchResponse in + // |policy_responses_dir_|\CachedPolicyInfo. + HRESULT ReadCachedPolicyInfoFile(CachedPolicyInfo* info); + + // Reads the information within the PolicyFetchResponse file within the + // |policy_responses_dir_|\{Base64Encoded{kGoogleUpdatePolicyType}} directory. + // Then calls on GetCachedOmahaPolicy() to populate |info|. + HRESULT ReadCachedOmahaPolicy(CachedOmahaPolicy* info); + + // For testing purpose only. + static std::unique_ptr CreateTestInstance( + const CPath& policy_cache_dir, const CString& enrollment_token); private: // Constructs an instance with a runtime-provided enrollment token (e.g., one // obtained via the etoken extra arg). - explicit DmStorage(const CString& runtime_enrollment_token); + DmStorage(const CPath& policy_responses_dir, + const CString& runtime_enrollment_token); // The possible sources of a device management token, sorted by decreasing // precedence. @@ -134,6 +178,8 @@ class DmStorage { // The origin of the current device management token. DmTokenSource dm_token_source_; + const CPath policy_responses_dir_; + static DmStorage* instance_; friend class DmStorageTest; diff --git a/omaha/goopdate/dm_storage_test_utils.cc b/omaha/goopdate/dm_storage_test_utils.cc index 05d6da500..ab5379522 100644 --- a/omaha/goopdate/dm_storage_test_utils.cc +++ b/omaha/goopdate/dm_storage_test_utils.cc @@ -84,4 +84,4 @@ void WriteLegacyDmToken(const char* dm_token) { #endif // defined(HAS_LEGACY_DM_CLIENT) -} // namespace +} // namespace omaha diff --git a/omaha/goopdate/dm_storage_test_utils.h b/omaha/goopdate/dm_storage_test_utils.h index a191a03b6..3e890262d 100644 --- a/omaha/goopdate/dm_storage_test_utils.h +++ b/omaha/goopdate/dm_storage_test_utils.h @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef OMAHA_GOOPDATE_DM_STORAGE_TEST_UTILS_H_ +#define OMAHA_GOOPDATE_DM_STORAGE_TEST_UTILS_H_ + #include namespace omaha { @@ -31,3 +34,6 @@ void WriteLegacyDmToken(const char* dm_token); #endif // defined(HAS_LEGACY_DM_CLIENT) } // namespace omaha + +#endif // OMAHA_GOOPDATE_DM_STORAGE_TEST_UTILS_H_ + diff --git a/omaha/goopdate/dm_storage_unittest.cc b/omaha/goopdate/dm_storage_unittest.cc index 22aec06e6..f7b066708 100644 --- a/omaha/goopdate/dm_storage_unittest.cc +++ b/omaha/goopdate/dm_storage_unittest.cc @@ -20,8 +20,12 @@ #include "omaha/base/scope_guard.h" #include "omaha/base/string.h" #include "omaha/base/utils.h" +#include "omaha/common/config_manager.h" +#include "omaha/goopdate/dm_messages.h" #include "omaha/goopdate/dm_storage_test_utils.h" #include "omaha/testing/unit_test.h" +#include "wireless/android/enterprise/devicemanagement/proto/dm_api.pb.h" +#include "wireless/android/enterprise/devicemanagement/proto/omaha_settings.pb.h" namespace omaha { @@ -39,7 +43,8 @@ class DmStorageTest : public RegistryProtectedTest { #endif // defined(HAS_LEGACY_DM_CLIENT) DmStorage* NewDmStorage(const CString& enrollment_token) { - return new DmStorage(enrollment_token); + return new DmStorage(ConfigManager::Instance()->GetPolicyResponsesDir(), + enrollment_token); } CPath GetPolicyResponseFilePath(const CPath& policy_responses_dir, @@ -56,31 +61,118 @@ class DmStorageTest : public RegistryProtectedTest { return policy_response_file; } - void VerifyPolicies(const CPath& policy_responses_dir, - const PolicyResponsesMap& expected_responses) { - for (const auto& expected_response : expected_responses) { + void CheckFileContentsMatch(const CPath& file_path, + const std::string& expected_contents) { + std::vector raw_contents; + ASSERT_HRESULT_SUCCEEDED(ReadEntireFileShareMode( + file_path, 0, FILE_SHARE_READ, &raw_contents)); + std::string contents(reinterpret_cast(&raw_contents[0]), + raw_contents.size()); + + ASSERT_STREQ(expected_contents.c_str(), contents.c_str()); + } + + std::string CannedOmahaPolicyFetchResponse() { + wireless_android_enterprise_devicemanagement::OmahaSettingsClientProto + omaha_settings; + + omaha_settings.set_auto_update_check_period_minutes(111); + omaha_settings.set_download_preference( + CStringA(kDownloadPreferenceCacheable)); + omaha_settings.mutable_updates_suppressed()->set_start_hour(8); + omaha_settings.mutable_updates_suppressed()->set_start_minute(8); + omaha_settings.mutable_updates_suppressed()->set_duration_min(47); + omaha_settings.set_proxy_mode(CStringA(kProxyModePacScript)); + omaha_settings.set_proxy_pac_url("foo.c/proxy.pa"); + omaha_settings.set_install_default( + wireless_android_enterprise_devicemanagement::INSTALL_DEFAULT_DISABLED); + omaha_settings.set_update_default( + wireless_android_enterprise_devicemanagement::MANUAL_UPDATES_ONLY); + + wireless_android_enterprise_devicemanagement::ApplicationSettings app; + app.set_app_guid(CStringA(kChromeAppId)); + + app.set_install( + wireless_android_enterprise_devicemanagement::INSTALL_DISABLED); + app.set_update( + wireless_android_enterprise_devicemanagement::AUTOMATIC_UPDATES_ONLY); + app.set_target_version_prefix("3.6.55"); + app.set_rollback_to_target_version( + wireless_android_enterprise_devicemanagement:: + ROLLBACK_TO_TARGET_VERSION_ENABLED); + app.set_target_channel("beta"); + + auto repeated_app_settings = omaha_settings.mutable_application_settings(); + repeated_app_settings->Add(std::move(app)); + + enterprise_management::PolicyData policy_data; + policy_data.set_policy_value(omaha_settings.SerializeAsString()); + + enterprise_management::PolicyFetchResponse response; + response.set_policy_data(policy_data.SerializeAsString()); + + return response.SerializeAsString(); + } + + void CheckCannedCachedOmahaPolicy(const CachedOmahaPolicy& info) { + EXPECT_TRUE(info.is_initialized); + EXPECT_EQ(111, info.auto_update_check_period_minutes); + EXPECT_STREQ(kDownloadPreferenceCacheable, info.download_preference); + EXPECT_EQ(8, info.updates_suppressed.start_hour); + EXPECT_EQ(8, info.updates_suppressed.start_minute); + EXPECT_EQ(47, info.updates_suppressed.duration_min); + EXPECT_STREQ(kProxyModePacScript, info.proxy_mode); + EXPECT_STREQ(_T("foo.c/proxy.pa"), info.proxy_pac_url); + EXPECT_EQ(kPolicyDisabled, info.install_default); + EXPECT_EQ(kPolicyManualUpdatesOnly, info.update_default); + + EXPECT_EQ(info.application_settings.size(), 1); + EXPECT_TRUE(::IsEqualGUID(info.application_settings.begin()->first, + StringToGuid(kChromeAppId))); + const ApplicationSettings& app = info.application_settings.begin()->second; + EXPECT_EQ(kPolicyDisabled, app.install); + EXPECT_EQ(kPolicyAutomaticUpdatesOnly, app.update); + EXPECT_STREQ(_T("3.6.55"), app.target_version_prefix); + EXPECT_TRUE(app.rollback_to_target_version); + EXPECT_STREQ(_T("beta"), app.target_channel); + } + + void VerifyPolicies(DmStorage* dm_storage, + const PolicyResponses& expected_responses) { + const CPath policy_responses_dir(dm_storage->policy_responses_dir()); + if (!expected_responses.policy_info.empty()) { + CPath policy_info_file(policy_responses_dir); + policy_info_file.Append(kCachedPolicyInfoFileName); + CheckFileContentsMatch(policy_info_file, expected_responses.policy_info); + } + + for (const auto& expected_response : expected_responses.responses) { CPath policy_response_file = GetPolicyResponseFilePath( policy_responses_dir, expected_response.first); - std::vector raw_policy_response; - ASSERT_HRESULT_SUCCEEDED(ReadEntireFileShareMode( - policy_response_file, 0, FILE_SHARE_READ, &raw_policy_response)); - const std::string policy_response( - reinterpret_cast(&raw_policy_response[0]), - raw_policy_response.size()); + CheckFileContentsMatch(policy_response_file, expected_response.second); - ASSERT_STREQ(expected_response.second.c_str(), policy_response.c_str()); + if (expected_response.first == kGoogleUpdatePolicyType) { + CachedOmahaPolicy info; + EXPECT_EQ(S_OK, dm_storage->ReadCachedOmahaPolicy(&info)); + CheckCannedCachedOmahaPolicy(info); + } } } }; -const TCHAR DmStorageTest::kETRuntime[] = _T("runtime"); -const TCHAR DmStorageTest::kETInstall[] = _T("install"); -const TCHAR DmStorageTest::kETCompanyPolicy[] = _T("company_policy"); +const TCHAR DmStorageTest::kETRuntime[] = + _T("57FEBE8F-48D0-487B-A788-CF1019DCD452"); +const TCHAR DmStorageTest::kETInstall[] = + _T("075688CE-FECC-43DA-BBFB-228BF9C75758"); +const TCHAR DmStorageTest::kETCompanyPolicy[] = + _T("098BF4B2-361C-4855-900E-2FB967136C64"); const char DmStorageTest::kDmTCompany[] = "company"; #if defined(HAS_LEGACY_DM_CLIENT) -const TCHAR DmStorageTest::kETLegacyPolicy[] = _T("legacy_policy"); -const TCHAR DmStorageTest::kETOldLegacyPolicy[] = _T("old_legacy_policy"); +const TCHAR DmStorageTest::kETLegacyPolicy[] = + _T("55A78AF7-BA8F-4BE0-A93F-148093050293"); +const TCHAR DmStorageTest::kETOldLegacyPolicy[] = + _T("1FFFB822-E79B-4A8C-85B3-00DB4C7E3785"); const char DmStorageTest::kDmTLegacy[] = "legacy"; #endif // defined(HAS_LEGACY_DM_CLIENT) @@ -211,6 +303,7 @@ TEST_F(DmStorageTest, DmTokenFromCompany) { ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken(kDmTCompany)); std::unique_ptr dm_storage(NewDmStorage((CString()))); EXPECT_EQ(dm_storage->GetDmToken(), kDmTCompany); + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("")); } #if defined(HAS_LEGACY_DM_CLIENT) @@ -236,22 +329,27 @@ TEST_F(DmStorageTest, DmTokenPrecedence) { ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken(kDmTCompany)); std::unique_ptr dm_storage(NewDmStorage((CString()))); EXPECT_EQ(dm_storage->GetDmToken(), kDmTCompany); + ASSERT_NO_FATAL_FAILURE(WriteCompanyDmToken("")); } TEST_F(DmStorageTest, PersistPolicies) { + const CPath policy_responses_dir = CPath( + ConcatenatePath(app_util::GetCurrentModuleDirectory(), _T("Policies"))); + + std::unique_ptr dm_storage = + DmStorage::CreateTestInstance(policy_responses_dir, CString()); + EXPECT_HRESULT_SUCCEEDED(dm_storage->StoreDmToken("dm_token")); + PolicyResponsesMap old_responses = { {"google/chrome/machine-level-user", "test-data-chrome"}, {"google/drive/machine-level-user", "test-data-drive"}, {"google/earth/machine-level-user", "test-data-earth"}, }; - const CPath policy_responses_dir = CPath(ConcatenatePath( - app_util::GetCurrentModuleDirectory(), - _T("Policies"))); - ASSERT_HRESULT_SUCCEEDED(DmStorage::PersistPolicies(policy_responses_dir, - old_responses)); - VerifyPolicies(policy_responses_dir, old_responses); + PolicyResponses expected_old_responses = {old_responses, ""}; + ASSERT_HRESULT_SUCCEEDED(dm_storage->PersistPolicies(expected_old_responses)); + VerifyPolicies(dm_storage.get(), expected_old_responses); PolicyResponsesMap new_responses = { {"google/chrome/machine-level-user", "test-data-chr"}, // Shorter data. @@ -259,15 +357,46 @@ TEST_F(DmStorageTest, PersistPolicies) { {"google/earth/machine-level-user", "test-data-earth-foo-bar-baz-foo-bar-baz-foo-bar-baz"}, // Longer data. {"google/newdrive/machine-level-user", "test-data-newdrive"}, // New. + {kGoogleUpdatePolicyType, CannedOmahaPolicyFetchResponse()}, // New. }; - ASSERT_HRESULT_SUCCEEDED(DmStorage::PersistPolicies(policy_responses_dir, - new_responses)); - VerifyPolicies(policy_responses_dir, new_responses); + PolicyResponses expected_new_responses = {new_responses, "expected data"}; + ASSERT_HRESULT_SUCCEEDED(dm_storage->PersistPolicies(expected_new_responses)); + VerifyPolicies(dm_storage.get(), expected_new_responses); EXPECT_FALSE(GetPolicyResponseFilePath( policy_responses_dir, "google/drive/machine-level-user").FileExists()); EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(policy_responses_dir)); + EXPECT_HRESULT_SUCCEEDED(dm_storage->DeleteDmToken()); +} + +TEST_F(DmStorageTest, IsValidDMToken) { + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->DeleteDmToken()); + + EXPECT_FALSE(DmStorage::Instance()->IsValidDMToken()); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->StoreDmToken("dm_token")); + EXPECT_TRUE(DmStorage::Instance()->IsValidDMToken()); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->DeleteDmToken()); +} + +TEST_F(DmStorageTest, IsInvalidDMToken) { + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->DeleteDmToken()); + EXPECT_FALSE(DmStorage::Instance()->IsInvalidDMToken()); + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->StoreDmToken("dm_token")); + EXPECT_FALSE(DmStorage::Instance()->IsInvalidDMToken()); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->InvalidateDMToken()); + EXPECT_TRUE(DmStorage::Instance()->IsInvalidDMToken()); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->DeleteDmToken()); } // This test must access the true registry, so it doesn't use the DmStorageTest @@ -278,4 +407,16 @@ TEST(DmStorageDeviceIdTest, GetDeviceId) { EXPECT_FALSE(DmStorage::Instance()->GetDeviceId().IsEmpty()); } +TEST_F(DmStorageTest, DeleteDmToken) { + EXPECT_HRESULT_SUCCEEDED(DmStorage::CreateInstance(CString())); + ON_SCOPE_EXIT(DmStorage::DeleteInstance); + EXPECT_EQ(DmStorage::Instance()->GetDmToken(), CStringA()); + + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->StoreDmToken("dm_token")); + EXPECT_TRUE(DmStorage::Instance()->IsValidDMToken()); + EXPECT_HRESULT_SUCCEEDED(DmStorage::Instance()->DeleteDmToken()); + EXPECT_FALSE(DmStorage::Instance()->IsValidDMToken()); + EXPECT_EQ(DmStorage::Instance()->GetDmToken(), CStringA()); +} + } // namespace omaha diff --git a/omaha/goopdate/download_manager.cc b/omaha/goopdate/download_manager.cc index d9caa05c7..01325dffb 100644 --- a/omaha/goopdate/download_manager.cc +++ b/omaha/goopdate/download_manager.cc @@ -41,6 +41,7 @@ #include "omaha/base/utils.h" #include "omaha/common/config_manager.h" #include "omaha/common/const_goopdate.h" +#include "omaha/common/google_signaturevalidator.h" #include "omaha/goopdate/model.h" #include "omaha/goopdate/package_cache.h" #include "omaha/goopdate/server_resource.h" @@ -94,15 +95,15 @@ HRESULT CreateNetworkRequest(NetworkRequest** network_request_ptr) { } // TODO(omaha): Unit test this method. -HRESULT ValidateSize(const CString& file_path, uint64 expected_size) { - CORE_LOG(L3, (_T("[ValidateSize][%s][%lld]"), file_path, expected_size)); - ASSERT1(File::Exists(file_path)); +HRESULT ValidateSize(File* source_file, uint64 expected_size) { + CORE_LOG(L3, (_T("[ValidateSize][%lld]"), expected_size)); + ASSERT1(source_file); ASSERT1(expected_size != 0); ASSERT(expected_size <= UINT_MAX, (_T("TODO(omaha): Add uint64 support to GetFileSizeUnopen()."))); uint32 file_size(0); - HRESULT hr = File::GetFileSizeUnopen(file_path, &file_size); + HRESULT hr = source_file->GetLength(&file_size); ASSERT1(SUCCEEDED(hr)); if (FAILED(hr)) { return hr; @@ -217,18 +218,19 @@ CString DownloadManager::GetMessageForError(const ErrorContext& error_context, case GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO: case GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER: case GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER: - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_HASH_MISMATCH, - &message))); + case GOOPDATEDOWNLOAD_E_AUTHENTICODE_VERIFICATION_FAILED: + VERIFY_SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_HASH_MISMATCH, + &message)); break; case GOOPDATEDOWNLOAD_E_CACHING_FAILED: - VERIFY1(SUCCEEDED(formatter.FormatMessage( - &message, IDS_CACHING_ERROR, error_context.extra_code1, &message))); + VERIFY_SUCCEEDED(formatter.FormatMessage( + &message, IDS_CACHING_ERROR, error_context.extra_code1, &message)); break; default: if (!worker_utils::FormatMessageForNetworkError(error_context.error_code, language, &message)) { - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_ERROR, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_ERROR, &message)); } break; } @@ -286,7 +288,7 @@ HRESULT DownloadManager::DownloadApp(App* app) { ++metric_worker_download_succeeded; } - VERIFY1(SUCCEEDED(DeleteStateForApp(app))); + VERIFY_SUCCEEDED(DeleteStateForApp(app)); return hr; } @@ -410,7 +412,7 @@ HRESULT DownloadManager::DoDownloadPackage(Package* package, State* state) { } } - VERIFY1(SUCCEEDED(network_request->Close())); + VERIFY_SUCCEEDED(network_request->Close()); DeleteBeforeOrAfterReboot(unique_filename_path); app->SetCurrentTimeAs(App::TIME_DOWNLOAD_COMPLETE); @@ -463,10 +465,23 @@ HRESULT DownloadManager::DoDownloadPackageFromUrl(const CString& url, // A file has been successfully downloaded from current url. Validate the file // and cache it. - hr = CallAsSelfAndImpersonate2(this, + + // We open the downloaded file as the current (impersonated) user. This + // ensures that we are not reading any privileged files that are otherwise + // inaccessible to the impersonated user. + File source_file; + hr = source_file.OpenShareMode(filename, false, false, FILE_SHARE_READ); + if (FAILED(hr)) { + return hr; + } + + // We copy the file to the Package Cache unimpersonated, since the package + // cache is in a privileged location. + hr = CallAsSelfAndImpersonate3(this, &DownloadManager::CachePackage, static_cast(package), - static_cast(&filename)); + &source_file, + &filename); if (FAILED(hr)) { OPT_LOG(LE, (_T("[DownloadManager::CachePackage failed][%#x]"), hr)); } @@ -483,7 +498,7 @@ void DownloadManager::Cancel(App* app) { for (size_t i = 0; i != download_state_.size(); ++i) { if (app == download_state_[i]->app()) { - VERIFY1(SUCCEEDED(download_state_[i]->CancelNetworkRequest())); + VERIFY_SUCCEEDED(download_state_[i]->CancelNetworkRequest()); } } } @@ -494,7 +509,7 @@ void DownloadManager::CancelAll() { __mutexScope(lock()); for (size_t i = 0; i != download_state_.size(); ++i) { - VERIFY1(SUCCEEDED(download_state_[i]->CancelNetworkRequest())); + VERIFY_SUCCEEDED(download_state_[i]->CancelNetworkRequest()); } } @@ -509,17 +524,29 @@ HRESULT DownloadManager::PurgeAppLowerVersions(const CString& app_id, } HRESULT DownloadManager::CachePackage(const Package* package, - const CString* filename_path) { + File* source_file, + const CString* source_file_path) { ASSERT1(package); - ASSERT1(filename_path); + ASSERT1(source_file); const CString app_id(package->app_version()->app()->app_guid_string()); const CString version(package->app_version()->version()); const CString package_name(package->filename()); PackageCache::Key key(app_id, version, package_name); - HRESULT hr = package_cache()->Put( - key, *filename_path, package->expected_hash()); + HRESULT hr = E_UNEXPECTED; + + if (ConfigManager::Instance()->ShouldVerifyPayloadAuthenticodeSignature()) { + hr = EnsureSignatureIsValid(*source_file_path); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[EnsureSignatureIsValid failed][%s][0x%08x]"), + package->filename(), hr)); + return GOOPDATEDOWNLOAD_E_AUTHENTICODE_VERIFICATION_FAILED; + } + } + + hr = package_cache()->Put( + key, source_file, package->expected_hash()); if (hr != SIGS_E_INVALID_SIGNATURE) { if (FAILED(hr)) { set_error_extra_code1(static_cast(hr)); @@ -532,7 +559,7 @@ HRESULT DownloadManager::CachePackage(const Package* package, // TODO(omaha): It would be nice to detect that we downloaded a proxy // page and tell the user this. It would be even better if we could // display it; that would require a lot more plumbing. - HRESULT size_hr = ValidateSize(*filename_path, package->expected_size()); + HRESULT size_hr = ValidateSize(source_file, package->expected_size()); if (FAILED(size_hr)) { hr = size_hr; } @@ -540,6 +567,21 @@ HRESULT DownloadManager::CachePackage(const Package* package, return hr; } +HRESULT DownloadManager::EnsureSignatureIsValid(const CString& file_path) { + const TCHAR* ext = ::PathFindExtension(file_path); + ASSERT1(ext); + if (*ext != _T('\0')) { + ext++; // Skip the dot. + for (size_t i = 0; i < arraysize(kAuthenticodeVerifiableExtensions); ++i) { + if (CString(kAuthenticodeVerifiableExtensions[i]).CompareNoCase(ext) + == 0) { + return VerifyGoogleAuthenticodeSignature(file_path, true); + } + } + } + return S_OK; +} + // The file is initially downloaded to a temporary unique name, to account // for the case where the same file is downloaded by multiple callers. HRESULT DownloadManager::BuildUniqueFileName(const CString& filename, @@ -553,8 +595,12 @@ HRESULT DownloadManager::BuildUniqueFileName(const CString& filename, return hr; } - // Format of the unique file name is: /-. const CString temp_dir(ConfigManager::Instance()->GetTempDownloadDir()); + if (temp_dir.IsEmpty()) { + return E_UNEXPECTED; + } + + // Format of the unique file name is: /-. CString temp_filename; SafeCStringFormat(&temp_filename, _T("%s-%s"), GuidToString(guid), filename); *unique_filename = ConcatenatePath(temp_dir, temp_filename); diff --git a/omaha/goopdate/download_manager.h b/omaha/goopdate/download_manager.h index fa59ac013..f3c40c586 100644 --- a/omaha/goopdate/download_manager.h +++ b/omaha/goopdate/download_manager.h @@ -27,6 +27,7 @@ namespace omaha { class App; struct ErrorContext; +class File; class HttpClient; struct Lockable; // TODO(omaha): make Lockable a class. class NetworkRequest; @@ -41,7 +42,8 @@ class DownloadManagerInterface { virtual HRESULT PurgeAppLowerVersions(const CString& app_id, const CString& version) = 0; virtual HRESULT CachePackage(const Package* package, - const CString* filename_path) = 0; + File* source_file, + const CString* source_file_path) = 0; virtual HRESULT DownloadApp(App* app) = 0; virtual HRESULT GetPackage(const Package* package, const CString& dir) const = 0; @@ -62,7 +64,8 @@ class DownloadManager : public DownloadManagerInterface { const CString& version); virtual HRESULT CachePackage(const Package* package, - const CString* filename_path); + File* source_file, + const CString* source_file_path); // Downloads the specified app and stores its packages in the package cache. // @@ -128,6 +131,8 @@ class DownloadManager : public DownloadManagerInterface { Package* package, State* state); + HRESULT EnsureSignatureIsValid(const CString& file_path); + bool is_machine() const; CString package_cache_root() const; diff --git a/omaha/goopdate/download_manager_unittest.cc b/omaha/goopdate/download_manager_unittest.cc index 3b35df674..ded044049 100644 --- a/omaha/goopdate/download_manager_unittest.cc +++ b/omaha/goopdate/download_manager_unittest.cc @@ -16,6 +16,7 @@ // TODO(omaha): why so many dependencies for this unit test? #include +#include #include #include "omaha/base/app_util.h" @@ -34,7 +35,6 @@ #include "omaha/goopdate/app_state_waiting_to_download.h" #include "omaha/goopdate/app_unittest_base.h" #include "omaha/goopdate/download_manager.h" -#include "omaha/goopdate/file_hash.h" #include "omaha/testing/unit_test.h" #include "omaha/third_party/smartany/scoped_any.h" @@ -45,24 +45,10 @@ namespace omaha { namespace { -const FileHash kUpdateBinHashSha1 = { - _T("YF2z/br/S6E3KTca0MT7qziJN44="), _T("") -}; -const FileHash kUpdateBinBothHashes = { - _T("YF2z/br/S6E3KTca0MT7qziJN44="), - _T("e5a00aa9991ac8a5ee3109844d84a55583bd20572ad3ffcd42792f3c36b183ad") -}; -const FileHash kUpdateBin1HashSha1 = { - _T("tbYInfmArVRUD62Ex292vN4LtGQ="), _T("") -}; -const FileHash kUpdateBin1BothHashes = { - _T("tbYInfmArVRUD62Ex292vN4LtGQ="), - _T("f955bdcb6611c4e3033cf5104e01c732001da4a79e23f7771fc6f0216195bd6e") -}; -const FileHash kUpdateBin1HashSha256 = { - _T(""), - _T("f955bdcb6611c4e3033cf5104e01c732001da4a79e23f7771fc6f0216195bd6e") -}; +const CString kUpdateBinHashSha256 = + _T("e5a00aa9991ac8a5ee3109844d84a55583bd20572ad3ffcd42792f3c36b183ad"); +const CString kUpdateBin1HashSha256 = + _T("f955bdcb6611c4e3033cf5104e01c732001da4a79e23f7771fc6f0216195bd6e"); const TCHAR kAppGuid1[] = _T("{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}"); const TCHAR kAppGuid2[] = _T("{C7F2B395-A01C-4806-AA07-9163F66AFC48}"); @@ -84,10 +70,6 @@ class DownloadAppWorkItem : public UserWorkItem { DISALLOW_COPY_AND_ASSIGN(DownloadAppWorkItem); }; -bool FileHashesEqual(const FileHash& hash1, const FileHash& hash2) { - return hash1.sha256 == hash2.sha256 && hash1.sha1 == hash2.sha1; -} - } // namespace class DownloadManagerTest : public AppTestBase { @@ -107,6 +89,16 @@ class DownloadManagerTest : public AppTestBase { CleanupFiles(); + RegKey::GetValue(MACHINE_REG_UPDATE_DEV, + kRegValueDisablePayloadAuthenticodeVerification, + &disable_payload_authenticode_verification_); + if (disable_payload_authenticode_verification_) { + // Ensure the registry is in a clean state w.r.t. payload verification. + EXPECT_SUCCEEDED(RegKey::DeleteValue( + MACHINE_REG_UPDATE_DEV, + kRegValueDisablePayloadAuthenticodeVerification)); + } + download_manager_.reset(new DownloadManager(is_machine_)); EXPECT_SUCCEEDED(download_manager_->Initialize()); } @@ -115,11 +107,55 @@ class DownloadManagerTest : public AppTestBase { download_manager_.reset(); CleanupFiles(); + if (disable_payload_authenticode_verification_) { + EXPECT_SUCCEEDED(RegKey::SetValue( + MACHINE_REG_UPDATE_DEV, + kRegValueDisablePayloadAuthenticodeVerification, + disable_payload_authenticode_verification_)); + } else { + EXPECT_SUCCEEDED(RegKey::DeleteValue( + MACHINE_REG_UPDATE_DEV, + kRegValueDisablePayloadAuthenticodeVerification)); + } + AppTestBase::TearDown(); } virtual void CleanupFiles() = 0; + void TestCachePackage(App* app, + const TCHAR* unittest_support_file_name, + HRESULT expected_result) { + CString file_path(app_util::GetCurrentModuleDirectory()); + ASSERT_TRUE(::PathAppend(CStrBuf(file_path, MAX_PATH), + _T("unittest_support"))); + ASSERT_TRUE(::PathAppend(CStrBuf(file_path, MAX_PATH), + unittest_support_file_name)); + ASSERT_TRUE(File::Exists(file_path)); + + File file; + HRESULT hr = file.OpenShareMode(file_path, false, false, FILE_SHARE_READ); + ASSERT_SUCCEEDED(hr); + + uint32 file_size(0); + ASSERT_SUCCEEDED(file.GetLength(&file_size)); + + CryptoHash crypto; + std::vector hash_out; + ASSERT_HRESULT_SUCCEEDED(crypto.Compute(file_path, 0, &hash_out)); + std::string hash; + b2a_hex(&hash_out[0], &hash, hash_out.size()); + + AppVersion* version = app->next_version(); + ASSERT_SUCCEEDED(version->AddPackage(unittest_support_file_name, + file_size, + CString(hash.c_str()))); + + Package* package = version->GetPackage(version->GetNumberOfPackages() - 1); + hr = download_manager_->CachePackage(package, &file, &file_path); + EXPECT_EQ(expected_result, hr) << unittest_support_file_name; + } + static void SetAppStateCheckingForUpdate(App* app) { SetAppStateForUnitTest(app, new fsm::AppStateCheckingForUpdate); } @@ -130,6 +166,7 @@ class DownloadManagerTest : public AppTestBase { const CString cache_path_; std::unique_ptr download_manager_; + DWORD disable_payload_authenticode_verification_ = 0; // Saved from registry }; @@ -174,13 +211,13 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultiplePackagesInOneApp) { "" "" "" "" @@ -200,7 +237,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultiplePackagesInOneApp) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinBothHashes, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); EXPECT_LT(0, app->GetDownloadTimeMs()); @@ -210,12 +247,12 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultiplePackagesInOneApp) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData1.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1HashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBin1HashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); EXPECT_LT(0, app->GetDownloadTimeMs()); - // Sanity check the pings, including the two download metrics pings. + // Check the pings, including the two download metrics pings. CString actual_pings; const PingEventVector& pings(app->ping_events()); for (size_t i = 0; i != pings.size(); ++i) { @@ -269,7 +306,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleApps) { "" "" "" "" "" @@ -308,7 +345,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleApps) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinBothHashes, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); EXPECT_LT(0, app->GetDownloadTimeMs()); @@ -323,7 +360,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleApps) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData1.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1HashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBin1HashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); EXPECT_LT(0, app->GetDownloadTimeMs()); @@ -358,7 +395,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Concurrent) { "" "" "" @@ -374,7 +411,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Concurrent) { "" "" "GetApp(i); SetAppStateWaitingToDownload(app); - std::unique_ptr work_item( - new DownloadAppWorkItem(download_manager_.get(), app)); - // WT_EXECUTELONGFUNCTION causes the thread pool to use multiple threads. ASSERT_HRESULT_SUCCEEDED(thread_pool.QueueUserWorkItem( - work_item.release(), + std::make_unique( + download_manager_.get(), app), COINIT_MULTITHREADED, WT_EXECUTELONGFUNCTION)); } @@ -441,7 +476,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Concurrent) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); @@ -450,7 +485,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Concurrent) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData1.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1BothHashes, package->expected_hash())); + EXPECT_STREQ(kUpdateBin1HashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); @@ -463,6 +498,8 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Concurrent) { } return; } + + thread_pool.Stop(); } // Downloads multiple apps concurrently and cancels the downloads while they @@ -534,12 +571,9 @@ TEST_F(DownloadManagerUserTest, DISABLED_DownloadApp_Cancel) { for (int i = 0; i != kNumApps; ++i) { app = app_bundle_->GetApp(i); SetAppStateWaitingToDownload(app); - - std::unique_ptr work_item( - new DownloadAppWorkItem(download_manager_.get(), app)); - ASSERT_HRESULT_SUCCEEDED(thread_pool.QueueUserWorkItem( - work_item.release(), + std::make_unique( + download_manager_.get(), app), COINIT_MULTITHREADED, WT_EXECUTELONGFUNCTION)); } @@ -626,6 +660,8 @@ TEST_F(DownloadManagerUserTest, DISABLED_DownloadApp_Cancel) { EXPECT_HRESULT_SUCCEEDED(current_state->get_errorCode(&extra_code1)); EXPECT_EQ(0, extra_code1); } + + thread_pool.Stop(); } // Common packages of different apps are not cached by the package cache and @@ -655,7 +691,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleAppsCommonPackage) { "" "" "" @@ -671,7 +707,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleAppsCommonPackage) { "" "" "" @@ -693,7 +729,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleAppsCommonPackage) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); @@ -707,7 +743,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_MultipleAppsCommonPackage) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); } @@ -732,7 +768,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_FileAlreadyInCache) { "" "" "" @@ -752,7 +788,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_FileAlreadyInCache) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); EXPECT_LT(0, app->GetDownloadTimeMs()); @@ -779,7 +815,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_FileAlreadyInCache) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); // No bytes are downloaded if the package has been cached already. EXPECT_EQ(0, package->bytes_downloaded()); @@ -805,7 +841,7 @@ TEST_F(DownloadManagerUserTest, DISABLED_DownloadApp_404) { "" "" "" @@ -825,18 +861,17 @@ TEST_F(DownloadManagerUserTest, DISABLED_DownloadApp_404) { ASSERT_TRUE(package); EXPECT_STREQ(_T("NoSuchFile-OmahaTest.exe"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(0, package->bytes_downloaded()); EXPECT_FALSE(download_manager_->IsPackageAvailable(package)); } -TEST_F(DownloadManagerUserTest, DownloadApp_Sha1HashFailure) { +TEST_F(DownloadManagerUserTest, DownloadApp_FailWhenOnlySha1Hash) { App* app = NULL; ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app)); EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("Hash Fail")))); EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE)); // Allow download. - // Provides the wrong hash for the package. CStringA buffer_string = "" @@ -849,7 +884,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Sha1HashFailure) { "" "" "" @@ -859,22 +894,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Sha1HashFailure) { "" ""; - EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string)); - SetAppStateWaitingToDownload(app); - - EXPECT_EQ(SIGS_E_INVALID_SIGNATURE, download_manager_->DownloadApp(app)); - - const Package* package = app->next_version()->GetPackage(0); - ASSERT_TRUE(package); - EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); - EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1HashSha1, package->expected_hash())); - EXPECT_EQ(2048, package->bytes_downloaded()); - EXPECT_FALSE(download_manager_->IsPackageAvailable(package)); - - // All bytes were downloaded even if the validation of the file has failed. - EXPECT_EQ(2048, package->bytes_downloaded()); - EXPECT_LT(0, app->GetDownloadTimeMs()); + EXPECT_FAILED(LoadBundleFromXml(app_bundle_.get(), buffer_string)); } TEST_F(DownloadManagerUserTest, DownloadApp_Sha256HashFailure) { @@ -896,7 +916,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Sha256HashFailure) { "" "" "" @@ -915,7 +935,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_Sha256HashFailure) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1HashSha256, package->expected_hash())); + EXPECT_STREQ(kUpdateBin1HashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_FALSE(download_manager_->IsPackageAvailable(package)); @@ -943,7 +963,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure_ActualSmaller) { "" "" "" @@ -963,7 +983,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure_ActualSmaller) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048000, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1HashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBin1HashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_FALSE(download_manager_->IsPackageAvailable(package)); @@ -990,7 +1010,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure_ActualLarger) { "" "" "" @@ -1010,7 +1030,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure_ActualLarger) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(20, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBin1HashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBin1HashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_FALSE(download_manager_->IsPackageAvailable(package)); @@ -1039,7 +1059,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_BaseUrlFallback) { "" "" "" @@ -1057,7 +1077,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_BaseUrlFallback) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); } @@ -1083,7 +1103,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_FallbackToNextUrlIfCachingFails) { "" "" "" @@ -1101,7 +1121,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_FallbackToNextUrlIfCachingFails) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(2048, package->bytes_downloaded()); EXPECT_TRUE(download_manager_->IsPackageAvailable(package)); } @@ -1125,7 +1145,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_EulaNotAccepted) { "" "" "" @@ -1147,7 +1167,7 @@ TEST_F(DownloadManagerUserTest, DownloadApp_EulaNotAccepted) { ASSERT_TRUE(package); EXPECT_STREQ(_T("UpdateData.bin"), package->filename()); EXPECT_EQ(2048, package->expected_size()); - EXPECT_TRUE(FileHashesEqual(kUpdateBinHashSha1, package->expected_hash())); + EXPECT_STREQ(kUpdateBinHashSha256, package->expected_hash()); EXPECT_EQ(0, package->bytes_downloaded()); EXPECT_FALSE(download_manager_->IsPackageAvailable(package)); EXPECT_EQ(0, app->GetDownloadTimeMs()); @@ -1172,7 +1192,7 @@ TEST_F(DownloadManagerUserTest, GetPackage) { "" "" "" @@ -1204,7 +1224,7 @@ TEST_F(DownloadManagerUserTest, GetPackage) { CString filename(ConcatenatePath(dir, package->filename())); std::vector files; files.push_back(filename); - EXPECT_SUCCEEDED(VerifyFileHash(files, kUpdateBinHashSha1.sha1)); + EXPECT_SUCCEEDED(VerifyFileHashSha256(files, kUpdateBinHashSha256)); // Getting the package the second time overwrites the destination file // and succeeds. @@ -1213,6 +1233,29 @@ TEST_F(DownloadManagerUserTest, GetPackage) { EXPECT_SUCCEEDED(DeleteDirectory(dir)); } +TEST_F(DownloadManagerUserTest, CachePackage) { + App* app = NULL; + ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app)); + + TestCachePackage(app, _T("SaveArguments.exe"), S_OK); + TestCachePackage(app, _T("sha2_0c15be4a15bb0903c901b1d6c265302f.msi"), S_OK); + // Make sure that unexpected file extensions are handled gracefully: + TestCachePackage(app, _T("declaration.txt"), S_OK); + +#ifdef VERIFY_PAYLOAD_AUTHENTICODE_SIGNATURE + const TCHAR* kFileWithOldCertificate = _T("old_google_certificate.dll"); + TestCachePackage(app, + kFileWithOldCertificate, + GOOPDATEDOWNLOAD_E_AUTHENTICODE_VERIFICATION_FAILED); + // Test that disabling verification makes the previously failing file succeed: + EXPECT_SUCCEEDED(RegKey::SetValue( + MACHINE_REG_UPDATE_DEV, + kRegValueDisablePayloadAuthenticodeVerification, + 1UL)); + TestCachePackage(app, kFileWithOldCertificate, S_OK); +#endif // VERIFY_PAYLOAD_AUTHENTICODE_SIGNATURE +} + TEST_F(DownloadManagerUserTest, GetPackage_NotPresent) { App* app = NULL; ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app)); @@ -1231,7 +1274,7 @@ TEST_F(DownloadManagerUserTest, GetPackage_NotPresent) { "" "" "" @@ -1273,7 +1316,7 @@ TEST(DownloadManagerTest, GetMessageForError) { EXPECT_STREQ( _T("Unable to connect to the Internet. If you use a firewall, please ") - _T("whitelist GoogleUpdate.exe."), + _T("allowlist ") MAIN_EXE_BASE_NAME _T(".exe."), DownloadManager::GetMessageForError( ErrorContext(GOOPDATE_E_NO_NETWORK), kEnglish)); diff --git a/omaha/goopdate/google_update.cc b/omaha/goopdate/google_update.cc index 0b3e5af89..3cd88d051 100644 --- a/omaha/goopdate/google_update.cc +++ b/omaha/goopdate/google_update.cc @@ -31,6 +31,7 @@ #include "omaha/goopdate/google_update3.h" #include "omaha/goopdate/omaha3_idl_datax.h" #include "omaha/goopdate/ondemand.h" +#include "omaha/goopdate/policy_status.h" #include "omaha/goopdate/process_launcher.h" #include "omaha/goopdate/update3web.h" #include "omaha/goopdate/worker.h" @@ -40,6 +41,7 @@ namespace omaha { // Template arguments need to be non-const TCHAR arrays. TCHAR kOnDemandMachineBrokerProgId[] = kProgIDOnDemandMachine; TCHAR kUpdate3WebMachineBrokerProgId[] = kProgIDUpdate3WebMachine; +TCHAR kPolicyStatusMachineBrokerProgId[] = kProgIDPolicyStatusMachine; TCHAR kHKRootUser[] = _T("HKCU"); TCHAR kHKRootMachine[] = _T("HKLM"); TCHAR kProgIDUpdate3COMClassUserLocal[] = kProgIDUpdate3COMClassUser; @@ -51,12 +53,14 @@ END_OBJECT_MAP() BEGIN_OBJECT_MAP(object_map_broker_machine_mode) OBJECT_ENTRY(__uuidof(OnDemandMachineAppsClass), OnDemandMachineBroker) OBJECT_ENTRY(__uuidof(GoogleUpdate3WebMachineClass), Update3WebMachineBroker) + OBJECT_ENTRY(__uuidof(PolicyStatusMachineClass), PolicyStatusMachineBroker) OBJECT_ENTRY(__uuidof(CoCreateAsyncClass), CoCreateAsync) END_OBJECT_MAP() BEGIN_OBJECT_MAP(object_map_ondemand_user_mode) OBJECT_ENTRY(__uuidof(GoogleUpdate3WebUserClass), Update3WebUser) OBJECT_ENTRY(__uuidof(OnDemandUserAppsClass), OnDemandUser) + OBJECT_ENTRY(__uuidof(PolicyStatusUserClass), PolicyStatusUser) OBJECT_ENTRY(__uuidof(CredentialDialogUserClass), CredentialDialogUser) END_OBJECT_MAP() @@ -67,6 +71,8 @@ BEGIN_OBJECT_MAP(object_map_ondemand_machine_mode) OnDemandMachineFallback) OBJECT_ENTRY(__uuidof(GoogleUpdate3WebMachineFallbackClass), Update3WebMachineFallback) + OBJECT_ENTRY(__uuidof(PolicyStatusMachineFallbackClass), + PolicyStatusMachineFallback) OBJECT_ENTRY(__uuidof(CredentialDialogMachineClass), CredentialDialogMachine) END_OBJECT_MAP() @@ -240,10 +246,10 @@ HRESULT RegisterOrUnregisterProxies(void* data, bool is_register) { CORE_LOG(L3, (_T("[RegisterOrUnregisterProxies][%d][%d]"), is_machine, is_register)); - VERIFY1(SUCCEEDED(RegisterOrUnregisterProxies64(is_machine, is_register)) || - !is_register); - VERIFY1(SUCCEEDED(RegisterOrUnregisterProxies32(is_machine, is_register)) || - !is_register); + HRESULT hr = RegisterOrUnregisterProxies64(is_machine, is_register); + VERIFY1(SUCCEEDED(hr) || !is_register); + hr = RegisterOrUnregisterProxies32(is_machine, is_register); + VERIFY1(SUCCEEDED(hr) || !is_register); return S_OK; } diff --git a/omaha/goopdate/google_update3.h b/omaha/goopdate/google_update3.h index 5c043421e..7bfe67a7c 100644 --- a/omaha/goopdate/google_update3.h +++ b/omaha/goopdate/google_update3.h @@ -41,10 +41,10 @@ template struct Update3COMClassMode { static bool is_machine() { return machine; } - static const TCHAR* const prog_id() { return progid; } + static const TCHAR* prog_id() { return progid; } static GUID class_id() { return clsid; } static UINT registry_res_id() { return registry_resid; } - static const TCHAR* const hk_root() { return hkroot; } + static const TCHAR* hk_root() { return hkroot; } }; template diff --git a/omaha/goopdate/google_update_ps.cc b/omaha/goopdate/google_update_ps.cc index 680ca5d0e..064591062 100644 --- a/omaha/goopdate/google_update_ps.cc +++ b/omaha/goopdate/google_update_ps.cc @@ -28,6 +28,7 @@ #include "omaha/goopdate/com_proxy.h" #include "omaha/goopdate/current_state.h" #include "omaha/goopdate/omaha3_idl_datax.h" +#include "omaha/goopdate/policy_status_value.h" namespace omaha { @@ -37,9 +38,11 @@ namespace omaha { #if IS_MACHINE_HANDLER OBJECT_ENTRY_AUTO(__uuidof(GoogleComProxyMachineClass), ComProxy) OBJECT_ENTRY_AUTO(__uuidof(CurrentStateMachineClass), CurrentAppState) + OBJECT_ENTRY_AUTO(__uuidof(PolicyStatusValueMachineClass), PolicyStatusValue) #else OBJECT_ENTRY_AUTO(__uuidof(GoogleComProxyUserClass), ComProxy) OBJECT_ENTRY_AUTO(__uuidof(CurrentStateUserClass), CurrentAppState) + OBJECT_ENTRY_AUTO(__uuidof(PolicyStatusValueUserClass), PolicyStatusValue) #endif namespace { diff --git a/omaha/goopdate/goopdate.cc b/omaha/goopdate/goopdate.cc index 97dac4802..da3ffba5f 100644 --- a/omaha/goopdate/goopdate.cc +++ b/omaha/goopdate/goopdate.cc @@ -33,12 +33,10 @@ // /ua // * Core: // /c -// * Cod Red check: +// * Code Red check: // /cr -// * Cod Red repair: +// * Code Red repair: // /recover [/machine] -// * OneClick: -// /pi "http://www.google.com/" "/install%20%22appguid=%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26lang=en%26appname=Google%2520Chrome%26needsadmin=false" /installsource oneclick // NOLINT // * COM server: // -Embedding @@ -46,6 +44,7 @@ #include #include +#include #include "omaha/base/app_util.h" #include "omaha/base/const_object_names.h" @@ -80,7 +79,6 @@ #include "omaha/common/oem_install_utils.h" #include "omaha/common/scheduled_task_utils.h" #include "omaha/common/stats_uploader.h" -#include "omaha/common/webplugin_utils.h" #include "omaha/core/core.h" #include "omaha/goopdate/code_red_check.h" #include "omaha/goopdate/crash.h" @@ -88,7 +86,6 @@ #include "omaha/goopdate/goopdate_internal.h" #include "omaha/goopdate/goopdate_metrics.h" #include "omaha/goopdate/resource_manager.h" -#include "omaha/net/net_diags.h" #include "omaha/service/service_main.h" #include "omaha/setup/setup_google_update.h" #include "omaha/setup/setup_service.h" @@ -135,20 +132,17 @@ bool CheckRegisteredVersion(const CString& version, case COMMANDLINE_MODE_NOARGS: case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_REPORTCRASH: case COMMANDLINE_MODE_INSTALL: case COMMANDLINE_MODE_UPDATE: case COMMANDLINE_MODE_RECOVER: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_REGISTER_PRODUCT: case COMMANDLINE_MODE_UNREGISTER_PRODUCT: case COMMANDLINE_MODE_SERVICE_REGISTER: case COMMANDLINE_MODE_SERVICE_UNREGISTER: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: return true; // COM servers and services that should only run after installation. @@ -170,10 +164,10 @@ bool CheckRegisteredVersion(const CString& version, default: // This binary's version should be the installed version. CString installed_version; - VERIFY1(SUCCEEDED(RegKey::GetValue( + VERIFY_SUCCEEDED(RegKey::GetValue( ConfigManager::Instance()->registry_update(is_machine), kRegValueInstalledVersion, - &installed_version))); + &installed_version)); return version == installed_version; } } @@ -212,7 +206,7 @@ class GoopdateImpl { HRESULT Main(HINSTANCE instance, const TCHAR* cmd_line, int cmd_show); - HRESULT QueueUserWorkItem(UserWorkItem* work_item, + HRESULT QueueUserWorkItem(std::unique_ptr work_item, DWORD coinit_flags, uint32 flags); @@ -249,9 +243,6 @@ class GoopdateImpl { // Handles error conditions by showing UI if appropriate. void HandleError(HRESULT hr, bool has_ui_been_displayed); - // Handles response to /pi command. - HRESULT HandleWebPlugin(); - // Handles responses to /cr command. HRESULT HandleCodeRedCheck(); @@ -287,11 +278,6 @@ class GoopdateImpl { // whether it is installed and functioning correctly by returning S_OK. HRESULT HandleHealthCheck(); - // The "registermsihelper" switch allows installing the MSI Helper in a - // separate process, isolating any crashes in MSI registration from affecting - // the rest of the codebase. - HRESULT HandleRegisterMsiHelper(); - // TODO(omaha): Reconcile the two uninstall functions and paths. void MaybeUninstallGoogleUpdate(); @@ -299,9 +285,9 @@ class GoopdateImpl { // or the app and there are no other apps registered. HRESULT UninstallIfNecessary(); - // Use PROCESS_MODE_BACKGROUND_BEGIN for processes that do background work. - bool ShouldSetBackgroundPriority(CommandLineMode mode); - HRESULT SetBackgroundPriorityIfNeeded(CommandLineMode mode); + // Use BELOW_NORMAL_PRIORITY_CLASS for processes that do background work. + bool ShouldSetBelowNormalPriority(CommandLineMode mode); + HRESULT SetBelowNormalPriorityIfNeeded(CommandLineMode mode); HRESULT CaptureUserMetrics(); @@ -367,7 +353,7 @@ GoopdateImpl::GoopdateImpl(Goopdate* goopdate, bool is_local_system) // Install the exception handler. If GoogleCrashHandler is running, this will // connect to it for out-of-process handling; if not, it will install an // in-process breakpad crash handler with a callback to upload it. - VERIFY1(SUCCEEDED(InstallExceptionHandler())); + VERIFY_SUCCEEDED(InstallExceptionHandler()); // Hints network configure manager how to create its singleton. NetworkConfigManager::set_is_machine(is_machine_); @@ -400,6 +386,8 @@ GoopdateImpl::~GoopdateImpl() { Stop(); + thread_pool_.reset(); + #if defined(HAS_DEVICE_MANAGEMENT) DmStorage::DeleteInstance(); #endif @@ -407,9 +395,9 @@ GoopdateImpl::~GoopdateImpl() { // Bug 994348 does not repro anymore. // If the assert fires, clean up the key, and fix the code if we have unit // tests or application code that create the key. - ASSERT(!RegKey::HasKey(_T("HKEY_USERS\\.DEFAULT\\Software\\Google\\Update")), + ASSERT(!RegKey::HasKey(_T("HKEY_USERS\\.DEFAULT\\Software\\") PATH_COMPANY_NAME _T("\\Update")), (_T("This assert has fired because it has found the registry key at ") - _T("'HKEY_USERS\\.DEFAULT\\Software\\Google\\Update'. ") + _T("'HKEY_USERS\\.DEFAULT\\Software\\") PATH_COMPANY_NAME _T("\\Update'. ") _T("Please delete the key and report to omaha-core team if ") _T("the assert fires again."))); @@ -425,20 +413,19 @@ GoopdateImpl::~GoopdateImpl() { set_new_handler(NULL); } -HRESULT GoopdateImpl::QueueUserWorkItem(UserWorkItem* work_item, +HRESULT GoopdateImpl::QueueUserWorkItem(std::unique_ptr work_item, DWORD coinit_flags, uint32 flags) { CORE_LOG(L3, (_T("[GoopdateImpl::QueueUserWorkItem]"))); ASSERT1(work_item); - ASSERT1(thread_pool_.get()); - - return thread_pool_->QueueUserWorkItem(work_item, coinit_flags, flags); + return thread_pool_->QueueUserWorkItem(std::move(work_item), + coinit_flags, + flags); } void GoopdateImpl::Stop() { - // The thread pool destructor waits for any remaining jobs to complete. - thread_pool_.reset(); + thread_pool_->Stop(); // Waits a little for any remaining jobs to complete. } // Assumes the resources are loaded and members are initialized. @@ -517,9 +504,9 @@ HRESULT GoopdateImpl::Main(HINSTANCE instance, did_install_uninstall_fail = FAILED(UninstallIfNecessary()); } else if (!has_uninstalled_) { if (args_.mode == COMMANDLINE_MODE_UA) { - VERIFY1(SUCCEEDED(AggregateAndReportMetrics(is_machine_, false))); + VERIFY_SUCCEEDED(AggregateAndReportMetrics(is_machine_, false)); } else if (!is_machine_ || vista_util::IsUserAdmin()) { - VERIFY1(SUCCEEDED(AggregateMetrics(is_machine_))); + VERIFY_SUCCEEDED(AggregateMetrics(is_machine_)); } } @@ -553,7 +540,6 @@ HRESULT GoopdateImpl::Main(HINSTANCE instance, COMMANDLINE_MODE_SERVICE_REGISTER != args_.mode && COMMANDLINE_MODE_SERVICE_UNREGISTER != args_.mode && COMMANDLINE_MODE_HEALTH_CHECK != args_.mode && - COMMANDLINE_MODE_REGISTER_MSI_HELPER != args_.mode && COMMANDLINE_MODE_UNKNOWN != args_.mode && !(COMMANDLINE_MODE_INSTALL == args_.mode && is_machine_ && @@ -580,11 +566,11 @@ HRESULT GoopdateImpl::DoMain(HINSTANCE instance, // The system terminates the process without displaying a retry dialog box // for the user. GoogleUpdate has no user state to be saved, therefore // prompting the user for input is meaningless. - VERIFY1(SUCCEEDED(SetProcessSilentShutdown())); + VERIFY_SUCCEEDED(SetProcessSilentShutdown()); - VERIFY1(SUCCEEDED(CaptureOSMetrics())); + VERIFY_SUCCEEDED(CaptureOSMetrics()); - VERIFY1(SUCCEEDED(vista_util::EnableProcessHeapMetadataProtection())); + VERIFY_SUCCEEDED(vista_util::EnableProcessHeapMetadataProtection()); CString module_path = app_util::GetModulePath(module_instance_); ASSERT1(!module_path.IsEmpty()); @@ -610,6 +596,12 @@ HRESULT GoopdateImpl::DoMain(HINSTANCE instance, // Continue because we want to load the resources and display an error. } +#if defined(HAS_DEVICE_MANAGEMENT) + // Reference the DmStorage instance here so the singleton can be created + // before use. + VERIFY_SUCCEEDED(DmStorage::CreateInstance(args_.extra.enrollment_token)); +#endif + // TODO(omaha3): Interactive updates might be useful for debugging or even // on-demand updates of all apps. Figure out how to expose this. For now, no // install source, which should not happen normally, is used as the trigger. @@ -636,7 +628,7 @@ HRESULT GoopdateImpl::DoMain(HINSTANCE instance, return hr; } - VERIFY1(SUCCEEDED(CaptureUserMetrics())); + VERIFY_SUCCEEDED(CaptureUserMetrics()); // The resources are now loaded and available if applicable for this instance. // If there was no bundle name specified on the command line, we take the @@ -655,7 +647,7 @@ HRESULT GoopdateImpl::DoMain(HINSTANCE instance, bool has_ui_been_displayed = false; if (!is_machine_ && vista_util::IsElevatedWithEnableLUAOn()) { - CORE_LOG(LW, (_T("User GoogleUpdate is possibly running in an unsupported ") + CORE_LOG(LW, (_T("User ") MAIN_EXE_BASE_NAME _T(" is possibly running in an unsupported ") _T("way, at High integrity with UAC possibly enabled."))); } @@ -692,7 +684,7 @@ HRESULT GoopdateImpl::InitializeGoopdateAndLoadResources() { // After parsing the command line, reinstall the crash handler to match the // state of the process. - VERIFY1(SUCCEEDED(InstallExceptionHandler())); + VERIFY_SUCCEEDED(InstallExceptionHandler()); // We have parsed the command line, and we are now resetting is_machine. NetworkConfigManager::set_is_machine( @@ -711,9 +703,9 @@ HRESULT GoopdateImpl::InitializeGoopdateAndLoadResources() { // Set the usage stats as soon as possible, which is after the command line // has been parsed, so that we can report crashes and other stats. - VERIFY1(SUCCEEDED(SetUsageStatsEnable())); + VERIFY_SUCCEEDED(SetUsageStatsEnable()); - VERIFY1(SUCCEEDED(internal::PromoteAppEulaAccepted(is_machine_))); + VERIFY_SUCCEEDED(internal::PromoteAppEulaAccepted(is_machine_)); if (ShouldCheckShutdownEvent(args_.mode) && IsShutdownEventSet()) { return GOOPDATE_E_SHUTDOWN_SIGNALED; @@ -725,6 +717,18 @@ HRESULT GoopdateImpl::InitializeGoopdateAndLoadResources() { return hr; } +#if defined(HAS_DEVICE_MANAGEMENT) + + CachedOmahaPolicy dm_policy; + hr = DmStorage::Instance()->ReadCachedOmahaPolicy(&dm_policy); + if (FAILED(hr)) { + OPT_LOG(LE, (_T("[ReadCachedOmahaPolicy failed][%#x]"), hr)); + } else { + ConfigManager::Instance()->SetOmahaDMPolicies(dm_policy); + } + +#endif // defined(HAS_DEVICE_MANAGEMENT) + return S_OK; } @@ -757,13 +761,7 @@ HRESULT GoopdateImpl::ExecuteMode(bool* has_ui_been_displayed) { ASSERT1(CheckRegisteredVersion(GetVersionString(), is_machine_, mode)); - VERIFY1(SUCCEEDED(SetBackgroundPriorityIfNeeded(mode))); - -#if defined(HAS_DEVICE_MANAGEMENT) - // Reference the DmStorage instance here so the singleton can be created - // before use. - VERIFY1(SUCCEEDED(DmStorage::CreateInstance(args_.extra.enrollment_token))); -#endif + VERIFY_SUCCEEDED(SetBelowNormalPriorityIfNeeded(mode)); #pragma warning(push) // C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by @@ -848,15 +846,9 @@ HRESULT GoopdateImpl::ExecuteMode(bool* has_ui_been_displayed) { NetworkConfigManager::Instance(); switch (mode) { - case COMMANDLINE_MODE_WEBPLUGIN: - return HandleWebPlugin(); - case COMMANDLINE_MODE_CODE_RED_CHECK: return HandleCodeRedCheck(); - case COMMANDLINE_MODE_NETDIAGS: - return NetDiags().Main(); - case COMMANDLINE_MODE_REGISTER_PRODUCT: // TODO(omaha3): Eliminate the need for this mode. return E_FAIL; @@ -920,9 +912,6 @@ HRESULT GoopdateImpl::ExecuteMode(bool* has_ui_been_displayed) { case COMMANDLINE_MODE_HEALTH_CHECK: return HandleHealthCheck(); - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: - return HandleRegisterMsiHelper(); - default: // We have a COMMANDLINE_MODE_ that isn't being handled. ASSERT1(false); @@ -954,12 +943,12 @@ bool GoopdateImpl::IsMachineProcess() { HRESULT GoopdateImpl::HandleReportCrash() { ++metric_goopdate_handle_report_crash; - VERIFY1(SUCCEEDED(AggregateMetrics(is_machine_))); + VERIFY_SUCCEEDED(AggregateMetrics(is_machine_)); ConfigManager* cm = ConfigManager::Instance(); CString upload_url; - VERIFY1(SUCCEEDED(cm->GetCrashReportUrl(&upload_url))); + VERIFY_SUCCEEDED(cm->GetCrashReportUrl(&upload_url)); ASSERT1(!upload_url.IsEmpty()); CrashReporter reporter; @@ -982,7 +971,6 @@ bool GoopdateImpl::ShouldCheckShutdownEvent(CommandLineMode mode) { case COMMANDLINE_MODE_NOARGS: case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_REPORTCRASH: case COMMANDLINE_MODE_RECOVER: @@ -991,13 +979,11 @@ bool GoopdateImpl::ShouldCheckShutdownEvent(CommandLineMode mode) { case COMMANDLINE_MODE_INSTALL: case COMMANDLINE_MODE_UPDATE: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_CODE_RED_CHECK: case COMMANDLINE_MODE_REGISTER_PRODUCT: case COMMANDLINE_MODE_UNREGISTER_PRODUCT: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: case COMMANDLINE_MODE_UA: return false; @@ -1062,10 +1048,8 @@ HRESULT GoopdateImpl::LoadResourceDllIfNecessary(CommandLineMode mode, case COMMANDLINE_MODE_CORE: case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_REPORTCRASH: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_CODE_RED_CHECK: case COMMANDLINE_MODE_REGISTER_PRODUCT: case COMMANDLINE_MODE_UNREGISTER_PRODUCT: @@ -1074,7 +1058,6 @@ HRESULT GoopdateImpl::LoadResourceDllIfNecessary(CommandLineMode mode, case COMMANDLINE_MODE_UNINSTALL: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: default: // These modes do not need the resource DLL. ASSERT1(!internal::CanDisplayUi(mode, false)); @@ -1143,19 +1126,6 @@ HRESULT GoopdateImpl::HandleCodeRedCheck() { return S_OK; } -// Even though http://b/1135173 is fixed, there is still a possibility that only -// some of the files will be copied if Setup is currently running. -// TODO(omaha3): If we save and use the metainstaller for OneClick, that may -// address this. - -// If we're called with the /webplugin command, we need to handle it and exit. -// This is called from the browser and the command line arguments come from the -// website so we need to be restrictive of what we let past. If everything from -// the plugin is valid, we'll relaunch goopdate with the proper commands. -HRESULT GoopdateImpl::HandleWebPlugin() { - return webplugin_utils::DoOneClickInstall(args_); -} - HRESULT GoopdateImpl::DoInstall(bool* has_ui_been_displayed) { OPT_LOG(L1, (_T("[GoopdateImpl::DoInstall]"))); ASSERT1(has_ui_been_displayed); @@ -1200,7 +1170,7 @@ HRESULT GoopdateImpl::DoInstall(bool* has_ui_been_displayed) { if (args_.is_oem_set) { hr = OemInstall(!args_.is_silent_set, // is_interactive - !args_.extra.runtime_only, // is_app_install + args_.extra.runtime_mode == RUNTIME_MODE_NOT_SET, args_.is_eula_required_set, args_.is_install_elevated, install_command_line, @@ -1209,7 +1179,7 @@ HRESULT GoopdateImpl::DoInstall(bool* has_ui_been_displayed) { has_ui_been_displayed); } else { hr = Install(!args_.is_silent_set, // is_interactive - !args_.extra.runtime_only, // is_app_install + args_.extra.runtime_mode == RUNTIME_MODE_NOT_SET, args_.is_eula_required_set, false, args_.is_enterprise_set, @@ -1300,11 +1270,12 @@ HRESULT GoopdateImpl::DoHandoff(bool* has_ui_been_displayed) { // prior version does a handoff to a newer version.) CString session_id = args_.session_id; if (session_id.IsEmpty()) { - VERIFY1(SUCCEEDED(GetGuid(&session_id))); + VERIFY_SUCCEEDED(GetGuid(&session_id)); } hr = InstallApps(is_machine_, !args_.is_silent_set, // is_interactive. + args_.is_always_launch_cmd_set, !args_.is_eula_required_set, // is_eula_accepted. args_.is_oem_set, args_.is_offline_set, @@ -1326,7 +1297,16 @@ HRESULT GoopdateImpl::DoUpdateAllApps(bool* has_ui_been_displayed ) { OPT_LOG(L1, (_T("[GoopdateImpl::DoUpdateAllApps]"))); ASSERT1(has_ui_been_displayed); - HRESULT hr = S_OK; + bool is_interactive_update = !args_.is_silent_set; + + // TODO(omaha): Consider moving InitializeClientSecurity calls inside + // install_apps.cc or maybe to update3_utils::CreateGoogleUpdate3Class(). + HRESULT hr = InitializeClientSecurity(); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[InitializeClientSecurity failed][%#x]"), hr)); + return is_interactive_update ? hr : S_OK; + } + #if defined(HAS_DEVICE_MANAGEMENT) // Make a best-effort attempt to register during UA processing to handle the // following cases: @@ -1339,7 +1319,7 @@ HRESULT GoopdateImpl::DoUpdateAllApps(bool* has_ui_been_displayed ) { // installs/updates and policy fetch. Once we have the Firebase Messaging // feature solidified, we can move the policy fetch logic over there. if (is_machine_) { - hr = dm_client::RegisterIfNeeded(DmStorage::Instance()); + hr = dm_client::RegisterIfNeeded(DmStorage::Instance(), false); if (FAILED(hr)) { OPT_LOG(LE, (_T("[Registration failed][%#x]"), hr)); // Emit to the Event Log. The entry will include details by way of @@ -1359,8 +1339,6 @@ HRESULT GoopdateImpl::DoUpdateAllApps(bool* has_ui_been_displayed ) { } #endif // defined(HAS_DEVICE_MANAGEMENT) - bool is_interactive_update = !args_.is_silent_set; - // TODO(omaha3): Interactive is used as an indication of an on-demand request. // It might also be useful to allow on-demand silent update requests. // This was a request when we added the ability to disable updates. @@ -1375,14 +1353,6 @@ HRESULT GoopdateImpl::DoUpdateAllApps(bool* has_ui_been_displayed ) { install_source = kCmdLineInstallSource_OnDemandUA; } - // TODO(omaha): Consider moving InitializeClientSecurity calls inside - // install_apps.cc or maybe to update3_utils::CreateGoogleUpdate3Class(). - hr = InitializeClientSecurity(); - if (FAILED(hr)) { - ASSERT1(false); - return is_interactive_update ? hr : S_OK; - } - hr = UpdateApps(is_machine_, is_interactive_update, is_on_demand, @@ -1474,32 +1444,6 @@ HRESULT GoopdateImpl::HandleHealthCheck() { return S_OK; } -HRESULT GoopdateImpl::HandleRegisterMsiHelper() { - const TCHAR* key_name = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE; - DWORD is_registered(0); - VERIFY1(SUCCEEDED(RegKey::GetValue(key_name, - kRegValueIsMSIHelperRegistered, - &is_registered))); - if (is_registered) { - return S_OK; - } - - SetupGoogleUpdate setup_google_update(is_machine_, false); - HRESULT hr = setup_google_update.InstallMsiHelper(); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[InstallMsiHelper failed][%#x]"), hr)); - ASSERT1(HRESULT_FROM_WIN32(ERROR_INSTALL_SERVICE_FAILURE) == hr || - HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING) == hr); - return hr; - } - - VERIFY1(SUCCEEDED(RegKey::SetValue(key_name, - kRegValueIsMSIHelperRegistered, - static_cast(1)))); - - return S_OK; -} - // TODO(omaha3): In Omaha 2, this was also called when /ig failed. There is a // separate call to UninstallSelf for /install in goopdate.cc. Should we call // this instead to ensure we ping? Should we try to only call from one location? @@ -1556,7 +1500,7 @@ HRESULT GoopdateImpl::UninstallIfNecessary() { } } -bool GoopdateImpl::ShouldSetBackgroundPriority(CommandLineMode mode) { +bool GoopdateImpl::ShouldSetBelowNormalPriority(CommandLineMode mode) { switch (mode) { // Modes that should be mindful about impacting foreground processes. case COMMANDLINE_MODE_REPORTCRASH: @@ -1572,13 +1516,11 @@ bool GoopdateImpl::ShouldSetBackgroundPriority(CommandLineMode mode) { case COMMANDLINE_MODE_NOARGS: case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_RECOVER: case COMMANDLINE_MODE_SERVICE_REGISTER: case COMMANDLINE_MODE_SERVICE_UNREGISTER: case COMMANDLINE_MODE_INSTALL: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_REGISTER_PRODUCT: case COMMANDLINE_MODE_UNREGISTER_PRODUCT: case COMMANDLINE_MODE_PING: @@ -1590,7 +1532,6 @@ bool GoopdateImpl::ShouldSetBackgroundPriority(CommandLineMode mode) { case COMMANDLINE_MODE_MEDIUM_SERVICE: case COMMANDLINE_MODE_HANDOFF_INSTALL: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: return false; default: @@ -1599,16 +1540,14 @@ bool GoopdateImpl::ShouldSetBackgroundPriority(CommandLineMode mode) { } } -HRESULT GoopdateImpl::SetBackgroundPriorityIfNeeded(CommandLineMode mode) { - if (!ShouldSetBackgroundPriority(mode)) { +HRESULT GoopdateImpl::SetBelowNormalPriorityIfNeeded(CommandLineMode mode) { + if (!ShouldSetBelowNormalPriority(mode)) { return S_FALSE; } - const DWORD priority = - vista_util::IsVistaOrLater() ? PROCESS_MODE_BACKGROUND_BEGIN : - BELOW_NORMAL_PRIORITY_CLASS; - const BOOL succeeded = ::SetPriorityClass(::GetCurrentProcess(), priority); - return succeeded ? S_OK : HRESULTFromLastError(); + return ::SetPriorityClass(::GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS) + ? S_OK + : HRESULTFromLastError(); } HRESULT GoopdateImpl::InstallExceptionHandler() { @@ -1668,7 +1607,7 @@ HRESULT GoopdateImpl::RegisterForDeviceManagement() { return S_FALSE; } - HRESULT hr = dm_client::RegisterIfNeeded(dm_storage); + HRESULT hr = dm_client::RegisterIfNeeded(dm_storage, true); // Exit early if no work was needed. if (hr == S_FALSE) { @@ -1827,7 +1766,7 @@ bool IsMachineProcess(CommandLineMode mode, ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) || goopdate_utils::IsRunningFromOfficialGoopdateDir(true) || _T("omaha_unittest.exe") == app_util::GetCurrentModuleName() || - _T("GoogleUpdate_unsigned.exe") == + MAIN_EXE_BASE_NAME _T("_unsigned.exe") == app_util::GetModuleName(NULL)); // Running in debugger. return is_running_from_official_machine_directory; @@ -1836,17 +1775,6 @@ bool IsMachineProcess(CommandLineMode mode, case COMMANDLINE_MODE_COMBROKER: return is_running_from_official_machine_directory; - // The following always runs as the user and is user-initiated. - case COMMANDLINE_MODE_WEBPLUGIN: - // The install location determines user vs. machine. - // This may not be the desired value when doing a cross-install or using - // the opposite plugin (i.e. user plugin is often used before the machine - // one). - ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) || - goopdate_utils::IsRunningFromOfficialGoopdateDir(true) || - _T("omaha_unittest.exe") == app_util::GetCurrentModuleName()); - return is_running_from_official_machine_directory; - // The following all run silently as the user for user installs or Local // System for machine installs. case COMMANDLINE_MODE_UPDATE: @@ -1901,7 +1829,6 @@ bool IsMachineProcess(CommandLineMode mode, case COMMANDLINE_MODE_UNINSTALL: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) || goopdate_utils::IsRunningFromOfficialGoopdateDir(true) || _T("omaha_unittest.exe") == app_util::GetCurrentModuleName()); @@ -1911,7 +1838,6 @@ bool IsMachineProcess(CommandLineMode mode, // in the wild. case COMMANDLINE_MODE_NOARGS: case COMMANDLINE_MODE_UNKNOWN: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: default: return is_running_from_official_machine_directory; @@ -1937,12 +1863,10 @@ bool CanDisplayUi(CommandLineMode mode, bool is_silent) { case COMMANDLINE_MODE_SERVICE: case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_REPORTCRASH: case COMMANDLINE_MODE_UPDATE: case COMMANDLINE_MODE_RECOVER: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_CODE_RED_CHECK: case COMMANDLINE_MODE_COMSERVER: case COMMANDLINE_MODE_REGISTER_PRODUCT: @@ -1956,7 +1880,6 @@ bool CanDisplayUi(CommandLineMode mode, bool is_silent) { case COMMANDLINE_MODE_UNINSTALL: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: default: // These modes are always silent. return false; @@ -1965,7 +1888,7 @@ bool CanDisplayUi(CommandLineMode mode, bool is_silent) { } // namespace internal -Goopdate* Goopdate::instance_ = NULL; +Goopdate* Goopdate::instance_ = nullptr; Goopdate& Goopdate::Instance() { ASSERT1(instance_); @@ -1994,10 +1917,10 @@ HRESULT Goopdate::Main(HINSTANCE instance, return impl_->Main(instance, cmd_line, cmd_show); } -HRESULT Goopdate::QueueUserWorkItem(UserWorkItem* work_item, +HRESULT Goopdate::QueueUserWorkItem(std::unique_ptr work_item, DWORD coinit_flags, uint32 flags) { - return impl_->QueueUserWorkItem(work_item, coinit_flags, flags); + return impl_->QueueUserWorkItem(std::move(work_item), coinit_flags, flags); } void Goopdate::Stop() { diff --git a/omaha/goopdate/goopdate.h b/omaha/goopdate/goopdate.h index e90b6538e..1ce7f9e9c 100644 --- a/omaha/goopdate/goopdate.h +++ b/omaha/goopdate/goopdate.h @@ -47,7 +47,7 @@ class Goopdate { // Runs the entry point for the application. HRESULT Main(HINSTANCE instance, const TCHAR* cmd_line, int cmd_show); - HRESULT QueueUserWorkItem(UserWorkItem* work_item, + HRESULT QueueUserWorkItem(std::unique_ptr work_item, DWORD coinit_flags, uint32 flags); diff --git a/omaha/goopdate/goopdate.rc b/omaha/goopdate/goopdate.rc index f2c5cdc1b..a75ee4f8a 100644 --- a/omaha/goopdate/goopdate.rc +++ b/omaha/goopdate/goopdate.rc @@ -31,5 +31,3 @@ IDR_GOOGLE_UPDATE3_SERVICE_APPID REGISTRY "omaha/base/generic_reg_file_appid.rg 1 TYPELIB "goopdate\\omaha3_idl.tlb" -IDB_CHROME BITMAP "omaha/goopdate/resources/chrome.bmp" - diff --git a/omaha/goopdate/goopdate_unittest.cc b/omaha/goopdate/goopdate_unittest.cc index a97782d09..e234ceb70 100644 --- a/omaha/goopdate/goopdate_unittest.cc +++ b/omaha/goopdate/goopdate_unittest.cc @@ -24,24 +24,24 @@ namespace omaha { namespace { const TCHAR* const kAppMachineClientStatePath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\"); const TCHAR* const kApp2MachineClientStatePath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{553B2D8C-E6A7-43ed-ACC9-A8BA5D34395F}\\"); const TCHAR* const kAppUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\"); const TCHAR* const kApp2UserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{553B2D8C-E6A7-43ed-ACC9-A8BA5D34395F}\\"); const TCHAR* const kAppMachineClientStateMediumPath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\"); // Update this when new modes are added. -const int kLastMode = COMMANDLINE_MODE_REGISTER_MSI_HELPER; +const int kLastMode = COMMANDLINE_MODE_HEALTH_CHECK; } // namespace @@ -630,7 +630,7 @@ TEST_F(GoopdateRegistryProtectedTest, TEST_F(GoopdateRegistryProtectedTest, PromoteAppEulaAccepted_User_UpdateZero_MediumAppValueOneAndStateKey) { const TCHAR* const kAppUserClientStateMediumPath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\"); EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath)); @@ -720,7 +720,6 @@ static void EnsureUnitTestUpdatedWithNewModes() { case COMMANDLINE_MODE_SERVICE: case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_REPORTCRASH: case COMMANDLINE_MODE_INSTALL: @@ -728,7 +727,6 @@ static void EnsureUnitTestUpdatedWithNewModes() { case COMMANDLINE_MODE_HANDOFF_INSTALL: case COMMANDLINE_MODE_UA: case COMMANDLINE_MODE_RECOVER: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_CODE_RED_CHECK: case COMMANDLINE_MODE_COMSERVER: case COMMANDLINE_MODE_REGISTER_PRODUCT: @@ -742,7 +740,6 @@ static void EnsureUnitTestUpdatedWithNewModes() { case COMMANDLINE_MODE_UNINSTALL: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: // // When adding a new mode, be sure to update kLastMode too. // @@ -761,7 +758,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineDirOnly) { } EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_REGSERVER)); EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNREGSERVER)); - EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_NETDIAGS)); EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_CRASH)); // TODO(omaha): Change to machine. EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_REPORTCRASH)); @@ -776,7 +772,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineDirOnly) { } EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UA)); EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_RECOVER)); - EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_WEBPLUGIN)); EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_CODE_RED_CHECK)); EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_COMSERVER)); { @@ -799,7 +794,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineDirOnly) { EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNINSTALL)); EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_PING)); EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_HEALTH_CHECK)); - EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_REGISTER_MSI_HELPER)); EXPECT_TRUE(FromMachineDirHelper( static_cast(kLastMode + 1))); } @@ -811,7 +805,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_IsLocalSystemOnly) { EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_SERVICE)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REGSERVER)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNREGSERVER)); - EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_NETDIAGS)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_CRASH)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REPORTCRASH)); { @@ -825,7 +818,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_IsLocalSystemOnly) { } EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_UA)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_RECOVER)); - EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_WEBPLUGIN)); EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_CODE_RED_CHECK)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_COMSERVER)); { @@ -845,7 +837,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_IsLocalSystemOnly) { EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNINSTALL)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_PING)); EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_HEALTH_CHECK)); - EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REGISTER_MSI_HELPER)); EXPECT_FALSE(IsLocalSystemHelper( static_cast(kLastMode + 1))); } @@ -860,7 +851,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineOverrideOnly) { } EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_REGSERVER)); EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNREGSERVER)); - EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_NETDIAGS)); EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CRASH)); EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_REPORTCRASH)); { @@ -874,7 +864,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineOverrideOnly) { } EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_UA)); EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_RECOVER)); - EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_WEBPLUGIN)); EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CODE_RED_CHECK)); EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_COMSERVER)); { @@ -897,7 +886,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineOverrideOnly) { EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNINSTALL)); EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_PING)); EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_HEALTH_CHECK)); - EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_REGISTER_MSI_HELPER)); EXPECT_FALSE(MachineOverrideHelper( static_cast(kLastMode + 1))); } @@ -912,7 +900,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminFalseOnly) { } EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGSERVER)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNREGSERVER)); - EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_NETDIAGS)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CRASH)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REPORTCRASH)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_INSTALL)); @@ -920,7 +907,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminFalseOnly) { EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_HANDOFF_INSTALL)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UA)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_RECOVER)); - EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_WEBPLUGIN)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CODE_RED_CHECK)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_COMSERVER)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGISTER_PRODUCT)); @@ -937,7 +923,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminFalseOnly) { EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNINSTALL)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_PING)); EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_HEALTH_CHECK)); - EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGISTER_MSI_HELPER)); EXPECT_FALSE(NeedsAdminFalseHelper( static_cast(kLastMode + 1))); } @@ -952,7 +937,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminTrueOnly) { } EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGSERVER)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNREGSERVER)); - EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_NETDIAGS)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CRASH)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REPORTCRASH)); EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_INSTALL)); @@ -960,7 +944,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminTrueOnly) { EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_HANDOFF_INSTALL)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UA)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_RECOVER)); - EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_WEBPLUGIN)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CODE_RED_CHECK)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_COMSERVER)); EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGISTER_PRODUCT)); @@ -977,7 +960,6 @@ TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminTrueOnly) { EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNINSTALL)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_PING)); EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_HEALTH_CHECK)); - EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGISTER_MSI_HELPER)); EXPECT_FALSE(NeedsAdminTrueHelper( static_cast(kLastMode + 1))); } diff --git a/omaha/goopdate/install_manager.cc b/omaha/goopdate/install_manager.cc index e31872f56..b29abd1ae 100644 --- a/omaha/goopdate/install_manager.cc +++ b/omaha/goopdate/install_manager.cc @@ -71,8 +71,8 @@ InstallManager::InstallManager(const Lockable* model_lock, bool is_machine) ConfigManager::Instance()->GetUserInstallWorkingDir(); CORE_LOG(L3, (_T("[install_working_dir][%s]"), install_working_dir())); - VERIFY1(SUCCEEDED(CreateDir(install_working_dir_, NULL))); - VERIFY1(SUCCEEDED(DeleteDirectoryContents(install_working_dir_))); + VERIFY_SUCCEEDED(CreateDir(install_working_dir_, NULL)); + VERIFY_SUCCEEDED(DeleteDirectoryContents(install_working_dir_)); installer_wrapper_.reset(new InstallerWrapper(is_machine_)); } @@ -361,9 +361,9 @@ void InstallManager::PopulateSuccessfulInstallResultInfo( // Load message based on post install action if not overridden. if (result_info->text.IsEmpty()) { StringFormatter formatter(app->app_bundle()->display_language()); - VERIFY1(SUCCEEDED(formatter.LoadString( + VERIFY_SUCCEEDED(formatter.LoadString( IDS_APPLICATION_INSTALLED_SUCCESSFULLY, - &result_info->text))); + &result_info->text)); } } diff --git a/omaha/goopdate/install_manager_unittest.cc b/omaha/goopdate/install_manager_unittest.cc index 0e2484c36..37b8cdb75 100644 --- a/omaha/goopdate/install_manager_unittest.cc +++ b/omaha/goopdate/install_manager_unittest.cc @@ -54,25 +54,23 @@ namespace omaha { namespace { const TCHAR kAppId[] = _T("{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}"); -const GUID kAppGuid = {0xB18BC01B, 0xE0BD, 0x4BF0, - {0xA3, 0x3E, 0x11, 0x33, 0x05, 0x5E, 0x5F, 0xDE}}; const TCHAR kApp2Id[] = _T("{85794B39-42E5-457c-B567-4A0F2A0FB272}"); const TCHAR kFullAppClientsKeyPath[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}"); const TCHAR kFullAppClientStateKeyPath[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}"); const TCHAR kFullFooAppClientKeyPath[] = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{D6B08267-B440-4C85-9F79-E195E80D9937}"); const TCHAR kFullFooAppClientStateKeyPath[] = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{D6B08267-B440-4C85-9F79-E195E80D9937}"); const TCHAR kFullApp2ClientsKeyPath[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{85794B39-42E5-457c-B567-4A0F2A0FB272}"); const TCHAR kSetupFooV1RelativeLocation[] = @@ -95,14 +93,7 @@ const TCHAR kMsiLogFormat[] = _T("%s.log"); // brand, InstallTime, DayOfInstall, DayOfLastActivity, DayOfLastRollCall, and // LastCheckSuccess are automatically populated. -const int kNumAutoPopulatedValues = 6; - -FileHash CreateFileHash(const CString& sha1, const CString& sha256) { - FileHash hash; - hash.sha1 = sha1; - hash.sha256 = sha256; - return hash; -} +const int kNumAutoPopulatedValues = 7; } // namespace @@ -122,7 +113,7 @@ class InstallManagerTest : public testing::TestWithParam { virtual void SetUp() {} virtual void TearDown() {} - const bool IsMachine() const { + bool IsMachine() const { return GetParam(); } @@ -407,8 +398,7 @@ TEST_F(InstallManagerInstallAppUserTest, TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallerWithoutFilenameExtension) { - app_->next_version()->AddPackage( - _T("foo"), 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T("foo"), 100, _T("sha256hash")); // TODO(omaha): We should be able to eliminate this. SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_); @@ -429,8 +419,7 @@ TEST_F(InstallManagerInstallAppUserTest, TEST_F(InstallManagerInstallAppUserTest, InstallApp_UnsupportedInstallerFilenameExtension) { - app_->next_version()->AddPackage( - _T("foo.bar"), 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T("foo.bar"), 100, _T("sha256hash")); // TODO(omaha): We should be able to eliminate this. SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_); @@ -449,12 +438,11 @@ TEST_F(InstallManagerInstallAppUserTest, EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_)); } -TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallerEmtpyFilename) { +TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallerEmptyFilename) { // Package asserts that the filename and file path are not NULL. ExpectAsserts expect_asserts; - app_->next_version()->AddPackage( - _T(""), 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T(""), 100, _T("sha256hash")); // This test does not call // app_->next_version()->GetPackage(0)->set_local_file_path(). @@ -492,8 +480,7 @@ TEST_F(InstallManagerInstallAppUserTest, InstallApp_NoPackage) { } TEST_F(InstallManagerInstallAppUserTest, InstallApp_ExeFileDoesNotExist) { - app_->next_version()->AddPackage( - _T("foo.exe"), 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T("foo.exe"), 100, _T("sha256hash")); // TODO(omaha): We should be able to eliminate this. SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_); @@ -546,8 +533,7 @@ TEST_F(InstallManagerInstallAppUserTest, kRegValueProductVersion, _T("0.10.69.5"))); - app_->next_version()->AddPackage( - kCmdExecutable, 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(kCmdExecutable, 100, _T("sha256hash")); EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Exe App")))); SetArgumentsInManifest(arguments, _T("0.10.69.5"), app_); @@ -586,8 +572,7 @@ TEST_F(InstallManagerInstallAppUserTest, kRegValueProductVersion, _T("0.10.69.5"))); - app_->next_version()->AddPackage( - kCmdExecutable, 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(kCmdExecutable, 100, _T("sha256hash")); SetArgumentsInManifest(arguments, _T("0.10.69.5"), app_); @@ -641,9 +626,9 @@ TEST_F(InstallManagerInstallAppMachineTest, InstallApp_MsiInstallerSucceeds) { EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); // TODO(omaha): This should be just a filename. - app_->next_version()->AddPackage( - kSetupFooV1RelativeLocation, - 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(kSetupFooV1RelativeLocation, + 100, + _T("sha256hash")); app_->set_app_guid(StringToGuid(kFooId)); EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Foo")))); EXPECT_SUCCEEDED(app_->put_iid(CComBSTR(kIid))); @@ -728,9 +713,9 @@ TEST_F(InstallManagerInstallAppMachineTest, EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); // TODO(omaha): This should be just a filename. - app_->next_version()->AddPackage( - kSetupFooV1RelativeLocation, - 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(kSetupFooV1RelativeLocation, + 100, + _T("sha256hash")); app_->set_app_guid(StringToGuid(kFooId)); EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Foo")))); @@ -782,9 +767,9 @@ TEST_F(InstallManagerInstallAppMachineTest, TEST_F(InstallManagerInstallAppUserTest, InstallApp_UpdateOmahaSucceeds) { const CString kExistingVersion(_T("0.9.69.5")); - app_->next_version()->AddPackage( - _T("SaveArguments.exe"), - 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T("SaveArguments.exe"), + 100, + _T("sha256hash")); app_->set_app_guid(StringToGuid(kGoogleUpdateAppId)); // TODO(omaha3): This isn't supported yet. @@ -827,9 +812,9 @@ TEST_F(InstallManagerInstallAppUserTest, InstallApp_UpdateOmahaSucceedsWhenClientsKeyAbsent) { const CString kExistingVersion(_T("0.9.69.5")); - app_->next_version()->AddPackage( - _T("SaveArguments.exe"), - 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T("SaveArguments.exe"), + 100, + _T("sha256hash")); app_->set_app_guid(StringToGuid(kGoogleUpdateAppId)); // TODO(omaha3): This isn't supported yet. @@ -859,8 +844,7 @@ TEST_F(InstallManagerInstallAppUserTest, CString arguments; arguments.Format(kExecuteCommandAndTerminateSwitch, _T("")); - app_->next_version()->AddPackage( - kCmdExecutable, 100, CreateFileHash(_T("hash"), _T(""))); + app_->next_version()->AddPackage(kCmdExecutable, 100, _T("sha256hash")); EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Some App")))); SetArgumentsInManifest(arguments, _T("5.6.7.8"), app_); @@ -898,8 +882,7 @@ TEST_F(InstallManagerInstallAppUserTest, ASSERT_SUCCEEDED(File::Remove(log_path)); ASSERT_FALSE(File::Exists(log_path)); - app_->next_version()->AddPackage( - _T("foo.msi"), 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(_T("foo.msi"), 100, _T("sha256hash")); // TODO(omaha): We should be able to eliminate this. SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_); @@ -942,8 +925,7 @@ TEST_F(InstallManagerInstallAppUserTest, InstallApp_MsiIsBusy_NoRetries) { CString arguments; arguments.Format(kExecuteCommandAndTerminateSwitch, commands); - app_->next_version()->AddPackage( - kCmdExecutable, 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(kCmdExecutable, 100, _T("sha256hash")); EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Some App")))); SetArgumentsInManifest(arguments, _T("1.2.3.4"), app_); @@ -996,8 +978,7 @@ TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallMultipleApps) { kRegValueProductVersion, _T("0.10.69.5"))); - app_->next_version()->AddPackage( - kCmdExecutable, 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app_->next_version()->AddPackage(kCmdExecutable, 100, _T("sha256hash")); EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Exe App")))); SetArgumentsInManifest(arguments1, _T("0.10.69.5"), app_); @@ -1028,8 +1009,7 @@ TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallMultipleApps) { kRegValueProductVersion, _T("0.10.69.5"))); - app2->next_version()->AddPackage( - kCmdExecutable, 100, CreateFileHash(_T("hash"), _T("sha256hash"))); + app2->next_version()->AddPackage(kCmdExecutable, 100, _T("sha256hash")); EXPECT_SUCCEEDED(app2->put_displayName(CComBSTR(_T("Exe App")))); SetArgumentsInManifest(arguments2, _T("0.10.69.5"), app2); @@ -1272,6 +1252,8 @@ TEST_P(InstallManagerTest, InstallDir_ReadOnlyFiles) { FakeGLock fake_glock; InstallManager install_manager(&fake_glock, IsMachine()); + ::Sleep(10); + EXPECT_TRUE(File::Exists(install_dir)); EXPECT_TRUE(::PathIsDirectoryEmpty(install_dir)); diff --git a/omaha/goopdate/installer_wrapper.cc b/omaha/goopdate/installer_wrapper.cc index fa4f41f84..d65cb3c9c 100644 --- a/omaha/goopdate/installer_wrapper.cc +++ b/omaha/goopdate/installer_wrapper.cc @@ -13,7 +13,10 @@ // limitations under the License. // ======================================================================== +#include + #include + #include "goopdate/omaha3_idl.h" #include "omaha/goopdate/installer_wrapper.h" #include "omaha/base/const_object_names.h" @@ -110,14 +113,14 @@ void GetSystemErrorString(uint32 error_code, const CString error_message(GetMessageForSystemErrorCode(error_code)); if (!error_message.IsEmpty()) { - VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string, + VERIFY_SUCCEEDED(formatter.FormatMessage(error_string, IDS_INSTALLER_FAILED_WITH_MESSAGE, error_code_string, - error_message))); + error_message)); } else { - VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string, + VERIFY_SUCCEEDED(formatter.FormatMessage(error_string, IDS_INSTALLER_FAILED_NO_MESSAGE, - error_code_string))); + error_code_string)); } OPT_LOG(LEVEL_ERROR, (_T("[installer system error][%u][%s]"), @@ -204,40 +207,40 @@ CString InstallerWrapper::GetMessageForError(HRESULT error_code, switch (error_code) { case GOOPDATEINSTALL_E_FILENAME_INVALID: - VERIFY1(SUCCEEDED(formatter.FormatMessage(&message, + VERIFY_SUCCEEDED(formatter.FormatMessage(&message, IDS_INVALID_INSTALLER_FILENAME, - installer_filename))); + installer_filename)); break; case GOOPDATEINSTALL_E_INSTALLER_FAILED_START: - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_FAILED_TO_START, - &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALLER_FAILED_TO_START, + &message)); break; case GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT: - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_TIMED_OUT, - &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALLER_TIMED_OUT, + &message)); break; case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY: case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION: case GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH: - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); break; case GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING: - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_MSI_INSTALL_ALREADY_RUNNING, - &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_MSI_INSTALL_ALREADY_RUNNING, + &message)); break; case GOOPDATEINSTALL_E_INSTALLER_FAILED: ASSERT(false, (_T("[GetOmahaErrorTextToReport]") _T("GOOPDATEINSTALL_E_INSTALLER_FAILED should never be reported ") _T("directly. The installer error string should be reported."))); - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); break; case GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR: default: ASSERT(false, (_T("[GetOmahaErrorTextToReport]") _T("[An Omaha error occurred that this method does not ") _T("know how to report.][0x%08x]"), error_code)); - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)); break; } @@ -272,9 +275,17 @@ HRESULT InstallerWrapper::BuildCommandLineFromFilename( // created, so Omaha does not delete it. CString enclosed_installer_data_file_path; - VERIFY1(SUCCEEDED(goopdate_utils::WriteInstallerDataToTempFile( + // We use the App Installer's directory for the InstallerData file. + CPath app_installer_directory(file_path); + if (!app_installer_directory.RemoveFileSpec()) { + OPT_LOG(LE, (_T("[Does not appear to be a filename '%s']"), file_path)); + return GOOPDATEINSTALL_E_FILENAME_INVALID; + } + + VERIFY_SUCCEEDED(goopdate_utils::WriteInstallerDataToTempFile( + app_installer_directory, installer_data, - &enclosed_installer_data_file_path))); + &enclosed_installer_data_file_path)); if (!enclosed_installer_data_file_path.IsEmpty()) { EnclosePath(&enclosed_installer_data_file_path); } diff --git a/omaha/goopdate/installer_wrapper_unittest.cc b/omaha/goopdate/installer_wrapper_unittest.cc index 919eaddd1..dd2e3ea90 100644 --- a/omaha/goopdate/installer_wrapper_unittest.cc +++ b/omaha/goopdate/installer_wrapper_unittest.cc @@ -47,16 +47,16 @@ const GUID kAppGuid = {0xB18BC01B, 0xE0BD, 0x4BF0, {0xA3, 0x3E, 0x11, 0x33, 0x05, 0x5E, 0x5F, 0xDE}}; const TCHAR kFullAppClientsKeyPath[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}"); const TCHAR kFullAppClientStateKeyPath[] = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}"); const TCHAR kFullFooAppClientKeyPath[] = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{D6B08267-B440-4C85-9F79-E195E80D9937}"); const TCHAR kFullFooAppClientStateKeyPath[] = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{D6B08267-B440-4C85-9F79-E195E80D9937}"); const TCHAR kSetupFooV1RelativeLocation[] = @@ -125,8 +125,8 @@ int GetNumMsiTries() { extern const TCHAR kRegExecutable[] = _T("reg.exe"); extern const TCHAR kSetInstallerResultTypeMsiErrorRegCmdArgs[] = - _T("add HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE} ") + _T("add \"HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}\" ") _T("/v InstallerResult /t REG_DWORD /d 2 /f"); extern const TCHAR kMsiInstallerBusyExitCodeCmd[] = _T("exit 1618"); @@ -711,7 +711,7 @@ TEST_F(InstallerWrapperUserTest, EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action); } -TEST_F(InstallerWrapperUserTest, InstallApp_InstallerEmtpyFilename) { +TEST_F(InstallerWrapperUserTest, InstallApp_InstallerEmptyFilename) { EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, iw_->InstallApp(NULL, kAppGuid, diff --git a/omaha/goopdate/job_observer.cc b/omaha/goopdate/job_observer.cc index 72b4403ba..054953bcf 100644 --- a/omaha/goopdate/job_observer.cc +++ b/omaha/goopdate/job_observer.cc @@ -141,7 +141,7 @@ void JobObserverCOMDecorator::OnComplete(const ObserverCompletionInfo& info) { job_observer_ = NULL; } - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (on_demand_events) { on_demand_events->DoExit(); SetEventSink(NULL); @@ -160,7 +160,7 @@ void JobObserverCOMDecorator::SetEventSink( // TODO(omaha): Need to add a DoPause() to OnDemandEventsInterface. We never // expect these to be used since Chrome never did. STDMETHODIMP JobObserverCOMDecorator::DoPause() { - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (!on_demand_events) { return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL; } @@ -170,7 +170,7 @@ STDMETHODIMP JobObserverCOMDecorator::DoPause() { // TODO(omaha): Need to add a DoResume() to OnDemandEventsInterface. STDMETHODIMP JobObserverCOMDecorator::DoResume() { - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (!on_demand_events) { return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL; } @@ -179,7 +179,7 @@ STDMETHODIMP JobObserverCOMDecorator::DoResume() { } STDMETHODIMP JobObserverCOMDecorator::DoClose() { - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (!on_demand_events) { return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL; } @@ -191,7 +191,7 @@ STDMETHODIMP JobObserverCOMDecorator::DoClose() { // TODO(omaha): Reconcile IJobObserver::DoRestartBrowsers() with // OnDemandEventsInterface::DoRestartBrowser(). STDMETHODIMP JobObserverCOMDecorator::DoRestartBrowsers() { - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (!on_demand_events) { return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL; } @@ -200,7 +200,7 @@ STDMETHODIMP JobObserverCOMDecorator::DoRestartBrowsers() { } STDMETHODIMP JobObserverCOMDecorator::DoReboot() { - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (!on_demand_events) { return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL; } @@ -210,7 +210,7 @@ STDMETHODIMP JobObserverCOMDecorator::DoReboot() { STDMETHODIMP JobObserverCOMDecorator::DoLaunchBrowser(const WCHAR* url) { UNREFERENCED_PARAMETER(url); - OnDemandEventsInterface* on_demand_events(on_demand_events()); + OnDemandEventsInterface* on_demand_events = GetOnDemandEvents(); if (!on_demand_events) { return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL; } diff --git a/omaha/goopdate/job_observer.h b/omaha/goopdate/job_observer.h index d2d04dffd..cb35df29f 100644 --- a/omaha/goopdate/job_observer.h +++ b/omaha/goopdate/job_observer.h @@ -78,7 +78,7 @@ class JobObserverCOMDecorator STDMETHOD(DoLaunchBrowser)(const WCHAR* url); private: - OnDemandEventsInterface* on_demand_events() { + OnDemandEventsInterface* GetOnDemandEvents() { return on_demand_events_; } diff --git a/omaha/goopdate/non_localized_resource.h b/omaha/goopdate/non_localized_resource.h index 3bcec6b2a..e47dfb046 100644 --- a/omaha/goopdate/non_localized_resource.h +++ b/omaha/goopdate/non_localized_resource.h @@ -23,6 +23,4 @@ #define IDR_LOCAL_SERVICE_RGS 2008 #define IDR_GOOGLE_UPDATE3_SERVICE_APPID 2009 -#define IDB_CHROME 2100 - #endif // OMAHA_GOOPDATE_NON_LOCALIZED_RESOURCE_H_ diff --git a/omaha/goopdate/omaha3_idl.idl b/omaha/goopdate/omaha3_idl.idl index 53b22c205..f750fa998 100644 --- a/omaha/goopdate/omaha3_idl.idl +++ b/omaha/goopdate/omaha3_idl.idl @@ -520,6 +520,229 @@ interface ICredentialDialog : IUnknown { [out] BSTR* password); }; +// This is a legacy interface that could be removed in future versions. Do not +// use. +[ + object, + dual, + uuid(BFA9CB0F-987A-4E8A-A3BE-5988F315F35E), + helpstring("IPolicyStatus Interface"), + pointer_default(unique) +] +interface IPolicyStatus : IDispatch { + // Global Update Policies + + // Returns the time interval between update checks in minutes. + // 0 indicates updates are disabled. + [propget] HRESULT lastCheckPeriodMinutes([out, retval] DWORD* minutes); + + // For domain-joined machines, returns the suppressed times if any, and also + // checks the current time against the times that updates are suppressed. + // Updates are suppressed if the current time falls between the start time and + // the duration. + // The duration does not account for daylight savings time. For instance, if + // the start time is 22:00 hours, and with a duration of 8 hours, the updates + // will be suppressed for 8 hours regardless of whether daylight savings time + // changes happen in between. + [propget] HRESULT updatesSuppressedTimes( + [out] DWORD* start_hour, + [out] DWORD* start_min, + [out] DWORD* duration_min, + [out] VARIANT_BOOL* are_updates_suppressed); + + // Returns the value of the "DownloadPreference" group policy or an + // empty string if the group policy does not exist, the policy is unknown, or + // an error happened. + [propget] HRESULT downloadPreferenceGroupPolicy([out, retval] BSTR* pref); + + // Gets the total disk size limit for cached packages. When this limit is hit, + // packages may be deleted from oldest until total size is below the limit. + [propget] HRESULT packageCacheSizeLimitMBytes([out, retval] DWORD* limit); + + // Gets the package cache life limit. If a cached package is older than this + // limit, it may be deleted. + [propget] HRESULT packageCacheExpirationTimeDays([out, retval] DWORD* days); + + // Application Update Policies + + // Returns 1 if installation of the specified app is allowed. + // Otherwise, returns 0. + [propget] HRESULT effectivePolicyForAppInstalls([in] BSTR app_id, + [out, retval] DWORD* policy); + + // Returns 1 if updates of the specified app is allowed. + // Otherwise, returns one of 0 (Disabled), 2 (ManualUpdatesOnly), or + // 3 (AutomaticUpdatesOnly). + [propget] HRESULT effectivePolicyForAppUpdates([in] BSTR app_id, + [out, retval] DWORD* policy); + + // Returns the target version prefix for the app, if the machine is joined to + // a domain and has the corresponding policy set. + // Examples: + // * "" (or not configured): update to latest version available. + // * "55.": update to any minor version of 55 (e.g. 55.24.34 or 55.60.2). + // * "55.2.": update to any minor version of 55.2 (e.g. 55.2.34 or 55.2.2). + // * "55.24.34": update to this specific version only. + [propget] HRESULT targetVersionPrefix([in] BSTR app_id, + [out, retval] BSTR* prefix); + + // Returns whether the RollbackToTargetVersion policy has been set for the + // app. Setting RollbackToTargetVersion will result in a version downgrade if + // the app version on the client is higher than the version on the server. + // This could happen under circumstances such as: + // - TargetVersionPrefix is used to pick an older version on the channel. + // - TargetChannel is used to move the client to a channel with a lower + // version (e.g., Dev/Beta to Beta/Stable). + // - A user somehow installed a newer version on the client. + // When not set, a client will not receive updates until the app version on + // the server passes the version on the client. + [propget] HRESULT isRollbackToTargetVersionAllowed( + [in] BSTR app_id, + [out, retval] VARIANT_BOOL* rollback_allowed); +}; + +// IPolicyStatusValue represents the managed state of a single Google Update +// policy. It contains the current source and value, as well as if any conflicts +// exist with that policy. +[ + object, + dual, + uuid(EF69FA43-6F9C-42E2-B6C0-A4FDC525EB07), + helpstring("IPolicyStatusValue Interface"), + pointer_default(unique) +] +interface IPolicyStatusValue : IDispatch { + [propget] HRESULT source([out, retval] BSTR*); + [propget] HRESULT value([out, retval] BSTR*); + [propget] HRESULT hasConflict([out, retval] VARIANT_BOOL* has_conflict); + [propget] HRESULT conflictSource([out, retval] BSTR*); + [propget] HRESULT conflictValue([out, retval] BSTR*); +} + +// IPolicyStatus2 exposes the following: +// * properties for Google Update that includes Global Update state, such as the +// Version of the Updater, the Time that Updates were checked for last. +// * A way to refresh the latest policies from the DM Server. +// * the managed state of Omaha policies. Each policy returns an +// IPolicyStatusValue that can be queried for the current source and value, as +// well as if any conflicts exist with that policy. +// IPolicyStatusValue is implemented by an object that marshals itself by +// value. To get the "current" value, the policy needs to be queried fresh. +[ + object, + dual, + uuid(7C0204A9-287C-478E-94DC-0337E0241CFB), + helpstring("IPolicyStatus2 Interface"), + pointer_default(unique) +] +interface IPolicyStatus2 : IDispatch { + // Global Update Status. + + // Returns the running version of the Updater. For instance, 1.3.35.454. + [propget] HRESULT updaterVersion([out, retval] BSTR* version); + + // Returns the last time that the Updater successfully checked for updates. + [propget] HRESULT lastCheckedTime([out, retval] DATE* last_checked); + + // DM policy cache refresh. + + // Gets the latest policies from the DM Server. + HRESULT refreshPolicies(); + + // Global Update Policies + + // Returns the time interval between update checks in minutes. + // 0 indicates updates are disabled. + [propget] HRESULT lastCheckPeriodMinutes( + [out, retval] IPolicyStatusValue** value); + + // For domain-joined machines, returns the suppressed times if any, and also + // checks the current time against the times that updates are suppressed. + // Updates are suppressed if the current time falls between the start time and + // the duration. + // The duration does not account for daylight savings time. For instance, if + // the start time is 22:00 hours, and with a duration of 8 hours, the updates + // will be suppressed for 8 hours regardless of whether daylight savings time + // changes happen in between. + [propget] HRESULT updatesSuppressedTimes( + [out] IPolicyStatusValue** value, + VARIANT_BOOL* are_updates_suppressed); + + // Returns the value of the "DownloadPreference" group policy or an + // empty string if the group policy does not exist, the policy is unknown, or + // an error happened. + [propget] HRESULT downloadPreferenceGroupPolicy( + [out, retval] IPolicyStatusValue** value); + + // Gets the total disk size limit for cached packages. When this limit is hit, + // packages may be deleted from oldest until total size is below the limit. + [propget] HRESULT packageCacheSizeLimitMBytes( + [out, retval] IPolicyStatusValue** value); + + // Gets the package cache life limit. If a cached package is older than this + // limit, it may be deleted. + [propget] HRESULT packageCacheExpirationTimeDays( + [out, retval] IPolicyStatusValue** value); + + // Gets the proxy policy values. + [propget] HRESULT proxyMode([out, retval] IPolicyStatusValue** value); + [propget] HRESULT proxyPacUrl([out, retval] IPolicyStatusValue** value); + [propget] HRESULT proxyServer([out, retval] IPolicyStatusValue** value); + + // Application Update Policies + + // Returns 1 if installation of the specified app is allowed. + // Otherwise, returns 0. + [propget] HRESULT effectivePolicyForAppInstalls( + [in] BSTR app_id, + [out, retval] IPolicyStatusValue** value); + + // Returns 1 if updates of the specified app is allowed. + // Otherwise, returns one of 0 (Disabled), 2 (ManualUpdatesOnly), or + // 3 (AutomaticUpdatesOnly). + [propget] HRESULT effectivePolicyForAppUpdates( + [in] BSTR app_id, + [out, retval] IPolicyStatusValue** value); + + // Returns the target version prefix for the app, if the machine is joined to + // a domain and has the corresponding policy set. + // Examples: + // * "" (or not configured): update to latest version available. + // * "55.": update to any minor version of 55 (e.g. 55.24.34 or 55.60.2). + // * "55.2.": update to any minor version of 55.2 (e.g. 55.2.34 or 55.2.2). + // * "55.24.34": update to this specific version only. + [propget] HRESULT targetVersionPrefix( + [in] BSTR app_id, + [out, retval] IPolicyStatusValue** value); + + // Returns whether the RollbackToTargetVersion policy has been set for the + // app. If RollbackToTargetVersion is set, the TargetVersionPrefix policy + // governs the version to rollback clients with higher versions to. + [propget] HRESULT isRollbackToTargetVersionAllowed( + [in] BSTR app_id, [out, retval] IPolicyStatusValue** value); + + // Returns the target channel for the app, if the machine is joined to a + // domain and has the corresponding policy set. + [propget] HRESULT targetChannel([in] BSTR app_id, + [out, retval] IPolicyStatusValue** value); +}; + +// IPolicyStatus3 exposes everything IPolicyStatus2 does, and in addition, +// exposes the forceInstallApps policy. +[ + object, + dual, + uuid(60355531-5BFD-45AB-942C-7912628752C7), + helpstring("IPolicyStatus3 Interface"), + pointer_default(unique) +] +interface IPolicyStatus3 : IPolicyStatus2 { + // Global Update Policies + + [propget] HRESULT forceInstallApps([in] VARIANT_BOOL is_machine, + [out, retval] IPolicyStatusValue** value); +}; + // BEGIN gupdatem interfaces. // The following interfaces are exposed as a narrower version of the @@ -910,6 +1133,10 @@ library GoogleUpdate3Lib { interface IPackage; interface ICurrentState; + interface IPolicyStatus; + interface IPolicyStatus2; + interface IPolicyStatus3; + interface IPolicyStatusValue; interface IGoogleUpdate3Web; interface IAppBundleWeb; interface IAppWeb; @@ -1005,6 +1232,54 @@ library GoogleUpdate3Lib { [default] interface IUnknown; } + [ + uuid(D7EE9B13-6F14-4182-9505-DEDC3EE3F3FC), + helpstring("PolicyStatusValueUserClass") + ] + coclass PolicyStatusValueUserClass { + [default] interface IUnknown; + } + + [ + uuid(FBFA21E1-BBB8-46B3-95EB-791E29BA42F3), + helpstring("PolicyStatusValueMachineClass") + ] + coclass PolicyStatusValueMachineClass { + [default] interface IUnknown; + } + + [ + uuid(2F08D2B0-EFA6-46A2-84DD-1245F752CBF1), + helpstring("Policy Status for per-user applications.") + ] + coclass PolicyStatusUserClass { + [default] interface IUnknown; + } + + [ + uuid(B9AEB1CC-DF9B-45CB-B70B-084D2E869A1C), + helpstring("Policy Status pass-through broker for machine applications.") + ] + coclass PolicyStatusMachineClass { + [default] interface IUnknown; + } + + [ + uuid(F1ED0E3A-971D-4DFD-ACF2-BAC8BA03E5D7), + helpstring("Policy Status for per-machine applications.") + ] + coclass PolicyStatusMachineServiceClass { + [default] interface IUnknown; + } + + [ + uuid(B0F9B7EB-997F-47B9-AC2F-9718EDD705D9), + helpstring("Fallback for if PolicyStatusMachineServiceClass fails.") + ] + coclass PolicyStatusMachineFallbackClass { + [default] interface IUnknown; + } + [ uuid(___AUTO_GENERATED_GUID___), helpstring("GoogleComProxyMachineClass") diff --git a/omaha/goopdate/omaha3_idl_datax.c b/omaha/goopdate/omaha3_idl_datax.c index aaa237f66..5474b2d1b 100644 --- a/omaha/goopdate/omaha3_idl_datax.c +++ b/omaha/goopdate/omaha3_idl_datax.c @@ -18,7 +18,8 @@ #pragma warning(push) // C4152: nonstandard extension, function/data pointer conversion in expression -#pragma warning(disable : 4152) +// C2251: code_seg changed after including header +#pragma warning(disable : 4152 5251) #define REGISTER_PROXY_DLL #define USE_STUBLESS_PROXY diff --git a/omaha/goopdate/omaha_customization_goopdate_apis_unittest.cc b/omaha/goopdate/omaha_customization_goopdate_apis_unittest.cc index d6db836a7..5ede77563 100644 --- a/omaha/goopdate/omaha_customization_goopdate_apis_unittest.cc +++ b/omaha/goopdate/omaha_customization_goopdate_apis_unittest.cc @@ -27,24 +27,6 @@ #include "goopdate/omaha3_idl.h" #include "omaha/testing/omaha_customization_test.h" -// TODO(omaha): Add tests for to detect interface changes that would require -// rolling _OMAHA3_IDL_PROXY_CLSID_IS. These include: -// 1) interface changes invovlving the number or signature of methods -// 2) or that new interfaces have been added -// For #2, we already have the InvalidIndex test for interfaces in the TypeLib, -// so we just need to add checks for interfaces not in the TypeLib. -// -// ITypeLib and ITypeInfo methods might be useful. See: -// http://msdn.microsoft.com/en-us/library/aa912648.aspx -// http://msdn.microsoft.com/en-us/library/aa909031.aspx -// -// I do not know how to get information about interfaces not in a TypeLib. -// Fortunately, most Omaha 3 interfaces are in one. -// -// If we can not get all the information we need, we can always save a "golden" -// idl.h file and diff against it. - - // Most of the tests are intentionally not using the omaha namespace. Most of // the values being tested are not in this namespace, and being in the global // namespace is required by TEST_GU_INT_F to catch conflicts with Google types @@ -86,6 +68,13 @@ TEST(OmahaCustomizationTest, Constants_ComProgIds) { EXPECT_GU_STREQ(_T("GoogleUpdate.CoreClass"), kProgIDGoogleUpdateCoreService); EXPECT_GU_STREQ(_T("GoogleUpdate.ProcessLauncher"), kProgIDProcessLauncher); + + EXPECT_GU_STREQ(_T("GoogleUpdate.PolicyStatusUser"), kProgIDPolicyStatusUser); + EXPECT_GU_STREQ(_T("GoogleUpdate.PolicyStatusMachine"), + kProgIDPolicyStatusMachine); + EXPECT_GU_STREQ(_T("GoogleUpdate.PolicyStatusMachineFallback"), + kProgIDPolicyStatusMachineFallback); + EXPECT_GU_STREQ(_T("GoogleUpdate.PolicyStatusSvc"), kProgIDPolicyStatusSvc); } } // namespace omaha @@ -286,6 +275,46 @@ TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, ICredentialDialog) { __uuidof(ICredentialDialog)); } +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IPolicyStatus) { + EXPECT_GU_ID_EQ(_T("{F63F6F8B-ACD5-413C-A44B-0409136D26CB}"), + __uuidof(IPolicyStatus)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("IPolicyStatus"))); + EXPECT_STREQ(_T("IPolicyStatus Interface"), item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IPolicyStatusValue) { + EXPECT_GU_ID_EQ(_T("{27634814-8E41-4C35-8577-980134A96544}"), + __uuidof(IPolicyStatusValue)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("IPolicyStatusValue"))); + EXPECT_STREQ(_T("IPolicyStatusValue Interface"), item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IPolicyStatus2) { + EXPECT_GU_ID_EQ(_T("{34527502-D3DB-4205-A69B-789B27EE0414}"), + __uuidof(IPolicyStatus2)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("IPolicyStatus2"))); + EXPECT_STREQ(_T("IPolicyStatus2 Interface"), item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IPolicyStatus3) { + EXPECT_GU_ID_EQ(_T("{05A30352-EB25-45B6-8449-BCA7B0542CE5}"), + __uuidof(IPolicyStatus3)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("IPolicyStatus3"))); + EXPECT_STREQ(_T("IPolicyStatus3 Interface"), item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IGoogleUpdate3Web) { EXPECT_GU_ID_EQ(_T("{494B20CF-282E-4BDD-9F5D-B70CB09D351E}"), __uuidof(IGoogleUpdate3Web)); @@ -481,6 +510,77 @@ TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, EXPECT_TRUE(!help_file_); } +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, + PolicyStatusValueUserClass) { + EXPECT_GU_ID_EQ(_T("{85D8EE2F-794F-41F0-BB03-49D56A23BEF4}"), + __uuidof(PolicyStatusValueUserClass)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("PolicyStatusValueUserClass"))); + EXPECT_STREQ(_T("PolicyStatusValueUserClass"), item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, + PolicyStatusValueMachineClass) { + EXPECT_GU_ID_EQ(_T("{C6271107-A214-4F11-98C0-3F16BC670D28}"), + __uuidof(PolicyStatusValueMachineClass)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("PolicyStatusValueMachineClass"))); + EXPECT_STREQ(_T("PolicyStatusValueMachineClass"), item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, + PolicyStatusUserClass) { + EXPECT_GU_ID_EQ(_T("{6DDCE70D-A4AE-4E97-908C-BE7B2DB750AD}"), + __uuidof(PolicyStatusUserClass)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("PolicyStatusUserClass"))); + EXPECT_STREQ(_T("Policy Status for per-user applications."), + item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, + PolicyStatusMachineClass) { + EXPECT_GU_ID_EQ(_T("{521FDB42-7130-4806-822A-FC5163FAD983}"), + __uuidof(PolicyStatusMachineClass)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("PolicyStatusMachineClass"))); + EXPECT_STREQ(_T("Policy Status pass-through broker ") + _T("for machine applications."), + item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, + PolicyStatusMachineServiceClass) { + EXPECT_GU_ID_EQ(_T("{1C4CDEFF-756A-4804-9E77-3E8EB9361016}"), + __uuidof(PolicyStatusMachineServiceClass)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("PolicyStatusMachineServiceClass"))); + EXPECT_STREQ(_T("Policy Status for per-machine applications."), + item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + +TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, + PolicyStatusMachineFallbackClass) { + EXPECT_GU_ID_EQ(_T("{ADDF22CF-3E9B-4CD7-9139-8169EA6636E4}"), + __uuidof(PolicyStatusMachineFallbackClass)); + + EXPECT_SUCCEEDED(GetDocumentation(_T("PolicyStatusMachineFallbackClass"))); + EXPECT_STREQ(_T("Fallback for if PolicyStatusMachineServiceClass fails."), + item_doc_string_); + EXPECT_EQ(0, help_context_); + EXPECT_TRUE(!help_file_); +} + TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, GoogleComProxyMachineClass) { EXPECT_SUCCEEDED(GetDocumentation(_T("GoogleComProxyMachineClass"))); @@ -580,7 +680,7 @@ TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, // Verifies there are no new interfaces in the TypeLib. TEST_F(OmahaCustomizationGoopdateComInterfaceTest, VerifyNoNewInterfaces) { - EXPECT_EQ(36, type_lib_->GetTypeInfoCount()) + EXPECT_EQ(46, type_lib_->GetTypeInfoCount()) << _T("A new interface may have been added. If so, add the interface to ") << _T("to kIIDsToRegister, and add test(s) for new interface(s)."); } @@ -626,4 +726,3 @@ TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, EXPECT_GU_ID_EQ(_T("{909489C2-85A6-4322-AA56-D25278649D67}"), __uuidof(IGoogleUpdateCore)); } - diff --git a/omaha/goopdate/ondemand.h b/omaha/goopdate/ondemand.h index 7c034b189..677546ceb 100644 --- a/omaha/goopdate/ondemand.h +++ b/omaha/goopdate/ondemand.h @@ -31,6 +31,7 @@ #include "omaha/base/scope_guard.h" #include "omaha/base/synchronized.h" #include "omaha/base/thread_pool_callback.h" +#include "omaha/base/user_rights.h" #include "omaha/base/utils.h" #include "omaha/common/const_goopdate.h" #include "omaha/goopdate/com_proxy.h" @@ -51,19 +52,16 @@ struct OnDemandParameters { bool is_check_only, const CString& sess_id, HANDLE caller_impersonation_token, - HANDLE caller_primary_token, - Gate* gate) + HANDLE caller_primary_token) : app_id(guid), job_observer_git_cookie(job_observer_cookie), is_update_check_only(is_check_only), session_id(sess_id), impersonation_token(caller_impersonation_token), - primary_token(caller_primary_token), - on_demand_gate(gate) { + primary_token(caller_primary_token) { ASSERT1(guid.GetLength() > 0); ASSERT1(IsGuid(session_id)); ASSERT1(job_observer_cookie); - ASSERT1(gate); } CString app_id; @@ -72,7 +70,6 @@ struct OnDemandParameters { CString session_id; HANDLE impersonation_token; HANDLE primary_token; - Gate* on_demand_gate; }; HRESULT DoOnDemand(bool is_machine, @@ -168,41 +165,44 @@ class ATL_NO_VTABLE OnDemand } if (session_id_.IsEmpty()) { - VERIFY1(SUCCEEDED(GetGuid(&session_id_))); + VERIFY_SUCCEEDED(GetGuid(&session_id_)); } + // We Lock the ATL Module here since we want the process to stick around + // until the newly created threadpool item below starts and also completes + // execution. The corresponding Unlock of the ATL Module is done at the end + // of the threadpool proc. + _pAtlModule->Lock(); + ScopeGuard atl_module_unlock = MakeObjGuard(*_pAtlModule, + &CAtlModule::Unlock); + // Create a thread pool work item for deferred execution of the on demand // check. The thread pool owns this call back object. The thread owns the // impersonation and primary tokens. - typedef StaticThreadPoolCallBack1 Callback; - Gate on_demand_gate; - std::unique_ptr callback( - new Callback(&OnDemand::DoOnDemandInternal, - internal::OnDemandParameters( - guid, - job_observer_git.Detach(), - is_update_check_only, - session_id_, - dup_impersonation_token.GetHandle(), - dup_primary_token.GetHandle(), - &on_demand_gate))); - - hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), - COINIT_APARTMENTTHREADED, - WT_EXECUTELONGFUNCTION); + using Callback = StaticThreadPoolCallBack1; + hr = Goopdate::Instance().QueueUserWorkItem( + std::make_unique( + &OnDemand::DoOnDemandInternal, + internal::OnDemandParameters( + guid, + job_observer_git.Detach(), + is_update_check_only, + session_id_, + dup_impersonation_token.GetHandle(), + dup_primary_token.GetHandle())), + COINIT_APARTMENTTHREADED, + WT_EXECUTELONGFUNCTION); if (FAILED(hr)) { CORE_LOG(LE, (_T("[QueueUserWorkItem failed][0x%x]"), hr)); return hr; } + atl_module_unlock.Dismiss(); if (T::is_machine()) { dup_impersonation_token.Detach(); dup_primary_token.Detach(); } - callback.release(); - VERIFY1(on_demand_gate.Wait(INFINITE)); - return S_OK; } @@ -210,10 +210,8 @@ class ATL_NO_VTABLE OnDemand internal::OnDemandParameters on_demand_params) { CORE_LOG(L2, (_T("[DoOnDemandInternal][%d]"), on_demand_params.is_update_check_only)); - _pAtlModule->Lock(); - ON_SCOPE_EXIT_OBJ(*_pAtlModule, &CAtlModule::Unlock); - VERIFY1(on_demand_params.on_demand_gate->Open()); + ON_SCOPE_EXIT_OBJ(*_pAtlModule, &CAtlModule::Unlock); scoped_handle impersonation_token(on_demand_params.impersonation_token); scoped_handle primary_token(on_demand_params.primary_token); @@ -255,26 +253,26 @@ class ATL_NO_VTABLE OnDemand struct OnDemandModeUser { static bool is_machine() { return false; } - static const TCHAR* const prog_id() { return kProgIDOnDemandUser; } + static const TCHAR* prog_id() { return kProgIDOnDemandUser; } static GUID class_id() { return __uuidof(OnDemandUserAppsClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; } - static const TCHAR* const hk_root() { return _T("HKCU"); } + static const TCHAR* hk_root() { return _T("HKCU"); } }; struct OnDemandModeMachineFallback { static bool is_machine() { return true; } - static const TCHAR* const prog_id() { return kProgIDOnDemandMachineFallback; } + static const TCHAR* prog_id() { return kProgIDOnDemandMachineFallback; } static GUID class_id() { return __uuidof(OnDemandMachineAppsFallbackClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVER_ELEVATION_RGS; } - static const TCHAR* const hk_root() { return _T("HKLM"); } + static const TCHAR* hk_root() { return _T("HKLM"); } }; struct OnDemandModeService { static bool is_machine() { return true; } - static const TCHAR* const prog_id() { return kProgIDOnDemandSvc; } + static const TCHAR* prog_id() { return kProgIDOnDemandSvc; } static GUID class_id() { return __uuidof(OnDemandMachineAppsServiceClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVICE_RGS; } - static const TCHAR* const hk_root() { return _T("HKLM"); } + static const TCHAR* hk_root() { return _T("HKLM"); } }; typedef OnDemand OnDemandUser; diff --git a/omaha/goopdate/package.cc b/omaha/goopdate/package.cc index 5a504a537..c1ded7c9d 100644 --- a/omaha/goopdate/package.cc +++ b/omaha/goopdate/package.cc @@ -122,12 +122,12 @@ void Package::OnRequestRetryScheduled(time64 next_download_retry_time) { void Package::SetFileInfo(const CString& filename, uint64 size, - const FileHash& expected_hash) { + const CString& expected_hash) { __mutexScope(model()->lock()); ASSERT1(!filename.IsEmpty()); ASSERT1(0 < size); - ASSERT1(!expected_hash.sha256.IsEmpty() ||!expected_hash.sha1.IsEmpty()); + ASSERT1(!expected_hash.IsEmpty()); filename_ = filename; expected_size_ = size; @@ -145,9 +145,9 @@ uint64 Package::expected_size() const { return expected_size_; } -FileHash Package::expected_hash() const { +CString Package::expected_hash() const { __mutexScope(model()->lock()); - ASSERT1(!expected_hash_.sha256.IsEmpty() ||!expected_hash_.sha1.IsEmpty()); + ASSERT1(!expected_hash_.IsEmpty()); return expected_hash_; } diff --git a/omaha/goopdate/package.h b/omaha/goopdate/package.h index dd551414d..f3c9c4368 100644 --- a/omaha/goopdate/package.h +++ b/omaha/goopdate/package.h @@ -29,7 +29,6 @@ #include "omaha/base/time.h" #include "omaha/common/progress_sampler.h" #include "omaha/goopdate/com_wrapper_creator.h" -#include "omaha/goopdate/file_hash.h" #include "omaha/goopdate/model_object.h" // TODO(omaha): Consider implementing the NetworkRequestCallback portion in a // PImpl or similar pattern. As it is, every file that includes model.h also @@ -60,14 +59,14 @@ class Package virtual void OnRequestBegin(); virtual void OnRequestRetryScheduled(time64 next_download_retry_time); - void SetFileInfo(const CString& filename, uint64 size, const FileHash& hash); + void SetFileInfo(const CString& filename, uint64 size, const CString& hash); // Returns the name of the file specified in the manifest. CString filename() const; // Returns the expected size of the file in bytes. uint64 expected_size() const; // Returns expected file hashes. - FileHash expected_hash() const; + CString expected_hash() const; uint64 bytes_downloaded() const; @@ -85,7 +84,7 @@ class Package // The name of the package as it appears in the manifest. CString filename_; uint64 expected_size_; - FileHash expected_hash_; + CString expected_hash_; int bytes_downloaded_; int bytes_total_; @@ -108,7 +107,6 @@ class ATL_NO_VTABLE PackageWrapper kMajorTypeLibVersion, kMinorTypeLibVersion> { public: - // IPackage. STDMETHOD(get)(BSTR dir); STDMETHOD(get_isAvailable)(VARIANT_BOOL* is_available); diff --git a/omaha/goopdate/package_cache.cc b/omaha/goopdate/package_cache.cc index 2fa8ac49d..395fe948e 100644 --- a/omaha/goopdate/package_cache.cc +++ b/omaha/goopdate/package_cache.cc @@ -29,7 +29,6 @@ #include "omaha/base/signaturevalidator.h" #include "omaha/base/utils.h" #include "omaha/common/config_manager.h" -#include "omaha/goopdate/file_hash.h" #include "omaha/goopdate/package_cache_internal.h" #include "omaha/goopdate/worker_metrics.h" @@ -142,18 +141,54 @@ void SortPackageInfoByTime(std::vector* packages_info) { PackageSortByTimePredicate); } -CString GetHashString(const FileHash& hash) { - return hash.sha256.IsEmpty() ? hash.sha1 : hash.sha256; +HRESULT FileCopy(File* source_file, const CString& destination) { + ASSERT1(source_file); + + File destination_file; + HRESULT hr = destination_file.Open(destination, true, false); + if (FAILED(hr)) { + return hr; + } + + hr = source_file->SeekToBegin(); + if (FAILED(hr)) { + return hr; + } + + byte buffer[4096] = {}; + uint32 bytes_read = 0; + do { + hr = source_file->Read(arraysize(buffer), buffer, &bytes_read); + if (FAILED(hr)) { + return hr; + } + + if (!bytes_read) { + return S_OK; + } + + uint32 bytes_written(0); + hr = destination_file.Write(buffer, bytes_read, &bytes_written); + if (FAILED(hr)) { + return hr; + } + + if (bytes_written != bytes_read) { + return E_UNEXPECTED; + } + } while (bytes_read > 0); + + return S_OK; } } // namespace internal PackageCache::PackageCache() { cache_time_limit_days_ = - ConfigManager::Instance()->GetPackageCacheExpirationTimeDays(); + ConfigManager::Instance()->GetPackageCacheExpirationTimeDays(NULL); cache_size_limit_bytes_ = 1024 * 1024 * static_cast( - ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes()); + ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes(NULL)); } PackageCache::~PackageCache() { @@ -179,9 +214,9 @@ HRESULT PackageCache::Initialize(const CString& cache_root) { return S_OK; } -bool PackageCache::IsCached(const Key& key, const FileHash& hash) const { +bool PackageCache::IsCached(const Key& key, const CString& hash) const { CORE_LOG(L3, (_T("[PackageCache::IsCached][key '%s'][hash %s]"), - key.ToString(), internal::GetHashString(hash))); + key.ToString(), hash)); __mutexScope(cache_lock_); @@ -195,11 +230,13 @@ bool PackageCache::IsCached(const Key& key, const FileHash& hash) const { } HRESULT PackageCache::Put(const Key& key, - const CString& source_file, - const FileHash& hash) { + File* source_file, + const CString& hash) { + ASSERT1(source_file); + ++metric_worker_package_cache_put_total; - CORE_LOG(L3, (_T("[PackageCache::Put][key '%s'][source_file '%s'][hash %s]"), - key.ToString(), source_file, internal::GetHashString(hash))); + CORE_LOG(L3, (_T("[PackageCache::Put][key '%s'][hash %s]"), + key.ToString(), hash)); __mutexScope(cache_lock_); @@ -225,9 +262,7 @@ HRESULT PackageCache::Put(const Key& key, // TODO(omaha): consider not overwriting the file if the file is // in the cache and it is valid. - // When not impersonated, File::Copy resets the ownership of the destination - // file and it inherits ACEs from the new parent directory. - hr = File::Copy(source_file, destination_file, true); + hr = internal::FileCopy(source_file, destination_file); if (FAILED(hr)) { CORE_LOG(LE, (_T("[failed to copy file to cache][0x%08x][%s]"), hr, destination_file)); @@ -238,7 +273,7 @@ HRESULT PackageCache::Put(const Key& key, if (FAILED(hr)) { CORE_LOG(LE, (_T("[failed to verify hash for file '%s'][expected hash %s]"), - destination_file, internal::GetHashString(hash))); + destination_file, hash)); VERIFY1(::DeleteFile(destination_file)); return hr; } @@ -249,9 +284,9 @@ HRESULT PackageCache::Put(const Key& key, HRESULT PackageCache::Get(const Key& key, const CString& destination_file, - const FileHash& hash) const { + const CString& hash) const { CORE_LOG(L3, (_T("[PackageCache::Get][key '%s'][dest file '%s'][hash '%s']"), - key.ToString(), destination_file, internal::GetHashString(hash))); + key.ToString(), destination_file, hash)); __mutexScope(cache_lock_); @@ -274,7 +309,7 @@ HRESULT PackageCache::Get(const Key& key, hr = VerifyHash(source_file, hash); if (FAILED(hr)) { CORE_LOG(LE, (_T("[failed to verify hash for file '%s'][expected hash %s]"), - source_file, internal::GetHashString(hash))); + source_file, hash)); return hr; } @@ -485,19 +520,17 @@ HRESULT PackageCache::BuildCacheFileName(const CString& app_id, } HRESULT PackageCache::VerifyHash(const CString& filename, - const FileHash& expected_hash) { + const CString& expected_hash) { CORE_LOG(L3, (_T("[PackageCache::VerifyHash][%s][%s]"), - filename, internal::GetHashString(expected_hash))); + filename, expected_hash)); HighresTimer verification_timer; std::vector files; files.push_back(filename); - HRESULT hr = expected_hash.sha256.IsEmpty() ? - VerifyFileHash(files, expected_hash.sha1) : - VerifyFileHashSha256(files, expected_hash.sha256); + + HRESULT hr = VerifyFileHashSha256(files, expected_hash); CORE_LOG(L3, (_T("[PackageCache::VerifyHash completed][0x%08x][%d ms]"), hr, verification_timer.GetElapsedMs())); - return hr; } diff --git a/omaha/goopdate/package_cache.h b/omaha/goopdate/package_cache.h index b1352ad74..4145ec474 100644 --- a/omaha/goopdate/package_cache.h +++ b/omaha/goopdate/package_cache.h @@ -25,7 +25,7 @@ namespace omaha { -struct FileHash; +class File; class PackageCache { public: @@ -66,14 +66,14 @@ class PackageCache { HRESULT Initialize(const CString& cache_root); HRESULT Put(const Key& key, - const CString& source_file, - const FileHash& hash); + File* source_file, + const CString& hash); HRESULT Get(const Key& key, const CString& destination_file, - const FileHash& hash) const; + const CString& hash) const; - bool IsCached(const Key& key, const FileHash& hash) const; + bool IsCached(const Key& key, const CString& hash) const; HRESULT Purge(const Key& key); @@ -98,7 +98,7 @@ class PackageCache { CString cache_root() const; static HRESULT VerifyHash(const CString& filename, - const FileHash& expected_hash); + const CString& expected_hash); private: friend class PackageCacheTest; diff --git a/omaha/goopdate/package_cache_internal.h b/omaha/goopdate/package_cache_internal.h index e116e309f..e6f1a87c3 100644 --- a/omaha/goopdate/package_cache_internal.h +++ b/omaha/goopdate/package_cache_internal.h @@ -77,6 +77,8 @@ HRESULT FindDirectoryPackagesInfo(const CString& dir_path, void SortPackageInfoByTime(std::vector* packages_info); +HRESULT FileCopy(File* source_file, const CString& destination); + } // namespace internal } // namespace omaha diff --git a/omaha/goopdate/package_cache_unittest.cc b/omaha/goopdate/package_cache_unittest.cc index 1c4fb2728..e71b85af4 100644 --- a/omaha/goopdate/package_cache_unittest.cc +++ b/omaha/goopdate/package_cache_unittest.cc @@ -20,7 +20,6 @@ #include "omaha/base/safe_format.h" #include "omaha/base/string.h" #include "omaha/base/utils.h" -#include "omaha/goopdate/file_hash.h" #include "omaha/goopdate/package_cache.h" #include "omaha/testing/unit_test.h" @@ -28,10 +27,8 @@ namespace omaha { namespace { -const TCHAR* kFile1Sha1Hash = _T("ImV9skETZqGFMjs32vbZTvzAYJU="); const TCHAR* kFile1Sha256Hash = _T("49b45f78865621b154fa65089f955182345a67f9746841e43e2d6daa288988d0"); -const TCHAR* kFile2Sha1Hash = _T("Igq6bYaeXFJCjH770knXyJ6V53s="); const TCHAR* kFile2Sha256Hash = _T("f0bbd84d7ec364f6c33161d781b49d840ed792b8b10668c4180b9e6e128d0bc9"); @@ -53,24 +50,27 @@ class PackageCacheTest : public testing::TestWithParam { executable_path, _T("unittest_support\\download_cache_test\\") _T("{89640431-FE64-4da8-9860-1A1085A60E13}\\gears-win32-opt.msi")); - hash_file1_.sha1 = kFile1Sha1Hash; - if (GetParam()) { - hash_file1_.sha256 = kFile1Sha256Hash; - } + hash_file1_ = kFile1Sha256Hash; size_file1_ = 870400; source_file2_ = ConcatenatePath( executable_path, _T("unittest_support\\download_cache_test\\") _T("{7101D597-3481-4971-AD23-455542964072}\\livelysetup.exe")); - hash_file2_.sha1 = kFile2Sha1Hash; - if (GetParam()) { - hash_file2_.sha256 = kFile2Sha256Hash; - } + hash_file2_ = kFile2Sha256Hash; size_file2_ = 479848; EXPECT_TRUE(File::Exists(source_file1_)); EXPECT_TRUE(File::Exists(source_file2_)); + + EXPECT_SUCCEEDED(source_file1_file_.OpenShareMode(source_file1_, + false, + false, + FILE_SHARE_READ)); + EXPECT_SUCCEEDED(source_file2_file_.OpenShareMode(source_file2_, + false, + false, + FILE_SHARE_READ)); } virtual void SetUp() { @@ -119,38 +119,40 @@ class PackageCacheTest : public testing::TestWithParam { const CString cache_root_; CString source_file1_; - FileHash hash_file1_; + File source_file1_file_; + CString hash_file1_; uint64 size_file1_; CString source_file2_; - FileHash hash_file2_; + File source_file2_file_; + CString hash_file2_; uint64 size_file2_; PackageCache package_cache_; }; // Tests the members of key when the constructor arguments are empty strings. -TEST_P(PackageCacheTest, DefaultVersion) { +TEST_F(PackageCacheTest, DefaultVersion) { Key key(_T(""), _T(""), _T("")); EXPECT_STREQ(_T(""), key.app_id()); EXPECT_STREQ(_T("0.0.0.0"), key.version()); EXPECT_STREQ(_T(""), key.package_name()); } -TEST_P(PackageCacheTest, Initialize) { +TEST_F(PackageCacheTest, Initialize) { EXPECT_FALSE(package_cache_.cache_root().IsEmpty()); EXPECT_STREQ(cache_root_, package_cache_.cache_root()); EXPECT_EQ(0, package_cache_.Size()); } -TEST_P(PackageCacheTest, InitializeErrors) { +TEST_F(PackageCacheTest, InitializeErrors) { PackageCache package_cache; EXPECT_EQ(E_INVALIDARG, package_cache.Initialize(NULL)); EXPECT_EQ(E_INVALIDARG, package_cache.Initialize(_T(""))); EXPECT_EQ(E_INVALIDARG, package_cache.Initialize(_T("foo"))); } -TEST_P(PackageCacheTest, BuildCacheFileName) { +TEST_F(PackageCacheTest, BuildCacheFileName) { CString actual; EXPECT_HRESULT_SUCCEEDED( BuildCacheFileNameForKey(Key(_T("1"), _T("2"), _T("3")), &actual)); @@ -168,7 +170,7 @@ TEST_P(PackageCacheTest, BuildCacheFileName) { } // Tests Put, Get, IsCached, and Purge calls. -TEST_P(PackageCacheTest, BasicTest) { +TEST_F(PackageCacheTest, BasicTest) { EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll()); Key key1(_T("app1"), _T("ver1"), _T("package1")); @@ -177,9 +179,7 @@ TEST_P(PackageCacheTest, BasicTest) { EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_)); // Cache one file. - EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key1, - source_file1_, - hash_file1_)); + EXPECT_SUCCEEDED(package_cache_.Put(key1, &source_file1_file_, hash_file1_)); EXPECT_EQ(size_file1_, package_cache_.Size()); // Check the file is in the cache. @@ -193,45 +193,37 @@ TEST_P(PackageCacheTest, BasicTest) { EXPECT_FALSE(destination_file.IsEmpty()); // Get the file two times. - EXPECT_HRESULT_SUCCEEDED(package_cache_.Get(key1, - destination_file, - hash_file1_)); + EXPECT_SUCCEEDED(package_cache_.Get(key1, destination_file, hash_file1_)); EXPECT_TRUE(File::Exists(destination_file)); - EXPECT_HRESULT_SUCCEEDED(PackageCache::VerifyHash(destination_file, - hash_file1_)); - EXPECT_HRESULT_SUCCEEDED(package_cache_.Get(key1, - destination_file, - hash_file1_)); + EXPECT_SUCCEEDED(PackageCache::VerifyHash(destination_file, hash_file1_)); + + EXPECT_SUCCEEDED(package_cache_.Get(key1, destination_file, hash_file1_)); EXPECT_TRUE(File::Exists(destination_file)); - EXPECT_HRESULT_SUCCEEDED(PackageCache::VerifyHash(destination_file, - hash_file1_)); + + EXPECT_SUCCEEDED(PackageCache::VerifyHash(destination_file, hash_file1_)); EXPECT_TRUE(::DeleteFile(destination_file)); // Cache another file. Key key2(_T("app2"), _T("ver2"), _T("package2")); - EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key2, - source_file2_, - hash_file2_)); + EXPECT_SUCCEEDED(package_cache_.Put(key2, &source_file2_file_, hash_file2_)); EXPECT_EQ(size_file1_ + size_file2_, package_cache_.Size()); EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_)); // Cache the same file again. It should be idempotent. - EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key2, - source_file2_, - hash_file2_)); + EXPECT_SUCCEEDED(package_cache_.Put(key2, &source_file2_file_, hash_file2_)); EXPECT_EQ(size_file1_ + size_file2_, package_cache_.Size()); EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_)); EXPECT_TRUE(File::Exists(source_file2_)); - EXPECT_HRESULT_SUCCEEDED(package_cache_.Purge(key1)); + EXPECT_SUCCEEDED(package_cache_.Purge(key1)); EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_)); EXPECT_EQ(size_file2_, package_cache_.Size()); - EXPECT_HRESULT_SUCCEEDED(package_cache_.Purge(key2)); + EXPECT_SUCCEEDED(package_cache_.Purge(key2)); EXPECT_FALSE(package_cache_.IsCached(key2, hash_file2_)); EXPECT_EQ(0, package_cache_.Size()); @@ -247,7 +239,7 @@ TEST_P(PackageCacheTest, BasicTest) { EXPECT_FALSE(File::Exists(destination_file)); } -TEST_P(PackageCacheTest, PutBadHashTest) { +TEST_F(PackageCacheTest, PutBadHashTest) { EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll()); Key key1(_T("app1"), _T("ver1"), _T("package1")); @@ -255,16 +247,11 @@ TEST_P(PackageCacheTest, PutBadHashTest) { // Check the file is not in the cache. EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_)); - // Try caching one file when the SHA1 hash is not correct. - FileHash bad_hash; - if (GetParam()) { - bad_hash.sha256 = - _T("0000bad0000364f6c33161d781b49d840ed792b8b10668c4180b9e6e128d0bc9"); - } else { - bad_hash.sha1 = _T("JmV9skETZqGFMjs32vbZTvzAYJU="); - } + // Try caching one file when the hash is not correct. + CString bad_hash = + _T("0000bad0000364f6c33161d781b49d840ed792b8b10668c4180b9e6e128d0bc9"); EXPECT_EQ(SIGS_E_INVALID_SIGNATURE, package_cache_.Put(key1, - source_file1_, + &source_file1_file_, bad_hash)); // Check the file is not the cache. EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_)); @@ -272,23 +259,25 @@ TEST_P(PackageCacheTest, PutBadHashTest) { // The key must include the app id, version, and package name for Put and Get // operations. If the version is not provided, "0.0.0.0" is used internally. -TEST_P(PackageCacheTest, BadKeyTest) { +TEST_F(PackageCacheTest, BadKeyTest) { Key key_empty_app(_T(""), _T("b"), _T("c")); Key key_empty_name(_T("a"), _T("b"), _T("")); - FileHash bad_hash; - bad_hash.sha1 = _T("a"); - bad_hash.sha256 = _T("b"); + CString bad_hash; + bad_hash = _T("b"); EXPECT_EQ(E_INVALIDARG, package_cache_.Get(key_empty_app, _T("a"), bad_hash)); EXPECT_EQ(E_INVALIDARG, package_cache_.Get(key_empty_name, _T("a"), bad_hash)); - EXPECT_EQ(E_INVALIDARG, package_cache_.Put(key_empty_app, _T("a"), bad_hash)); - EXPECT_EQ(E_INVALIDARG, - package_cache_.Put(key_empty_name, _T("a"), bad_hash)); + EXPECT_EQ(E_INVALIDARG, package_cache_.Put(key_empty_app, + &source_file1_file_, + bad_hash)); + EXPECT_EQ(E_INVALIDARG, package_cache_.Put(key_empty_name, + &source_file1_file_, + bad_hash)); } -TEST_P(PackageCacheTest, PurgeVersionTest) { +TEST_F(PackageCacheTest, PurgeVersionTest) { // Cache two files for two versions of the same app. Key key11(_T("app1"), _T("ver1"), _T("package1")); Key key12(_T("app1"), _T("ver1"), _T("package2")); @@ -296,16 +285,16 @@ TEST_P(PackageCacheTest, PurgeVersionTest) { Key key22(_T("app1"), _T("ver2"), _T("package2")); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key11, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key12, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key21, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key22, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_TRUE(package_cache_.IsCached(key11, hash_file1_)); @@ -334,7 +323,7 @@ TEST_P(PackageCacheTest, PurgeVersionTest) { EXPECT_EQ(0, package_cache_.Size()); } -TEST_P(PackageCacheTest, PurgeAppTest) { +TEST_F(PackageCacheTest, PurgeAppTest) { // Cache two files for two apps. Key key11(_T("app1"), _T("ver1"), _T("package1")); Key key12(_T("app1"), _T("ver1"), _T("package2")); @@ -342,16 +331,16 @@ TEST_P(PackageCacheTest, PurgeAppTest) { Key key22(_T("app2"), _T("ver2"), _T("package2")); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key11, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key12, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key21, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key22, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_TRUE(package_cache_.IsCached(key11, hash_file1_)); @@ -380,7 +369,7 @@ TEST_P(PackageCacheTest, PurgeAppTest) { EXPECT_EQ(0, package_cache_.Size()); } -TEST_P(PackageCacheTest, PurgeAppLowerVersionsTest) { +TEST_F(PackageCacheTest, PurgeAppLowerVersionsTest) { EXPECT_EQ(E_INVALIDARG, package_cache_.PurgeAppLowerVersions(_T("app1"), _T("1"))); @@ -390,16 +379,16 @@ TEST_P(PackageCacheTest, PurgeAppLowerVersionsTest) { Key key_21_2(_T("app1"), _T("2.1.0.0"), _T("package2")); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_10_1, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_10_2, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_11_1, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_21_2, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAppLowerVersions(_T("app1"), @@ -428,7 +417,7 @@ TEST_P(PackageCacheTest, PurgeAppLowerVersionsTest) { EXPECT_EQ(0, package_cache_.Size()); } -TEST_P(PackageCacheTest, PurgeAll) { +TEST_F(PackageCacheTest, PurgeAll) { // Cache two files for two apps. Key key11(_T("app1"), _T("ver1"), _T("package1")); Key key12(_T("app1"), _T("ver1"), _T("package2")); @@ -436,16 +425,16 @@ TEST_P(PackageCacheTest, PurgeAll) { Key key22(_T("app2"), _T("ver2"), _T("package2")); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key11, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key12, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key21, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key22, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_TRUE(package_cache_.IsCached(key11, hash_file1_)); @@ -465,7 +454,7 @@ TEST_P(PackageCacheTest, PurgeAll) { EXPECT_EQ(0, package_cache_.Size()); } -TEST_P(PackageCacheTest, PurgeOldPackagesIfOverSizeLimit) { +TEST_F(PackageCacheTest, PurgeOldPackagesIfOverSizeLimit) { EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll()); const int kCacheSizeLimitMB = 2; @@ -486,7 +475,7 @@ TEST_P(PackageCacheTest, PurgeOldPackagesIfOverSizeLimit) { version.Format(_T("version%d"), i); package.Format(_T("package%d"), i); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(Key(app, version, package), - source_file1_, + &source_file1_file_, hash_file1_)); current_size += size_file1_; EXPECT_EQ(current_size, package_cache_.Size()); @@ -509,7 +498,7 @@ TEST_P(PackageCacheTest, PurgeOldPackagesIfOverSizeLimit) { EXPECT_LE(package_cache_.Size(), kSizeLimitBytes); } -TEST_P(PackageCacheTest, PurgeExpiredCacheFiles) { +TEST_F(PackageCacheTest, PurgeExpiredCacheFiles) { EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll()); const int kCacheLifeLimitDays = 100; @@ -518,10 +507,10 @@ TEST_P(PackageCacheTest, PurgeExpiredCacheFiles) { Key key1(_T("app1"), _T("version1"), _T("package1")); Key key2(_T("app2"), _T("version2"), _T("package2")); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key1, - source_file1_, + &source_file1_file_, hash_file1_)); EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key2, - source_file2_, + &source_file2_file_, hash_file2_)); EXPECT_TRUE(package_cache_.IsCached(key1, hash_file1_)); EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_)); @@ -539,14 +528,12 @@ TEST_P(PackageCacheTest, PurgeExpiredCacheFiles) { EXPECT_FALSE(package_cache_.IsCached(key2, hash_file2_)); } -TEST_P(PackageCacheTest, VerifyHash) { +TEST_F(PackageCacheTest, VerifyHash) { EXPECT_HRESULT_SUCCEEDED(PackageCache::VerifyHash(source_file1_, hash_file1_)); EXPECT_EQ(SIGS_E_INVALID_SIGNATURE, PackageCache::VerifyHash(source_file1_, hash_file2_)); } -INSTANTIATE_TEST_CASE_P(Sha1OrSha256, PackageCacheTest, ::testing::Bool()); - } // namespace omaha diff --git a/omaha/goopdate/ping_event_cancel_test.cc b/omaha/goopdate/ping_event_cancel_test.cc index cbab796a7..28bcd95ef 100644 --- a/omaha/goopdate/ping_event_cancel_test.cc +++ b/omaha/goopdate/ping_event_cancel_test.cc @@ -35,7 +35,7 @@ class PingEventCancelTest : public testing::Test { protected: void SetUpRegistry() { const TCHAR* const kOmahaUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\") GOOPDATE_APP_ID; diff --git a/omaha/clickonce/clickonce_bootstrap.rc b/omaha/goopdate/policy_status.cc similarity index 84% rename from omaha/clickonce/clickonce_bootstrap.rc rename to omaha/goopdate/policy_status.cc index f291ca906..1cef2fadd 100644 --- a/omaha/clickonce/clickonce_bootstrap.rc +++ b/omaha/goopdate/policy_status.cc @@ -1,4 +1,4 @@ -// Copyright 2008-2009 Google Inc. +// Copyright 2008-2010 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,5 +13,9 @@ // limitations under the License. // ======================================================================== -1 24 "clickonce_bootstrap.manifest" +#include "omaha/goopdate/policy_status.h" + +namespace omaha { + +} // namespace omaha diff --git a/omaha/goopdate/policy_status.h b/omaha/goopdate/policy_status.h new file mode 100644 index 000000000..5461b5c74 --- /dev/null +++ b/omaha/goopdate/policy_status.h @@ -0,0 +1,506 @@ +// Copyright 2008-2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// +// Contains PolicyStatus class to launch a process using a COM interface. This +// COM object can be created by medium integrity callers. + +#ifndef OMAHA_GOOPDATE_POLICY_STATUS_H__ +#define OMAHA_GOOPDATE_POLICY_STATUS_H__ + +#include +#include +#include +#include +#include +#include +#include "omaha/base/atlregmapex.h" +#include "omaha/base/const_object_names.h" +#include "omaha/base/constants.h" +#include "omaha/base/debug.h" +#include "omaha/base/logging.h" +#include "omaha/base/omaha_version.h" +#include "omaha/base/preprocessor_fun.h" +#include "omaha/common/config_manager.h" +#include "omaha/common/const_cmd_line.h" +#include "omaha/common/const_goopdate.h" +#include "omaha/common/goopdate_utils.h" +#include "omaha/goopdate/com_proxy.h" +#include "omaha/goopdate/non_localized_resource.h" + +#if defined(HAS_DEVICE_MANAGEMENT) +#include "omaha/goopdate/dm_client.h" +#endif + +// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR". +#include "goopdate/omaha3_idl.h" + +namespace omaha { + +const TCHAR* const kPolicyStatusDescription = + _T("Google Update Policy Status Class"); + +template +class ATL_NO_VTABLE PolicyStatus + : public CComObjectRootEx, + public CComCoClass>, + public IDispatchImpl, + public IDispatchImpl, + public StdMarshalInfo { + public: + DECLARE_NOT_AGGREGATABLE(PolicyStatus) + DECLARE_PROTECT_FINAL_CONSTRUCT() + + DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id()) + + BEGIN_REGISTRY_MAP() + REGMAP_ENTRY(_T("HKROOT"), T::hk_root()) + REGMAP_ENTRY(_T("VERSION"), _T("1.0")) + REGMAP_ENTRY(_T("PROGID"), T::prog_id()) + REGMAP_ENTRY(_T("DESCRIPTION"), kPolicyStatusDescription) + REGMAP_ENTRY(_T("CLSID"), T::class_id()) + REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName) + REGMAP_ENTRY(_T("ICONRESID"), PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON)) + REGMAP_ENTRY(_T("STRINGRESID"), + PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME)) + END_REGISTRY_MAP() + + BEGIN_COM_MAP(PolicyStatus) + COM_INTERFACE_ENTRY(IPolicyStatus) + COM_INTERFACE_ENTRY(IPolicyStatus2) + COM_INTERFACE_ENTRY(IPolicyStatus3) + COM_INTERFACE_ENTRY(IStdMarshalInfo) + END_COM_MAP() + + PolicyStatus() : StdMarshalInfo(T::is_machine()) { + CORE_LOG(L6, (_T("[PolicyStatus]"))); + } + + virtual ~PolicyStatus() { + CORE_LOG(L6, (_T("[~PolicyStatus]"))); + } + + // IPolicyStatus/IPolicyStatus2. + STDMETHODIMP get_lastCheckPeriodMinutes(DWORD* minutes) { + ASSERT1(minutes); + + bool is_overridden = false; + int period = ConfigManager::Instance()->GetLastCheckPeriodSec( + &is_overridden, + NULL); + *minutes = static_cast(period) / kSecPerMin; + return S_OK; + } + + STDMETHODIMP get_updatesSuppressedTimes( + DWORD* start_hour, + DWORD* start_min, + DWORD* duration_min, + VARIANT_BOOL* are_updates_suppressed) { + ASSERT1(start_hour); + ASSERT1(start_min); + ASSERT1(duration_min); + ASSERT1(are_updates_suppressed); + + UpdatesSuppressedTimes times; + bool updates_suppressed = false; + + HRESULT hr = ConfigManager::Instance()->GetUpdatesSuppressedTimes( + CTime::GetCurrentTime(), + ×, + &updates_suppressed, + NULL); + if (FAILED(hr)) { + return hr; + } + + *start_hour = times.start_hour; + *start_min = times.start_min; + *duration_min = times.duration_min; + *are_updates_suppressed = updates_suppressed ? VARIANT_TRUE : VARIANT_FALSE; + + return S_OK; + } + + STDMETHODIMP get_downloadPreferenceGroupPolicy(BSTR* pref) { + ASSERT1(pref); + + *pref = ConfigManager::Instance()->GetDownloadPreferenceGroupPolicy(NULL) + .AllocSysString(); + return S_OK; + } + + STDMETHODIMP get_packageCacheSizeLimitMBytes(DWORD* limit) { + ASSERT1(limit); + + *limit = static_cast( + ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes(NULL)); + return S_OK; + } + + STDMETHODIMP get_packageCacheExpirationTimeDays(DWORD* days) { + ASSERT1(days); + + *days = static_cast( + ConfigManager::Instance()->GetPackageCacheExpirationTimeDays(NULL)); + return S_OK; + } + + STDMETHODIMP get_effectivePolicyForAppInstalls(BSTR app_id, DWORD* policy) { + ASSERT1(policy); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + *policy = static_cast( + ConfigManager::Instance()->GetEffectivePolicyForAppInstalls(app_guid, + NULL)); + + return S_OK; + } + + STDMETHODIMP get_effectivePolicyForAppUpdates(BSTR app_id, DWORD* policy) { + ASSERT1(policy); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + *policy = static_cast( + ConfigManager::Instance()->GetEffectivePolicyForAppUpdates(app_guid, + NULL)); + + return S_OK; + } + + STDMETHODIMP get_targetVersionPrefix(BSTR app_id, BSTR* prefix) { + ASSERT1(prefix); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + *prefix = ConfigManager::Instance()->GetTargetVersionPrefix(app_guid, NULL) + .AllocSysString(); + + return S_OK; + } + + STDMETHODIMP get_isRollbackToTargetVersionAllowed( + BSTR app_id, + VARIANT_BOOL* rollback_allowed) { + ASSERT1(rollback_allowed); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + *rollback_allowed = + ConfigManager::Instance()->IsRollbackToTargetVersionAllowed(app_guid, + NULL) ? + VARIANT_TRUE : + VARIANT_FALSE; + + return S_OK; + } + + STDMETHODIMP get_updaterVersion(BSTR* version) { + ASSERT1(version); + + *version = CString(GetVersionString()).AllocSysString(); + return S_OK; + } + + STDMETHODIMP get_lastCheckedTime(DATE* last_checked) { + ASSERT1(last_checked); + + const DWORD value = + ConfigManager::Instance()->GetLastCheckedTime(T::is_machine()); + if (!value) { + return E_FAIL; + } + + FILETIME ft = {}; + Time64ToFileTime(Int32ToTime64(value), &ft); + *last_checked = ATL::COleDateTime(ft); + return S_OK; + } + + STDMETHODIMP refreshPolicies() { +#if defined(HAS_DEVICE_MANAGEMENT) + return T::is_machine() && ::IsUserAnAdmin() ? dm_client::RefreshPolicies() : + E_ACCESSDENIED; +#else + return E_NOTIMPL; +#endif // #if defined(HAS_DEVICE_MANAGEMENT) + } + + STDMETHODIMP get_lastCheckPeriodMinutes(IPolicyStatusValue** value) { + ASSERT1(value); + + bool is_overridden = false; + + // |value| will be returned in minutes. + int period = + ConfigManager::Instance()->GetLastCheckPeriodSec(&is_overridden, + value); + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_updatesSuppressedTimes( + IPolicyStatusValue** value, + VARIANT_BOOL* are_updates_suppressed) { + ASSERT1(value); + ASSERT1(are_updates_suppressed); + + UpdatesSuppressedTimes times; + bool updates_suppressed = false; + + HRESULT hr = ConfigManager::Instance()->GetUpdatesSuppressedTimes( + CTime::GetCurrentTime(), + ×, + &updates_suppressed, + value); + if (FAILED(hr)) { + return hr; + } + + ASSERT1(*value); + *are_updates_suppressed = updates_suppressed ? VARIANT_TRUE : VARIANT_FALSE; + + return S_OK; + } + + STDMETHODIMP get_downloadPreferenceGroupPolicy(IPolicyStatusValue** value) { + ASSERT1(value); + + ConfigManager::Instance()->GetDownloadPreferenceGroupPolicy(value); + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_packageCacheSizeLimitMBytes(IPolicyStatusValue** value) { + ASSERT1(value); + + ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes(value); + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_packageCacheExpirationTimeDays(IPolicyStatusValue** value) { + ASSERT1(value); + + ConfigManager::Instance()->GetPackageCacheExpirationTimeDays(value); + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_proxyMode(IPolicyStatusValue** value) { + ASSERT1(value); + + CString proxy_mode; + + HRESULT hr = ConfigManager::Instance()->GetProxyMode(&proxy_mode, value); + if (FAILED(hr)) { + return hr; + } + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_proxyPacUrl(IPolicyStatusValue** value) { + ASSERT1(value); + + CString proxy_pac_url; + + HRESULT hr = ConfigManager::Instance()->GetProxyPacUrl(&proxy_pac_url, + value); + if (FAILED(hr)) { + return hr; + } + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_proxyServer(IPolicyStatusValue** value) { + ASSERT1(value); + + CString proxy_server; + + HRESULT hr = ConfigManager::Instance()->GetProxyServer(&proxy_server, + value); + if (FAILED(hr)) { + return hr; + } + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_effectivePolicyForAppInstalls(BSTR app_id, + IPolicyStatusValue** value) { + ASSERT1(value); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + ConfigManager::Instance()->GetEffectivePolicyForAppInstalls(app_guid, + value); + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_effectivePolicyForAppUpdates(BSTR app_id, + IPolicyStatusValue** value) { + ASSERT1(value); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + ConfigManager::Instance()->GetEffectivePolicyForAppUpdates(app_guid, value); + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_targetVersionPrefix(BSTR app_id, + IPolicyStatusValue** value) { + ASSERT1(value); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + ConfigManager::Instance()->GetTargetVersionPrefix(app_guid, value); + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_isRollbackToTargetVersionAllowed( + BSTR app_id, + IPolicyStatusValue** value) { + ASSERT1(value); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + ConfigManager::Instance()->IsRollbackToTargetVersionAllowed(app_guid, + value); + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_targetChannel(BSTR app_id, IPolicyStatusValue** value) { + ASSERT1(value); + + GUID app_guid = GUID_NULL; + HRESULT hr = StringToGuidSafe(app_id, &app_guid); + if (FAILED(hr)) { + return hr; + } + + ConfigManager::Instance()->GetTargetChannel(app_guid, value); + + ASSERT1(*value); + return S_OK; + } + + STDMETHODIMP get_forceInstallApps(VARIANT_BOOL is_machine, + IPolicyStatusValue** value) { + ASSERT1(value); + + std::vector app_ids; + HRESULT hr = ConfigManager::Instance()->GetForceInstallApps(!!is_machine, + &app_ids, + value); + if (FAILED(hr)) { + return hr; + } + + ASSERT1(*value); + return S_OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(PolicyStatus); +}; + +struct PolicyStatusModeUser { + static bool is_machine() { return false; } + static const TCHAR* prog_id() { return kProgIDPolicyStatusUser; } + static GUID class_id() { return __uuidof(PolicyStatusUserClass); } + static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; } + static const TCHAR* hk_root() { return _T("HKCU"); } +}; + +struct PolicyStatusModeMachineFallback { + static bool is_machine() { return true; } + static const TCHAR* prog_id() { + return kProgIDPolicyStatusMachineFallback; + } + static GUID class_id() { return __uuidof(PolicyStatusMachineFallbackClass); } + static UINT registry_res_id() { return IDR_LOCAL_SERVER_ELEVATION_RGS; } + static const TCHAR* hk_root() { return _T("HKLM"); } +}; + +struct PolicyStatusModeService { + static bool is_machine() { return true; } + static const TCHAR* prog_id() { return kProgIDPolicyStatusSvc; } + static GUID class_id() { return __uuidof(PolicyStatusMachineServiceClass); } + static UINT registry_res_id() { return IDR_LOCAL_SERVICE_RGS; } + static const TCHAR* hk_root() { return _T("HKLM"); } +}; + +typedef PolicyStatus PolicyStatusUser; +typedef PolicyStatus + PolicyStatusMachineFallback; +typedef PolicyStatus PolicyStatusService; + +} // namespace omaha + +#endif // OMAHA_GOOPDATE_POLICY_STATUS_H__ diff --git a/omaha/goopdate/policy_status_value.cc b/omaha/goopdate/policy_status_value.cc new file mode 100644 index 000000000..57c9e12e4 --- /dev/null +++ b/omaha/goopdate/policy_status_value.cc @@ -0,0 +1,95 @@ +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +#include "omaha/goopdate/policy_status_value.h" +#include +#include +#include +#include "omaha/base/debug.h" +#include "omaha/base/logging.h" + +namespace omaha { + +HRESULT PolicyStatusValue::Create( + const CString& source, + const CString& value, + bool has_conflict, + const CString& conflict_source, + const CString& conflict_value, + IPolicyStatusValue** policy_status_value) { + ASSERT1(policy_status_value); + + CComObject* v = NULL; + HRESULT hr = CComObject::CreateInstance(&v); + if (FAILED(hr)) { + return hr; + } + + v->source_ = source.AllocSysString(); + v->value_ = value.AllocSysString(); + v->has_conflict_ = has_conflict ? VARIANT_TRUE : VARIANT_FALSE; + v->conflict_source_ = conflict_source.AllocSysString(); + v->conflict_value_ = conflict_value.AllocSysString(); + + CComPtr status_value(v); + *policy_status_value = status_value.Detach(); + + return S_OK; +} + +PolicyStatusValue::PolicyStatusValue() : m_bRequiresSave(TRUE), + has_conflict_(VARIANT_FALSE) { +} + +PolicyStatusValue::~PolicyStatusValue() { +} + +// IPolicyStatusValue. +STDMETHODIMP PolicyStatusValue::get_source(BSTR* source) { + ASSERT1(source); + + *source = source_.Copy(); + return S_OK; +} + +STDMETHODIMP PolicyStatusValue::get_value(BSTR* value) { + ASSERT1(value); + + *value = value_.Copy(); + return S_OK; +} + +STDMETHODIMP PolicyStatusValue::get_hasConflict(VARIANT_BOOL* has_conflict) { + ASSERT1(has_conflict); + + *has_conflict = has_conflict_; + return S_OK; +} + +STDMETHODIMP PolicyStatusValue::get_conflictSource(BSTR* conflict_source) { + ASSERT1(conflict_source); + + *conflict_source = conflict_source_.Copy(); + return S_OK; +} + +STDMETHODIMP PolicyStatusValue::get_conflictValue(BSTR* conflict_value) { + ASSERT1(conflict_value); + + *conflict_value = conflict_value_.Copy(); + return S_OK; +} + +} // namespace omaha diff --git a/omaha/goopdate/policy_status_value.h b/omaha/goopdate/policy_status_value.h new file mode 100644 index 000000000..6f557b62a --- /dev/null +++ b/omaha/goopdate/policy_status_value.h @@ -0,0 +1,115 @@ +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +// PolicyStatusValue represents the managed state of a single Google Update +// policy. It contains the current source and value, as well as if any conflicts +// exist with that policy. +// IPolicyStatusValue can be queried for the current source and value, as well +// as if any conflicts exist with that policy. +// PolicyStatusValue implements IMarshal to marshal itself by value for +// performance, since this reduces the number of interprocess calls. + +#ifndef OMAHA_GOOPDATE_POLICY_STATUS_VALUE_H_ +#define OMAHA_GOOPDATE_POLICY_STATUS_VALUE_H_ + +#include +#include +#include + +#include "base/basictypes.h" +#include "goopdate/omaha3_idl.h" +#include "omaha/base/atlregmapex.h" +#include "omaha/base/constants.h" +#include "omaha/base/marshal_by_value.h" +#include "omaha/goopdate/google_update_ps_resource.h" +#include "omaha/common/goopdate_utils.h" + +namespace omaha { + +class ATL_NO_VTABLE PolicyStatusValue + : public CComObjectRootEx, + public CComCoClass, + public IDispatchImpl, + public IPersistStreamInitImpl, + public MarshalByValue { + public: + static HRESULT Create(const CString& source, + const CString& value, + bool has_conflict, + const CString& conflict_source, + const CString& conflict_value, + IPolicyStatusValue** policy_status_value); + PolicyStatusValue(); + + static bool is_machine() { + return goopdate_utils::IsRunningFromOfficialGoopdateDir(true); + } + + static const CLSID& GetObjectCLSID() { + return is_machine() ? __uuidof(PolicyStatusValueMachineClass) : + __uuidof(PolicyStatusValueUserClass); + } + + DECLARE_REGISTRY_RESOURCEID_EX(IDR_INPROC_SERVER_RGS) + + BEGIN_REGISTRY_MAP() + REGMAP_ENTRY(_T("HKROOT"), is_machine() ? _T("HKLM") : _T("HKCU")) + REGMAP_ENTRY(_T("CLSID"), GetObjectCLSID()) + END_REGISTRY_MAP() + + BEGIN_PROP_MAP(PolicyStatusValue) + PROP_DATA_ENTRY("Source", source_, VT_BSTR) + PROP_DATA_ENTRY("Value", value_, VT_BSTR) + PROP_DATA_ENTRY("HasConflict", has_conflict_, VT_BOOL) + PROP_DATA_ENTRY("ConflictSource", conflict_source_, VT_BSTR) + PROP_DATA_ENTRY("ConflictValue", conflict_value_, VT_BSTR) + END_PROP_MAP() + + // IPolicyStatusValue. + STDMETHOD(get_source)(BSTR* source); + STDMETHOD(get_value)(BSTR* value); + STDMETHOD(get_hasConflict)(VARIANT_BOOL* has_conflict); + STDMETHOD(get_conflictSource)(BSTR* conflict_source); + STDMETHOD(get_conflictValue)(BSTR* conflict_value); + + protected: + virtual ~PolicyStatusValue(); + + BEGIN_COM_MAP(PolicyStatusValue) + COM_INTERFACE_ENTRY(IPolicyStatusValue) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IMarshal) + END_COM_MAP() + + BOOL m_bRequiresSave; + + private: + CComBSTR source_; + CComBSTR value_; + VARIANT_BOOL has_conflict_; + CComBSTR conflict_source_; + CComBSTR conflict_value_; + + DISALLOW_COPY_AND_ASSIGN(PolicyStatusValue); +}; + +} // namespace omaha + +#endif // OMAHA_GOOPDATE_POLICY_STATUS_VALUE_H_ + diff --git a/omaha/goopdate/resources/build.scons b/omaha/goopdate/resources/build.scons index 4db221668..c9d584dcf 100644 --- a/omaha/goopdate/resources/build.scons +++ b/omaha/goopdate/resources/build.scons @@ -36,9 +36,6 @@ for omaha_version_info in local_env['omaha_versions_info']: omaha_version_info.GetVersionString()), '/DLANGUAGE_STRING=\\"%s\\"' % lang ], - CPPPATH = [ - '$OBJ_ROOT/goopdate', # Needed for the tlb - ], LINKFLAGS = [ '/ENTRY:DllEntry', '/MERGE:.rdata=.text', @@ -75,7 +72,7 @@ for omaha_version_info in local_env['omaha_versions_info']: source=lang_inputs ) - signed_dll = lang_env.DualSignedBinary( + signed_dll = lang_env.SignedBinary( target='%s/%sgoopdateres_%s.dll' % (lang, prefix, lang), source=unsigned_dll, ) diff --git a/omaha/goopdate/resources/chrome.bmp b/omaha/goopdate/resources/chrome.bmp deleted file mode 100644 index bfde16d63..000000000 Binary files a/omaha/goopdate/resources/chrome.bmp and /dev/null differ diff --git a/omaha/goopdate/resources/goopdateres/generated_resources_en.rc b/omaha/goopdate/resources/goopdateres/generated_resources_en.rc index aa7d5c4ee..e5dbbbc65 100644 Binary files a/omaha/goopdate/resources/goopdateres/generated_resources_en.rc and b/omaha/goopdate/resources/goopdateres/generated_resources_en.rc differ diff --git a/omaha/goopdate/resources/goopdateres/goopdate.grh b/omaha/goopdate/resources/goopdateres/goopdate.grh index 0fa27cd55..2fac62b93 100644 --- a/omaha/goopdate/resources/goopdateres/goopdate.grh +++ b/omaha/goopdate/resources/goopdateres/goopdate.grh @@ -1,4 +1,4 @@ -// Copyright 2007-2016 Google Inc. +// Copyright 2007-2022 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,12 +13,29 @@ // limitations under the License. // ======================================================================== // This file is automatically generated by GRIT. Do not edit. -// Built on Fri Apr 08 17:01:06 2016 -#ifndef RESOURCE_421947754731__ -#define RESOURCE_421947754731__ +#pragma once +#define IDB_ERROR_ILLUSTRATION 1000 +#define IDD_PROGRESS 2000 +#define IDC_TITLE_BAR_SPACER 2001 +#define IDC_GET_HELP 2002 +#define IDC_CLOSE 2003 +#define IDC_BUTTON1 2004 +#define IDC_BUTTON2 2005 +#define IDC_PROGRESS 2006 +#define IDC_INSTALLER_STATE_TEXT 2007 +#define IDC_INFO_TEXT 2008 +#define IDC_PAUSE_RESUME_TEXT 2009 +#define IDC_COMPLETE_TEXT 2010 +#define IDC_ERROR_TEXT 2011 +#define IDC_ERROR_ILLUSTRATION 2012 +#define IDC_APP_BITMAP 2013 +#define IDD_INSTALL_STOPPED 2014 +#define IDC_INSTALL_STOPPED_TEXT 2015 +#define IDD_YES_NO 2016 +#define IDC_YES_NO_TEXT 2017 #define IDS_FRIENDLY_COMPANY_NAME 3000 #define IDS_PRODUCT_DISPLAY_NAME 3001 #define IDS_DEFAULT_APP_DISPLAY_NAME 3002 @@ -101,24 +118,3 @@ #define IDS_BUNDLE_MIXED_RESULTS_CANCELED_APPS 3082 #define IDS_APPLICATION_NAME_CONCATENATION 3083 #define IDS_PROXY_PROMPT_MESSAGE 3084 -#define IDB_ERROR_ILLUSTRATION 1000 -#define IDD_PROGRESS 2000 -#define IDC_TITLE_BAR_SPACER 2001 -#define IDC_GET_HELP 2002 -#define IDC_CLOSE 2003 -#define IDC_BUTTON1 2004 -#define IDC_BUTTON2 2005 -#define IDC_PROGRESS 2006 -#define IDC_INSTALLER_STATE_TEXT 2008 -#define IDC_INFO_TEXT 2009 -#define IDC_PAUSE_RESUME_TEXT 2010 -#define IDC_COMPLETE_TEXT 2011 -#define IDC_ERROR_TEXT 2012 -#define IDC_ERROR_ILLUSTRATION 2013 -#define IDC_APP_BITMAP 2014 -#define IDD_INSTALL_STOPPED 2015 -#define IDC_INSTALL_STOPPED_TEXT 2017 -#define IDD_YES_NO 2018 -#define IDC_YES_NO_TEXT 2020 - -#endif // RESOURCE_421947754731__ diff --git a/omaha/goopdate/resources/resource_only_dll.def b/omaha/goopdate/resources/resource_only_dll.def index 078c46b80..51c6f0b0d 100644 --- a/omaha/goopdate/resources/resource_only_dll.def +++ b/omaha/goopdate/resources/resource_only_dll.def @@ -13,4 +13,4 @@ ; limitations under the License. ; ======================================================================== ; -; dummy def file to create implib +; def file to create implib diff --git a/omaha/goopdate/update3web.h b/omaha/goopdate/update3web.h index 7d4dfe905..9671e315f 100644 --- a/omaha/goopdate/update3web.h +++ b/omaha/goopdate/update3web.h @@ -100,7 +100,7 @@ class ATL_NO_VTABLE Update3Web REGMAP_ENTRY(_T("HKROOT"), T::hk_root()) REGMAP_ENTRY(_T("VERSION"), _T("1.0")) REGMAP_ENTRY(_T("PROGID"), T::prog_id()) - REGMAP_ENTRY(_T("DESCRIPTION"), _T("GoogleUpdate Update3Web")) + REGMAP_ENTRY(_T("DESCRIPTION"), MAIN_EXE_BASE_NAME _T(" Update3Web")) REGMAP_ENTRY(_T("CLSID"), T::class_id()) REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName) REGMAP_ENTRY(_T("ICONRESID"), PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON)) @@ -117,30 +117,30 @@ class ATL_NO_VTABLE Update3Web struct Update3WebModeUser { static bool is_machine() { return false; } - static const TCHAR* const prog_id() { return kProgIDUpdate3WebUser; } + static const TCHAR* prog_id() { return kProgIDUpdate3WebUser; } static GUID class_id() { return __uuidof(GoogleUpdate3WebUserClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; } - static const TCHAR* const hk_root() { return _T("HKCU"); } + static const TCHAR* hk_root() { return _T("HKCU"); } }; struct Update3WebModeMachineFallback { static bool is_machine() { return true; } - static const TCHAR* const prog_id() { + static const TCHAR* prog_id() { return kProgIDUpdate3WebMachineFallback; } static GUID class_id() { return __uuidof(GoogleUpdate3WebMachineFallbackClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVER_ELEVATION_RGS; } - static const TCHAR* const hk_root() { return _T("HKLM"); } + static const TCHAR* hk_root() { return _T("HKLM"); } }; struct Update3WebModeService { static bool is_machine() { return true; } - static const TCHAR* const prog_id() { return kProgIDUpdate3WebSvc; } + static const TCHAR* prog_id() { return kProgIDUpdate3WebSvc; } static GUID class_id() { return __uuidof(GoogleUpdate3WebServiceClass); } static UINT registry_res_id() { return IDR_LOCAL_SERVICE_RGS; } - static const TCHAR* const hk_root() { return _T("HKLM"); } + static const TCHAR* hk_root() { return _T("HKLM"); } }; typedef Update3Web Update3WebUser; diff --git a/omaha/goopdate/update_request_utils.cc b/omaha/goopdate/update_request_utils.cc index 0ef8e3fae..05a9a891c 100644 --- a/omaha/goopdate/update_request_utils.cc +++ b/omaha/goopdate/update_request_utils.cc @@ -32,7 +32,7 @@ void BuildRequest(const App* app, return; } - if (!app->is_eula_accepted()) { + if (!app->is_eula_accepted() && !app->app_bundle()->is_offline_install()) { CORE_LOG(L3, (_T("[App EULA not accepted - not including app in ping][%s]"), app->app_guid_string())); return; @@ -62,21 +62,21 @@ void BuildRequest(const App* app, request_app.cohort_hint = app->cohort().hint; request_app.cohort_name = app->cohort().name; - if (!app->server_install_data_index().IsEmpty()) { - xml::request::Data install_data = { xml::value::kInstallData, - app->server_install_data_index(), - _T("") }; - request_app.data.push_back(install_data); - } - - if (!app->untrusted_data().IsEmpty()) { - xml::request::Data untrusted_data = { xml::value::kUntrusted, - _T(""), - app->untrusted_data() }; - request_app.data.push_back(untrusted_data); - } - if (is_update_check) { + if (!app->server_install_data_index().IsEmpty()) { + xml::request::Data install_data = { xml::value::kInstallData, + app->server_install_data_index(), + _T("") }; + request_app.data.push_back(install_data); + } + + if (!app->untrusted_data().IsEmpty()) { + xml::request::Data untrusted_data = { xml::value::kUntrusted, + _T(""), + app->untrusted_data() }; + request_app.data.push_back(untrusted_data); + } + request_app.ping.active = app->did_run(); request_app.ping.days_since_last_active_ping = app->days_since_last_active_ping(); @@ -94,6 +94,7 @@ void BuildRequest(const App* app, app->IsRollbackToTargetVersionAllowed(); request_app.update_check.target_version_prefix = app->GetTargetVersionPrefix(); + request_app.update_check.target_channel = app->GetTargetChannel(); } request_app.ping_events = app->ping_events(); diff --git a/omaha/goopdate/update_request_utils_unittest.cc b/omaha/goopdate/update_request_utils_unittest.cc index 8787c7cd5..0c2d551ef 100644 --- a/omaha/goopdate/update_request_utils_unittest.cc +++ b/omaha/goopdate/update_request_utils_unittest.cc @@ -31,15 +31,12 @@ namespace update_request_utils { namespace { #define USER_UPDATE_KEY \ - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\") + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\") #define APP_ID1 _T("{DDE97E2B-A82C-4790-A630-FCA02F64E8BE}"); const TCHAR* const kAppId1 = APP_ID1 -const TCHAR* const kAppId1ClientsKeyPathUser = - USER_UPDATE_KEY _T("Clients\\") APP_ID1; const TCHAR* const kAppId1ClientStateKeyPathUser = USER_UPDATE_KEY _T("ClientState\\") APP_ID1; const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_ID1; -const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_ID1; const TCHAR* const kAppDidRunValueName = _T("dr"); } // namespace @@ -134,7 +131,7 @@ TEST_P(UpdateRequestUtilsTest, BuildRequest_UpdateCheck_GroupPolicy_InstallDisabled) { EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); - SetPolicy(kInstallPolicyApp1, kPolicyDisabled); + SetEnrolledPolicy(kInstallPolicyApp1, kPolicyDisabled); BuildRequest(app_, true, update_request_.get()); @@ -157,8 +154,7 @@ TEST_P(UpdateRequestUtilsTest, const TCHAR* const kTargetVersionPrefixApp1 = _T("TargetVersionPrefix") APP_ID1; - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kTargetVersionPrefixApp1, _T("55.3")); + SetPolicyString(kTargetVersionPrefixApp1, _T("55.3")); BuildRequest(app_, true, update_request_.get()); @@ -176,15 +172,31 @@ TEST_P(UpdateRequestUtilsTest, BuildRequest_UpdateCheck_GroupPolicy_RollbackToTargetVersion) { EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); + const TCHAR* const kRollbackToTargetVersionApp1 = + _T("RollbackToTargetVersion") APP_ID1; + SetEnrolledPolicy(kRollbackToTargetVersionApp1, 1UL); + + BuildRequest(app_, true, update_request_.get()); + + const xml::request::Request& request = update_request_->request(); + ASSERT_EQ(1, request.apps.size()); + + const xml::request::App& app = request.apps[0]; + const xml::request::UpdateCheck& update_check = app.update_check; + EXPECT_TRUE(update_check.is_valid); + EXPECT_EQ(IsDomain() ? true : false, update_check.is_rollback_allowed); +} + +TEST_P(UpdateRequestUtilsTest, + BuildRequest_UpdateCheck_GroupPolicy_TargetChannel) { + EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE)); + EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain, IsDomain() ? 1UL : 0UL)); - const TCHAR* const kRollbackToTargetVersionApp1 = - _T("RollbackToTargetVersion") APP_ID1; - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRollbackToTargetVersionApp1, - 1UL); + const TCHAR* const kTargetChannelApp1 = _T("TargetChannel") APP_ID1; + SetPolicyString(kTargetChannelApp1, _T("beta")); BuildRequest(app_, true, update_request_.get()); @@ -194,7 +206,7 @@ TEST_P(UpdateRequestUtilsTest, const xml::request::App& app = request.apps[0]; const xml::request::UpdateCheck& update_check = app.update_check; EXPECT_TRUE(update_check.is_valid); - EXPECT_EQ(IsDomain() ? true : false, update_check.is_rollback_allowed); + EXPECT_STREQ(IsDomain() ? _T("beta") : _T(""), update_check.target_channel); } TEST_F(UpdateRequestUtilsTest, diff --git a/omaha/goopdate/update_response_utils.cc b/omaha/goopdate/update_response_utils.cc index dbf03b3e4..1474956bd 100644 --- a/omaha/goopdate/update_response_utils.cc +++ b/omaha/goopdate/update_response_utils.cc @@ -14,14 +14,18 @@ // ======================================================================== #include "omaha/goopdate/update_response_utils.h" + #include +#include +#include +#include + #include "omaha/base/debug.h" #include "omaha/base/error.h" #include "omaha/base/logging.h" -#include "omaha/base/atl_regexp.h" #include "omaha/base/system_info.h" -#include "omaha/common/lang.h" #include "omaha/common/experiment_labels.h" +#include "omaha/common/lang.h" #include "omaha/common/xml_const.h" #include "omaha/common/xml_parser.h" #include "omaha/goopdate/model.h" @@ -38,7 +42,7 @@ namespace { // version and it comes from the update response, or with the a dotted quad, // which is the OS version of the host. ULONGLONG OSVersionFromString(const CString& s) { - if (AtlRE::PartialMatch(s, AtlRE(_T("^\\d+\\.\\d+$")))) { + if (std::regex_search(std::wstring(s), std::wregex(_T("^\\d+\\.\\d+$")))) { // Convert from "x.y" to "x.y.0.0" format so we can use the existing // VersionFromString utility function. return VersionFromString(s + _T(".0.0")); @@ -46,8 +50,8 @@ ULONGLONG OSVersionFromString(const CString& s) { // The string is a dotted quad version. VersionFromString handles the error if // the parameter is something else. - ASSERT1(AtlRE::PartialMatch(s, AtlRE(_T("^\\d+\\.\\d+\\.\\d+\\.\\d+$")))); - + ASSERT1(std::regex_search(std::wstring(s), + std::wregex(_T("^\\d+\\.\\d+\\.\\d+\\.\\d+$")))); return VersionFromString(s); } @@ -55,13 +59,59 @@ bool IsPlatformCompatible(const CString& platform) { return platform.IsEmpty() || !platform.CompareNoCase(kPlatformWin); } -bool IsArchCompatible(const CString& arch) { - const CString current_arch(xml::ConvertProcessorArchitectureToString( - SystemInfo::GetProcessorArchitecture())); - return arch.IsEmpty() || - !arch.CompareNoCase(current_arch) || - (arch == xml::value::kArchIntel && - current_arch == xml::value::kArchAmd64); +// Checks if the current architecture is compatible with the entries in +// `arch_list`. `arch_list` can be a single entry, or multiple entries separated +// with `,`. Entries prefixed with `-` (negative entries) indicate +// non-compatible hosts. Non-prefixed entries indicate compatible guests. +// +// Returns `true` if: +// * `arch_list` is empty, or +// * none of the negative entries within `arch_list` match the current host +// architecture exactly, and there are no non-negative entries, or +// * one of the non-negative entries within `arch_list` matches the current +// architecture, or is compatible with the current architecture (i.e., it is a +// compatible guest for the current host) as determined by +// `::IsWow64GuestMachineSupported()`. +// * If `::IsWow64GuestMachineSupported()` is not available, returns `true` +// if `arch` is x86. +// +// Examples: +// * `arch_list` == "x86": returns `true` if run on all systems, because Omaha3 +// is x86, and is running the logic to determine compatibility). +// * `arch_list` == "x64": returns `true` if run on x64 or many arm64 systems. +// * `arch_list` == "x86,x64,-arm64": returns `false` if the underlying host is +// arm64. +// * `arch_list` == "-arm64": returns `false` if the underlying host is arm64. +bool IsArchCompatible(const CString& arch_list) { + std::vector architectures; + int pos = 0; + do { + const CString arch = arch_list.Tokenize(_T(","), pos).Trim().MakeLower(); + if (!arch.IsEmpty()) { + architectures.push_back(arch); + } + } while (pos != -1); + + if (architectures.empty()) { + return true; + } + + std::sort(architectures.begin(), architectures.end()); + if (std::find(architectures.begin(), architectures.end(), + _T('-') + SystemInfo::GetArchitecture().MakeLower()) != + architectures.end()) { + return false; + } + + architectures.erase( + std::remove_if(architectures.begin(), architectures.end(), + [](const CString& arch) { return arch[0] == '-'; }), + architectures.end()); + + return architectures.empty() || + std::find_if(architectures.begin(), architectures.end(), + SystemInfo::IsArchitectureSupported) != + architectures.end(); } bool IsOSVersionCompatible(const CString& min_os_version) { @@ -189,7 +239,7 @@ HRESULT BuildApp(const xml::UpdateResponse* update_response, ASSERT1(response_app); const xml::response::UpdateCheck& update_check = response_app->update_check; - VERIFY1(SUCCEEDED(app->put_ttToken(CComBSTR(update_check.tt_token)))); + VERIFY_SUCCEEDED(app->put_ttToken(CComBSTR(update_check.tt_token))); Cohort cohort; cohort.cohort = response_app->cohort; @@ -211,10 +261,9 @@ HRESULT BuildApp(const xml::UpdateResponse* update_response, for (size_t i = 0; i < update_check.install_manifest.packages.size(); ++i) { const xml::InstallPackage& package( update_check.install_manifest.packages[i]); - FileHash hash; - hash.sha1 = package.hash_sha1; - hash.sha256 = package.hash_sha256; - HRESULT hr = next_version->AddPackage(package.name, package.size, hash); + HRESULT hr = next_version->AddPackage(package.name, + package.size, + package.hash_sha256); if (FAILED(hr)) { return hr; } @@ -266,7 +315,7 @@ xml::UpdateResponseResult GetResult(const xml::UpdateResponse* update_response, if (!response_app) { CORE_LOG(L1, (_T("[UpdateResponse::GetResult][app not found][%s]"), appid)); - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text)); return std::make_pair(GOOPDATE_E_NO_SERVER_RESPONSE, text); } @@ -285,50 +334,50 @@ xml::UpdateResponseResult GetResult(const xml::UpdateResponse* update_response, // noupdate if (_tcsicmp(xml::response::kStatusNoUpdate, status) == 0) { - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text)); return std::make_pair(GOOPDATE_E_NO_UPDATE_RESPONSE, text); } // "restricted" if (_tcsicmp(xml::response::kStatusRestrictedExportCountry, status) == 0) { - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_RESTRICTED_RESPONSE_FROM_SERVER, - &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_RESTRICTED_RESPONSE_FROM_SERVER, + &text)); return std::make_pair(GOOPDATE_E_RESTRICTED_SERVER_RESPONSE, text); } // "error-UnKnownApplication" if (_tcsicmp(xml::response::kStatusUnKnownApplication, status) == 0) { - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text)); return std::make_pair(GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE, text); } // "error-hwnotsupported" if (_tcsicmp(xml::response::kStatusHwNotSupported, status) == 0) { - VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, + VERIFY_SUCCEEDED(formatter.FormatMessage(&text, IDS_HW_NOT_SUPPORTED, - app_name))); + app_name)); return std::make_pair(GOOPDATE_E_HW_NOT_SUPPORTED, text); } // "error-osnotsupported" if (_tcsicmp(xml::response::kStatusOsNotSupported, status) == 0) { - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_OS_NOT_SUPPORTED, &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_OS_NOT_SUPPORTED, &text)); return std::make_pair(GOOPDATE_E_OS_NOT_SUPPORTED, text); } // "error-internal" if (_tcsicmp(xml::response::kStatusInternalError, status) == 0) { - VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, + VERIFY_SUCCEEDED(formatter.FormatMessage(&text, IDS_NON_OK_RESPONSE_FROM_SERVER, - status))); + status)); return std::make_pair(GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE, text); } // "error-hash" if (_tcsicmp(xml::response::kStatusHashError, status) == 0) { - VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, + VERIFY_SUCCEEDED(formatter.FormatMessage(&text, IDS_NON_OK_RESPONSE_FROM_SERVER, - status))); + status)); return std::make_pair(GOOPDATE_E_SERVER_RESPONSE_NO_HASH, text); } @@ -338,16 +387,16 @@ xml::UpdateResponseResult GetResult(const xml::UpdateResponse* update_response, // just the publisher name. If it was a link, we could use point to a // redirect URL and provide the app GUID rather than somehow obtaining the // app-specific URL. - VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, + VERIFY_SUCCEEDED(formatter.FormatMessage(&text, IDS_INSTALLER_OLD, - kShortCompanyName))); + kShortCompanyName)); return std::make_pair(GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL, text); } - VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, + VERIFY_SUCCEEDED(formatter.FormatMessage(&text, IDS_NON_OK_RESPONSE_FROM_SERVER, - status))); + status)); return std::make_pair(GOOPDATE_E_UNKNOWN_SERVER_RESPONSE, text); } @@ -399,7 +448,7 @@ xml::UpdateResponseResult CheckSystemRequirements( return std::make_pair(S_OK, CString()); } - VERIFY1(SUCCEEDED(formatter.LoadString(IDS_OS_NOT_SUPPORTED, &text))); + VERIFY_SUCCEEDED(formatter.LoadString(IDS_OS_NOT_SUPPORTED, &text)); return std::make_pair(GOOPDATE_E_OS_NOT_SUPPORTED, text); } diff --git a/omaha/goopdate/update_response_utils_unittest.cc b/omaha/goopdate/update_response_utils_unittest.cc index 2c6aec342..8ee75bd60 100644 --- a/omaha/goopdate/update_response_utils_unittest.cc +++ b/omaha/goopdate/update_response_utils_unittest.cc @@ -94,7 +94,7 @@ class UpdateResponseUtilsTest : public AppTestBase, protected: UpdateResponseUtilsTest() : AppTestBase(IsMachine(), true) {} - const bool IsMachine() { + bool IsMachine() { return GetParam(); } }; @@ -291,98 +291,46 @@ TEST_F(UpdateResponseUtilsGetResultTest, HwNotSupported) { _T("en"))); } -TEST_F(UpdateResponseUtilsGetResultTest, SystemRequirementsElement_Compatible) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("win"); - sys_req.arch = _T("x86"); - sys_req.min_os_version = _T("6.0"); - - SetResponseForUnitTest(update_response_.get(), response); - - EXPECT_TRUE(kOk == CheckSystemRequirements(update_response_.get(), _T("en"))); -} - -TEST_F(UpdateResponseUtilsGetResultTest, - SystemRequirementsElement_PlatformMismatch) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("mac"); - sys_req.arch = _T("x86"); - sys_req.min_os_version = _T("6.0"); - - SetResponseForUnitTest(update_response_.get(), response); - - EXPECT_TRUE(kOsNotSupported == - CheckSystemRequirements(update_response_.get(), _T("en"))); -} - -TEST_F(UpdateResponseUtilsGetResultTest, - SystemRequirementsElement_ArchUnknown) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("win"); - sys_req.arch = _T("unknown"); - sys_req.min_os_version = _T("6.0"); - - SetResponseForUnitTest(update_response_.get(), response); - - EXPECT_TRUE(kOsNotSupported == - CheckSystemRequirements(update_response_.get(), _T("en"))); -} - -TEST_F(UpdateResponseUtilsGetResultTest, SystemRequirementsElement_Archx86) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("win"); - sys_req.arch = _T("x86"); - sys_req.min_os_version = _T("6.0"); - - SetResponseForUnitTest(update_response_.get(), response); - - EXPECT_TRUE(kOk == CheckSystemRequirements(update_response_.get(), _T("en"))); -} - -TEST_F(UpdateResponseUtilsGetResultTest, SystemRequirementsElement_Archx64) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("win"); - sys_req.arch = _T("x64"); - sys_req.min_os_version = _T("6.0"); - - SetResponseForUnitTest(update_response_.get(), response); - - EXPECT_TRUE( - (SystemInfo::GetProcessorArchitecture() == PROCESSOR_ARCHITECTURE_AMD64 ? - kOk : kOsNotSupported) - == CheckSystemRequirements(update_response_.get(), _T("en"))); -} - -TEST_F(UpdateResponseUtilsGetResultTest, - SystemRequirementsElement_ReallyHighSystemRequirementsVersion) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("win"); - sys_req.arch = _T("x86"); - sys_req.min_os_version = _T("60.0"); - - SetResponseForUnitTest(update_response_.get(), response); - - EXPECT_TRUE(kOsNotSupported == - CheckSystemRequirements(update_response_.get(), _T("en"))); -} - -TEST_F(UpdateResponseUtilsGetResultTest, - SystemRequirementsElement_ReallyLowSystemRequirementsVersion) { - xml::response::Response response; - xml::response::SystemRequirements& sys_req = response.sys_req; - sys_req.platform = _T("win"); - sys_req.arch = _T("x86"); - sys_req.min_os_version = _T("0.01"); - - SetResponseForUnitTest(update_response_.get(), response); +TEST_F(UpdateResponseUtilsGetResultTest, CheckSystemRequirements) { + const CString current_arch = SystemInfo::GetArchitecture(); + + const struct { + const TCHAR* platform; + const TCHAR* arch_list; + const TCHAR* min_os_version; + const xml::UpdateResponseResult& expected_result; + } test_cases[] = { + {_T("win"), _T("x86"), _T("6.0"), kOk}, + {_T("mac"), _T("x86"), _T("6.0"), kOsNotSupported}, + {_T("win"), _T("unknown"), _T("6.0"), kOsNotSupported}, + {_T("win"), _T("x64"), _T("6.0"), + current_arch == kArchAmd64 ? kOk : kOsNotSupported}, + {_T("win"), _T("-x64"), _T("6.0"), + current_arch != kArchAmd64 ? kOk : kOsNotSupported}, + {_T("win"), _T("x86,-x64"), _T("6.0"), + current_arch != kArchAmd64 ? kOk : kOsNotSupported}, + {_T("win"), _T("x86,x64,-arm64"), _T("6.0"), + current_arch != kArchArm64 ? kOk : kOsNotSupported}, + {_T("win"), _T("x86"), _T("60.0"), kOsNotSupported}, + {_T("win"), _T("x86"), _T("0.01"), kOk}, + }; - EXPECT_TRUE(kOk == CheckSystemRequirements(update_response_.get(), _T("en"))); + for (const auto& test_case : test_cases) { + xml::response::Response response; + xml::response::SystemRequirements& sys_req = response.sys_req; + sys_req.platform = test_case.platform; + sys_req.arch = test_case.arch_list; + sys_req.min_os_version = test_case.min_os_version; + + SetResponseForUnitTest(update_response_.get(), response); + + EXPECT_EQ(CheckSystemRequirements(update_response_.get(), _T("en")), + test_case.expected_result) + << test_case.platform << ": " << test_case.arch_list << ": " + << test_case.min_os_version << ": " << current_arch << ": " + << test_case.expected_result.first << ": " + << test_case.expected_result.second; + } } INSTANTIATE_TEST_CASE_P(IsMachine, UpdateResponseUtilsTest, ::testing::Bool()); diff --git a/omaha/goopdate/worker.cc b/omaha/goopdate/worker.cc index b584b245c..b898ff9e5 100644 --- a/omaha/goopdate/worker.cc +++ b/omaha/goopdate/worker.cc @@ -178,7 +178,7 @@ HRESULT AddUninstalledAppsPings(AppBundle* app_bundle) { // AppBundle's lifetime is controlled by the client. Improving the ping // architecture, such as having a ping queue managed by the Worker, may // enable this. - VERIFY1(SUCCEEDED(app_manager.RemoveClientState(app->app_guid()))); + VERIFY_SUCCEEDED(app_manager.RemoveClientState(app->app_guid())); } return S_OK; @@ -409,8 +409,6 @@ void Worker::CollectAmbientUsageStats() { SUCCEEDED(vista_util::IsUACOn(&is_uac_on))) { metric_worker_is_uac_disabled.Set(!is_uac_on); } - - metric_worker_is_clickonce_disabled.Set(IsClickOnceDisabled()); } HRESULT Worker::CheckForUpdateAsync(AppBundle* app_bundle) { @@ -455,8 +453,8 @@ void Worker::CheckForUpdateHelper(AppBundle* app_bundle, *is_check_successful = false; if (ConfigManager::Instance()->CanUseNetwork(is_machine_)) { - VERIFY1(SUCCEEDED(internal::SendOemInstalledPing( - is_machine_, app_bundle->session_id()))); + VERIFY_SUCCEEDED(internal::SendOemInstalledPing( + is_machine_, app_bundle->session_id())); } scoped_impersonation impersonate_user(app_bundle->impersonation_token()); @@ -512,7 +510,7 @@ void Worker::CheckForUpdateHelper(AppBundle* app_bundle, hr); CString event_text; CString url; - VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetUpdateCheckUrl(&url))); + VERIFY_SUCCEEDED(ConfigManager::Instance()->GetUpdateCheckUrl(&url)); SafeCStringFormat(&event_text, _T("url=%s\n%s"), url, app_bundle->FetchAndResetLogText()); @@ -865,8 +863,18 @@ HRESULT Worker::CacheOfflinePackages(AppBundle* app_bundle) { } } - HRESULT hr = download_manager_->CachePackage(package, - &offline_package_path); + File offline_package_file; + HRESULT hr = offline_package_file.OpenShareMode(offline_package_path, + false, + false, + FILE_SHARE_READ); + if (FAILED(hr)) { + return hr; + } + + hr = download_manager_->CachePackage(package, + &offline_package_file, + &offline_package_path); if (FAILED(hr)) { CORE_LOG(LE, (_T("[CachePackage failed][%s][%s][0x%x][%Iu]"), app->app_guid_string(), offline_package_path, hr, j)); @@ -1001,9 +1009,9 @@ void Worker::DoPostUpdateCheck(AppBundle* app_bundle, ASSERT1(app_bundle); ASSERT1(update_response); - VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( + VERIFY_SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas( is_machine_, - update_response))); + update_response)); PersistRetryAfter(app_bundle->update_check_client()->retry_after_sec()); @@ -1018,8 +1026,8 @@ void Worker::DoPostUpdateCheck(AppBundle* app_bundle, } if (app_bundle->is_offline_install()) { - VERIFY1(SUCCEEDED(CacheOfflinePackages(app_bundle))); - VERIFY1(SUCCEEDED(DeleteDirectory(app_bundle->offline_dir()))); + VERIFY_SUCCEEDED(CacheOfflinePackages(app_bundle)); + VERIFY_SUCCEEDED(DeleteDirectory(app_bundle->offline_dir())); } } @@ -1048,19 +1056,21 @@ HRESULT Worker::QueueDeferredFunctionCall0( ASSERT1(app_bundle.get()); ASSERT1(deferred_function); - using Callback = ThreadPoolCallBack1 >; + using Callback = ThreadPoolCallBack1>; auto callback = std::make_unique(this, deferred_function, app_bundle); - HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), + UserWorkItem* user_work_item = callback.get(); + HRESULT hr = Goopdate::Instance().QueueUserWorkItem(std::move(callback), COINIT_MULTITHREADED, WT_EXECUTELONGFUNCTION); if (FAILED(hr)) { return hr; } - // Transfers the ownership of the callback from Worker to ThreadPool. - app_bundle->set_user_work_item(callback.release()); + // This object is owned by the thread pool but the |app_bundle| maintains + // a dependency on it for debugging purposes. + app_bundle->set_user_work_item(user_work_item); return S_OK; } @@ -1079,15 +1089,17 @@ HRESULT Worker::QueueDeferredFunctionCall1( deferred_function, app_bundle, p1); - HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(), + UserWorkItem* user_work_item = callback.get(); + HRESULT hr = Goopdate::Instance().QueueUserWorkItem(std::move(callback), COINIT_MULTITHREADED, WT_EXECUTELONGFUNCTION); if (FAILED(hr)) { return hr; } - // Transfers the ownership of the callback from Worker to ThreadPool. - app_bundle->set_user_work_item(callback.release()); + // This object is owned by the thread pool but the |app_bundle| maintains + // a dependency on it for debugging purposes. + app_bundle->set_user_work_item(user_work_item); return S_OK; } diff --git a/omaha/goopdate/worker_metrics.cc b/omaha/goopdate/worker_metrics.cc index 42cdf29ae..b065565a5 100644 --- a/omaha/goopdate/worker_metrics.cc +++ b/omaha/goopdate/worker_metrics.cc @@ -45,8 +45,6 @@ DEFINE_METRIC_bool(worker_is_windows_installing); DEFINE_METRIC_bool(worker_is_uac_disabled); -DEFINE_METRIC_bool(worker_is_clickonce_disabled); - DEFINE_METRIC_bool(worker_has_software_firewall); DEFINE_METRIC_count(worker_silent_update_running_on_batteries); diff --git a/omaha/goopdate/worker_metrics.h b/omaha/goopdate/worker_metrics.h index aa20cf698..0df985e64 100644 --- a/omaha/goopdate/worker_metrics.h +++ b/omaha/goopdate/worker_metrics.h @@ -73,9 +73,6 @@ DECLARE_METRIC_bool(worker_is_windows_installing); // True if UAC is disabled. DECLARE_METRIC_bool(worker_is_uac_disabled); -// True if ClickOnce is disabled for the Internet zone for the current user. -DECLARE_METRIC_bool(worker_is_clickonce_disabled); - // True if a software firewall is detected. DECLARE_METRIC_bool(worker_has_software_firewall); diff --git a/omaha/goopdate/worker_unittest.cc b/omaha/goopdate/worker_unittest.cc index 0881b3007..06b8089ed 100644 --- a/omaha/goopdate/worker_unittest.cc +++ b/omaha/goopdate/worker_unittest.cc @@ -52,37 +52,31 @@ namespace { const TCHAR* const kGuid1 = _T("{8A001254-1003-465e-A970-0748961C5293}"); const TCHAR* const kGuid2 = _T("{058ADDBE-BF10-4ba1-93C0-6F4A52C03C7E}"); #else -const TCHAR* const kGuid1 = _T("{104844D6-7DDA-460B-89F0-FBF8AFDD0A67}"); +const TCHAR* const kGuid1 = _T("{65E60E95-0DE9-43FF-9F3F-4F7D2DFF04B5}"); const TCHAR* const kGuid2 = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}"); #endif -// The alphabetical order of these is important for -// RecordUpdateAvailableUsageStatsTest. -const TCHAR* const kApp1 = _T("{0C480772-AC73-418f-9603-66303DA4C7AA}"); -const TCHAR* const kApp2 = _T("{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}"); -const TCHAR* const kApp3 = _T("{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}"); - const uint64 kApp1GuidUpper = 0x0C480772AC73418f; const uint64 kApp2GuidUpper = 0x89906BCD4D124c9b; const TCHAR* const kApp1ClientsKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{0C480772-AC73-418f-9603-66303DA4C7AA}"); const TCHAR* const kApp2ClientsKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}"); const TCHAR* const kApp3ClientsKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\Clients\\{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}"); const TCHAR* const kApp1ClientStateKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{0C480772-AC73-418f-9603-66303DA4C7AA}"); const TCHAR* const kApp2ClientStateKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}"); const TCHAR* const kApp3ClientStateKeyPathUser = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\ClientState\\{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}"); void SetAppStateUpdateAvailable(App* app) { @@ -157,8 +151,8 @@ class MockDownloadManager : public DownloadManagerInterface { HRESULT()); MOCK_METHOD2(PurgeAppLowerVersions, HRESULT(const CString&, const CString&)); - MOCK_METHOD2(CachePackage, - HRESULT(const Package*, const CString*)); + MOCK_METHOD3(CachePackage, + HRESULT(const Package*, File*, const CString*)); MOCK_METHOD1(DownloadApp, HRESULT(App* app)); MOCK_METHOD1(DownloadPackage, @@ -424,7 +418,7 @@ TEST_F(WorkerMockedManagersTest, DownloadAsync) { SetAppStateUpdateAvailable(app2_); { - ::testing::InSequence dummy; + ::testing::InSequence order_is_guaranteed; EXPECT_CALL(*mock_download_manager_, DownloadApp(app1_)) .WillOnce(SimulateDownloadAppStateTransition()); EXPECT_CALL(*mock_download_manager_, DownloadApp(app2_)) @@ -457,7 +451,7 @@ TEST_F(WorkerMockedManagersTest, DownloadAndInstallAsync_AlreadyDownloaded) { .WillRepeatedly(Return(app_util::GetTempDir())); { - ::testing::InSequence dummy; + ::testing::InSequence order_is_guaranteed; EXPECT_CALL(*mock_install_manager_, InstallApp(app1_, _)) .WillOnce(SimulateInstallAppStateTransition()); EXPECT_CALL(*mock_install_manager_, InstallApp(app2_, _)) @@ -488,7 +482,7 @@ TEST_F(WorkerMockedManagersTest, DownloadAndInstallAsync_NotAlreadyDownloaded) { .WillRepeatedly(Return(app_util::GetTempDir())); { - ::testing::InSequence dummy; + ::testing::InSequence order_is_guaranteed; EXPECT_CALL(*mock_download_manager_, DownloadApp(app1_)) .WillOnce(SimulateDownloadAppStateTransition()); EXPECT_CALL(*mock_install_manager_, InstallApp(app1_, _)) @@ -520,7 +514,7 @@ TEST_F(WorkerMockedManagersTest, DownloadAsync_Then_DownloadAndInstallAsync) { SetAppStateUpdateAvailable(app2_); { - ::testing::InSequence dummy; + ::testing::InSequence order_is_guaranteed; EXPECT_CALL(*mock_download_manager_, DownloadApp(app1_)) .WillOnce(SimulateDownloadAppStateTransition()); EXPECT_CALL(*mock_download_manager_, DownloadApp(app2_)) @@ -546,7 +540,7 @@ TEST_F(WorkerMockedManagersTest, DownloadAsync_Then_DownloadAndInstallAsync) { .WillRepeatedly(Return(app_util::GetTempDir())); { - ::testing::InSequence dummy; + ::testing::InSequence order_is_guaranteed; EXPECT_CALL(*mock_install_manager_, InstallApp(app1_, _)) .WillOnce(SimulateInstallAppStateTransition()); EXPECT_CALL(*mock_install_manager_, InstallApp(app2_, _)) @@ -805,7 +799,7 @@ class RecordUpdateAvailableUsageStatsTest : public testing::Test { int GetNumProducts() { AppManager& app_manager = *AppManager::Instance(); AppIdVector registered_app_ids; - VERIFY1(SUCCEEDED(app_manager.GetRegisteredApps(®istered_app_ids))); + VERIFY_SUCCEEDED(app_manager.GetRegisteredApps(®istered_app_ids)); return static_cast(registered_app_ids.size()); } diff --git a/omaha/goopdate/worker_utils.cc b/omaha/goopdate/worker_utils.cc index f0105a745..c2bc13c62 100644 --- a/omaha/goopdate/worker_utils.cc +++ b/omaha/goopdate/worker_utils.cc @@ -39,26 +39,26 @@ bool FormatMessageForNetworkError(HRESULT error, switch (error) { case GOOPDATE_E_NO_NETWORK: - VERIFY1(SUCCEEDED(formatter.FormatMessage(msg, + VERIFY_SUCCEEDED(formatter.FormatMessage(msg, IDS_NO_NETWORK_PRESENT_ERROR, - kOmahaShellFileName))); + kOmahaShellFileName)); break; case GOOPDATE_E_NETWORK_UNAUTHORIZED: - VERIFY1(SUCCEEDED( - formatter.LoadString(IDS_ERROR_HTTPSTATUS_UNAUTHORIZED, msg))); + VERIFY_SUCCEEDED( + formatter.LoadString(IDS_ERROR_HTTPSTATUS_UNAUTHORIZED, msg)); break; case GOOPDATE_E_NETWORK_FORBIDDEN: - VERIFY1(SUCCEEDED( - formatter.LoadString(IDS_ERROR_HTTPSTATUS_FORBIDDEN, msg))); + VERIFY_SUCCEEDED( + formatter.LoadString(IDS_ERROR_HTTPSTATUS_FORBIDDEN, msg)); break; case GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED: - VERIFY1(SUCCEEDED( - formatter.LoadString(IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED, msg))); + VERIFY_SUCCEEDED( + formatter.LoadString(IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED, msg)); break; default: - VERIFY1(SUCCEEDED(formatter.FormatMessage(msg, + VERIFY_SUCCEEDED(formatter.FormatMessage(msg, IDS_NO_NETWORK_PRESENT_ERROR, - kOmahaShellFileName))); + kOmahaShellFileName)); return false; } diff --git a/omaha/goopdate/worker_utils_unittest.cc b/omaha/goopdate/worker_utils_unittest.cc index 0c0b08cbe..37e9cb7a1 100644 --- a/omaha/goopdate/worker_utils_unittest.cc +++ b/omaha/goopdate/worker_utils_unittest.cc @@ -37,7 +37,7 @@ TEST(WorkerUtilsTest, FormatMessageForNetworkError) { &message)); EXPECT_STREQ( _T("Unable to connect to the Internet. If you use a firewall, please ") - _T("whitelist GoogleUpdate.exe."), + _T("allowlist ") MAIN_EXE_BASE_NAME _T(".exe."), message); EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NETWORK_UNAUTHORIZED, @@ -68,7 +68,7 @@ TEST(WorkerUtilsTest, FormatMessageForNetworkError) { EXPECT_EQ(false, FormatMessageForNetworkError(E_FAIL, kEnglish, &message)); EXPECT_STREQ( _T("Unable to connect to the Internet. If you use a firewall, please ") - _T("whitelist GoogleUpdate.exe."), + _T("allowlist ") MAIN_EXE_BASE_NAME _T(".exe."), message); ResourceManager::Delete(); diff --git a/omaha/hammer.bat b/omaha/hammer.bat index b839b0ec9..652ba4f4f 100644 --- a/omaha/hammer.bat +++ b/omaha/hammer.bat @@ -1,9 +1,5 @@ @echo off -:: Hammer does not need this variable but the unit -:: tests do. -set OMAHA_PSEXEC_DIR=%ProgramFiles(x86)%\pstools - setlocal rem -- Set all environment variables used by Hammer and Omaha. -- @@ -23,6 +19,7 @@ if "%VisualStudioVersion%"=="12.0" goto vc120 if "%VisualStudioVersion%"=="14.0" goto vc140 if "%VisualStudioVersion%"=="15.0" goto vc141 if "%VisualStudioVersion%"=="16.0" goto vc160 +if "%VisualStudioVersion%"=="17.0" goto vc170 goto error_vc_not_supported :vc120 @@ -41,69 +38,96 @@ goto set_env_variables set OMAHA_MSC_VER=1920 goto set_env_variables +:vc170 +set OMAHA_MSC_VER=1930 +goto set_env_variables :set_env_variables :: Change these variables to match the local build environment. -:: Directory where the Go programming language toolchain is installed. -set GOROOT=C:\go - -:: Directory where AtlServer files are. -set OMAHA_ATL_SERVER_DIR=c:\atl_server\files - -:: This will depend on your OS. If this version of the .Net framework came with -:: the OS, then set it to the framework directory -:: (something like C:\Windows\Microsoft.NET\Framework\v2.0.50727). -:: Otherwise, set it to the directory where the .NET framework is installed. -set OMAHA_NET_DIR=%WINDIR%\Microsoft.NET\Framework\v2.0.50727 +:: Hammer does not need this variable but the unit tests do. +if not defined OMAHA_PSEXEC_DIR ( + set "OMAHA_PSEXEC_DIR=%ProgramFiles(x86)%\pstools" +) +echo OMAHA_PSEXEC_DIR: %OMAHA_PSEXEC_DIR% -:: This directory is needed to find mage.exe tool, which is the .Net manifest -:: generating tool. This tool ships as part of the Windows SDK. -:: However, newer versions of mage.exe can't targer older versions of .Net -:: framework. If there is a need for the click-once application to run on older -:: versions of the .Net framework, then an older version of the Windows SDK -:: needs to be installed and this environment variable point to that directory. -set OMAHA_NETFX_TOOLS_DIR=%WindowsSDK_ExecutablePath_x86% +:: Directory where the Go programming language toolchain is installed. +if not defined GOROOT ( + set "GOROOT=C:\go" +) +echo GOROOT: %GOROOT% :: This directory is needed to find protoc.exe, which is the protocol buffer :: compiler. From the release page https://github.com/google/protobuf/releases, :: download the zip file protoc-$VERSION-win32.zip. It contains the protoc :: binary. Unzip the contents under C:\protobuf. -set OMAHA_PROTOBUF_BIN_DIR=C:\protobuf\bin +if not defined OMAHA_PROTOBUF_BIN_DIR ( + set OMAHA_PROTOBUF_BIN_DIR=C:\protobuf\bin +) +echo OMAHA_PROTOBUF_BIN_DIR: %OMAHA_PROTOBUF_BIN_DIR% :: This directory is needed to find the protocol buffer source files. From the :: release page https://github.com/google/protobuf/releases, download the zip :: file protobuf-cpp-$VERSION.zip. Unzip the "src" sub-directory contents to :: C:\protobuf\src. -set OMAHA_PROTOBUF_SRC_DIR=C:\protobuf\src +if not defined OMAHA_PROTOBUF_SRC_DIR ( + set OMAHA_PROTOBUF_SRC_DIR=C:\protobuf\src +) +echo OMAHA_PROTOBUF_SRC_DIR: %OMAHA_PROTOBUF_SRC_DIR% :: Directory where Python (python.exe) is installed. -set OMAHA_PYTHON_DIR=C:\Python27 +if not defined OMAHA_PYTHON_DIR ( + set OMAHA_PYTHON_DIR=C:\Python27 +) +echo OMAHA_PYTHON_DIR: %OMAHA_PYTHON_DIR% :: Directory in WiX where candle.exe and light.exe are installed. -set OMAHA_WIX_DIR=%WIX%\bin +if not defined OMAHA_WIX_DIR ( + set "OMAHA_WIX_DIR=%WIX%\bin" +) +echo OMAHA_WIX_DIR: %OMAHA_WIX_DIR% :: Root directory of the WTL installation. -set OMAHA_WTL_DIR=C:\wtl\files +if not defined OMAHA_WTL_DIR ( + set OMAHA_WTL_DIR=C:\wtl\files +) +echo OMAHA_WTL_DIR: %OMAHA_WTL_DIR% + +if not defined OMAHA_PLATFORM_SDK_DIR ( + set "OMAHA_PLATFORM_SDK_DIR=%WindowsSdkDir%\" +) +echo OMAHA_PLATFORM_SDK_DIR: %OMAHA_PLATFORM_SDK_DIR% -set OMAHA_PLATFORM_SDK_DIR=%WindowsSdkDir%\ -set OMAHA_WINDOWS_SDK_10_0_VERSION=%WindowsSDKVersion:~0,-1% +if not defined OMAHA_WINDOWS_SDK_10_0_VERSION ( + set "OMAHA_WINDOWS_SDK_10_0_VERSION=%WindowsSDKVersion:~0,-1%" +) +echo OMAHA_WINDOWS_SDK_10_0_VERSION: %OMAHA_WINDOWS_SDK_10_0_VERSION% :: Directory which includes the sign.exe tool for Authenticode signing. -set OMAHA_SIGNTOOL_SDK_DIR="%WindowsSdkVerBinPath%\x86" -set PYTHONPATH=%OMAHA_PYTHON_DIR% +if not defined OMAHA_SIGNTOOL_SDK_DIR ( + set OMAHA_SIGNTOOL_SDK_DIR="%WindowsSdkVerBinPath%\x86" +) +echo OMAHA_SIGNTOOL_SDK_DIR: %OMAHA_SIGNTOOL_SDK_DIR% :: Directory of Scons (http://www.scons.org/). -set SCONS_DIR=C:\Python27\Lib\site-packages\scons-1.3.1 +if not defined SCONS_DIR ( + set "SCONS_DIR=%OMAHA_PYTHON_DIR%\scons-1.3.1" +) +echo SCONS_DIR: %SCONS_DIR% :: Directory of the Google's Software Construction Toolkit. -set SCT_DIR=C:\swtoolkit +if not defined SCT_DIR ( + set SCT_DIR=C:\swtoolkit +) +echo SCT_DIR: %SCT_DIR% -set PROXY_CLSID_TARGET=%~dp0proxy_clsids.txt -set CUSTOMIZATION_UT_TARGET=%~dp0common\omaha_customization_proxy_clsid.h +set "PROXY_CLSID_TARGET=%~dp0proxy_clsids.txt" +set "CUSTOMIZATION_UT_TARGET=%~dp0common\omaha_customization_proxy_clsid.h" rem Force Hammer to use Python 2.7 set PYTHON_TO_USE=python_27 +set "PYTHONPATH=%OMAHA_PYTHON_DIR%" + call "%SCT_DIR%\hammer.bat" %* if /i {%1} == {-c} ( diff --git a/omaha/installers/build.scons b/omaha/installers/build.scons index f7bd6ce1a..21789c65c 100644 --- a/omaha/installers/build.scons +++ b/omaha/installers/build.scons @@ -28,94 +28,6 @@ from installers import build_metainstaller _RECOVERY_MARKUP_DLL_BASE_NAME = 'recovery_markup' _RECOVERY_MARKUP_DLL = _RECOVERY_MARKUP_DLL_BASE_NAME + '.dll' -_CLICKONCE_DEPLOY_DIR = '$TARGET_ROOT/clickonce_deployment' - -# This will be of the form 'GoogleInstaller_en.application'. -def _GetClickOnceDeploymentName(language): - return 'GoogleInstaller_%s.application' % (language) - -# Generate a ClickOnce deployment manifest personalized with the localized -# display name of 'Google Installer'. -def _GenerateDeploymentForOneLanguage(omaha_version_info, language): - clickonce_deployment_name = _GetClickOnceDeploymentName(language) - - clickonce_manifest_name = 'clickonce_bootstrap.exe.manifest' - clickonce_manifest = '%s/%s' % (_CLICKONCE_DEPLOY_DIR, - clickonce_manifest_name) - - # Generate the deployment manifest with a dummy name of 'xxxXXXxxx'. The - # Python commands module does not work with Unicode strings, so we will - # substitute the name in the add_trusturlparams_and_name_command below. - generate_deploy_manifest_command = ( - '@mage -New Deployment -Install false -ToFile $TARGET -Name xxxXXXxxx' - ' -Version %s -Processor x86 -AppManifest $SOURCE -AppCodeBase %s' % - (omaha_version_info.GetVersionString(), clickonce_manifest_name)) - - - # Have to set up a clear chain of source->target1->target2->target3->etc so - # that declarative Hammer will know the order in which to run each command. - clickonce_target_1 = env.Command( - target=clickonce_deployment_name + '.base', - source=clickonce_manifest, - action=generate_deploy_manifest_command, - ) - - # Get the localized 'Google Installer' string. - mi_generated_resource = ( - '$MAIN_DIR/mi_exe_stub/mi_generated_resources_%s.rc' % language) - f_in = codecs.open(env.File(mi_generated_resource).abspath, 'r', 'utf16') - mi_resource_contents = f_in.read() - f_in.close() - - # Get and format strings necessary to generate the display name. - # index() will throw and abort the build if there is no match. - - # First, get the company name. - company_name_start = (mi_resource_contents.index('IDS_FRIENDLY_COMPANY_NAME')) - company_name_start = mi_resource_contents.index('"', company_name_start) - company_name_end = mi_resource_contents.index('"', company_name_start + 1) - # Since it is inserted into the display name, the quotes must be dropped. - company_name = mi_resource_contents[company_name_start + 1:company_name_end] - if -1 != company_name.find('"'): - raise Exception('Slice indexes are incorrect!') - - # Now get the installer display name and replace the placeholder with the - # company name. - display_name_start = ( - mi_resource_contents.index('IDS_INSTALLER_DISPLAY_NAME')) - display_name_start = mi_resource_contents.index('"', display_name_start) - display_name_end = mi_resource_contents.index('"', display_name_start + 1) - display_name = mi_resource_contents[display_name_start:display_name_end + 1] - display_name = display_name.replace('%1!s!', company_name) - - # display_name is utf8 encoded to allow the commands and the default codec to - # pass it through. - display_name = display_name.encode('utf8') - - # mage.exe does not provide a way to add the trustURLParameters attribute to - # an application manifest. This script fills that gap. It also adds in the - # localized display name, to get around issues with the Python commands - # module. - add_trusturlparams_and_name_command = ( - '@python %s --manifest_file=$SOURCE --output_file=$TARGET --display_name=' - '%s' % (env.File('$MAIN_DIR/clickonce/add_trusturlparams.py').abspath, - display_name)) - - # This is the next step in the target chain - clickonce_target_2 = env.Command( - target=clickonce_deployment_name + '.unsigned', - source=clickonce_target_1, - action=add_trusturlparams_and_name_command, - ) - - # Sign the deployment manifest. - # This will be of the form - # 'scons-out\dbg-win\clickonce_deployment\GoogleInstaller_en.application'. - manifest_target = '%s/%s' % (_CLICKONCE_DEPLOY_DIR, clickonce_deployment_name) - env.SignDotNetManifest(manifest_target, clickonce_target_2) - - - def _BuildSetup(omaha_versions_info, is_repair = False): # Build the meta-installer for each version. _PRODUCT_NAME = 'GoogleUpdate' @@ -147,7 +59,7 @@ def _BuildSetupRepairVersion(omaha_version_info, merged_output = env.Command( target='%smi_exe_stub_repair.exe' % (prefix), source=[source_binary, '$OBJ_ROOT/installers/' + _RECOVERY_MARKUP_DLL], - action='@$MAIN_DIR/tools/resmerge --copyappend $SOURCES $TARGET', + action='@"$MAIN_DIR/tools/resmerge" --copyappend $SOURCES $TARGET', ) build_metainstaller.BuildMetaInstaller( @@ -158,9 +70,6 @@ def _BuildSetupRepairVersion(omaha_version_info, omaha_files_path='$STAGING_DIR', prefix = prefix, suffix = '_repair', - additional_payload_contents = [ - '$STAGING_DIR/GoogleUpdateHelperPatch.msp', - ], ) @@ -180,25 +89,6 @@ def _BuildSetupVersion(omaha_version_info, prefix=prefix, ) - # Generate the i18n ClickOnce deployment manifest for languages that we - # support. - if env.Bit('all') or 'OMAHA_BUILD_CLICKONCE' in os.environ.keys(): - for language in omaha_version_info.GetSupportedLanguages(): - _GenerateDeploymentForOneLanguage(omaha_version_info, language) - - # zh-HK needs a deployment file, but it is not in - # omaha_version_info.GetSupportedLanguages() and there is no - # mi_generated_resources_zh-HK.rc file. The few translations are inherited - # from zh-TW, and there are no language code-specific values in the - # deployment file, so just copy the zh-TW file to zh-HK. - env.Command( - target='%s/%s' % (_CLICKONCE_DEPLOY_DIR, - _GetClickOnceDeploymentName('zh-HK')), - source='%s/%s' % (_CLICKONCE_DEPLOY_DIR, - _GetClickOnceDeploymentName('zh-TW')), - action='@copy /y $SOURCES $TARGET' - ) - if not env.Bit('official_installers'): omaha_versions_info = env['omaha_versions_info'] @@ -207,7 +97,6 @@ if not env.Bit('official_installers'): env.Replicate( target=[ - '$TARGET_ROOT/clickonce_deployment/bin/', '$STAGING_DIR', ], source='$OBJ_ROOT/installers/GoogleUpdateSetup.exe', diff --git a/omaha/installers/build_metainstaller.py b/omaha/installers/build_metainstaller.py index d9b507406..bfca64d40 100644 --- a/omaha/installers/build_metainstaller.py +++ b/omaha/installers/build_metainstaller.py @@ -90,7 +90,7 @@ def BuildMetaInstaller( tarball_output = env.Command( target=tarball_filename, # Archive filename source=payload_contents, # List of files to include in tarball - action='python.exe %s -o $TARGET $SOURCES' % ( + action='python.exe "%s" -o $TARGET $SOURCES' % ( env.File(installers_sources_path + '/generate_tarball.py').abspath), ) @@ -104,7 +104,7 @@ def BuildMetaInstaller( bcj_output = env.Command( target=bcj_filename, source=tarball_output, - action='%s "$SOURCES" "$TARGET"' % bcj2_path, + action='"%s" "$SOURCES" "$TARGET"' % bcj2_path, ) env.Depends(bcj_output, bcj2_path) @@ -116,12 +116,12 @@ def BuildMetaInstaller( lzma_output = lzma_env.Command( target=payload_filename, source=bcj_output, - action='%s e $SOURCES $TARGET $LZMAFLAGS' % lzma_path, + action='"%s" e $SOURCES $TARGET $LZMAFLAGS' % lzma_path, ) # Construct the resource generation script manifest_path = installers_sources_path + '/installers.manifest' - res_command = 'python.exe %s -i %s -o $TARGET -p $SOURCES -m %s -r %s' % ( + res_command = 'python.exe "%s" -i "%s" -o $TARGET -p $SOURCES -m "%s" -r "%s"' % ( env.File(installers_sources_path + '/generate_resource_script.py' ).abspath, env.File(installers_sources_path + '/resource.rc.in').abspath, @@ -166,16 +166,15 @@ def BuildMetaInstaller( merged_output = env.Command( target='unsigned_' + target_name, source=[empty_metainstaller_path, dll_output_name], - action= '%s --copyappend $SOURCES $TARGET' % resmerge_path - ) + action='"%s" --copyappend $SOURCES $TARGET' % resmerge_path) authenticode_signed_target_prefix = 'authenticode_' - authenticode_signed_exe = env.DualSignedBinary( + authenticode_signed_exe = env.SignedBinary( target=authenticode_signed_target_prefix + target_name, source=merged_output, - ) + ) - ready_for_tagging_exe = env.OmahaCertificateTagExe( + ready_for_tagging_exe = env.OmahaCertificateTag( target=target_name, source=authenticode_signed_exe, ) diff --git a/omaha/installers/generate_tarball.py b/omaha/installers/generate_tarball.py index be77045d6..6753622d4 100644 --- a/omaha/installers/generate_tarball.py +++ b/omaha/installers/generate_tarball.py @@ -19,9 +19,18 @@ import os.path import sys import tarfile -import urllib - -TEST_PREFIX = 'TEST_' +# NOTE: Normally this would use something like six.moves.urllib to handle the +# py2/py3 differences, but that's not available in the scons-based Omaha build +# environment (and it's probably not worth figuring out how to add it, so this +# is legacy Omaha stuff which is being replaced), so just hack in compatibility. +try: + # py3 + from urllib.parse import unquote as url_unquote +except ImportError: + # py2 + from urllib import unquote as url_unquote + +TEST_PREFIXES = ('TEST_', 'TEST2_') def GenerateTarball(output_filename, members): """ @@ -32,9 +41,9 @@ def GenerateTarball(output_filename, members): for filename in members: # A hacky convention to get around the spaces in filenames is to # urlencode them. So at this point we unescape those characters. - scrubbed_filename = urllib.unquote(os.path.basename(filename)) - if scrubbed_filename.startswith(TEST_PREFIX): - scrubbed_filename = scrubbed_filename[len(TEST_PREFIX):] + scrubbed_filename = url_unquote(os.path.basename(filename)) + if scrubbed_filename.startswith(TEST_PREFIXES): + scrubbed_filename = scrubbed_filename.split('_', 1)[1] tarball.add(filename, scrubbed_filename) tarball.close() diff --git a/omaha/installers/resource_only_dll.def b/omaha/installers/resource_only_dll.def index 078c46b80..51c6f0b0d 100644 --- a/omaha/installers/resource_only_dll.def +++ b/omaha/installers/resource_only_dll.def @@ -13,4 +13,4 @@ ; limitations under the License. ; ======================================================================== ; -; dummy def file to create implib +; def file to create implib diff --git a/omaha/installers/tag_meta_installers.py b/omaha/installers/tag_meta_installers.py index 30c7e9e37..57ce9c387 100644 --- a/omaha/installers/tag_meta_installers.py +++ b/omaha/installers/tag_meta_installers.py @@ -54,6 +54,23 @@ def UrlEncodeString(name): utf8_str = name.encode('utf8') return urllib.quote(utf8_str) +def ReadLinesFromFile(txt_filename): + """Read all lines from a file encoded as utf8 or utf16. + Args: + txt_filename: A text file. + + Returns: + A list of string. + """ + try: + installers_txt_file = codecs.open(txt_filename, 'r', 'utf16') + return installers_txt_file.readlines(); + except UnicodeError: + installers_txt_file = codecs.open(txt_filename) + return installers_txt_file.readlines(); + except: + return [] + def ReadBundleInstallerFile(installers_txt_filename): """Read the installation file and return a list of the values. Only reads information from bundle installer files. The filename @@ -67,8 +84,7 @@ def ReadBundleInstallerFile(installers_txt_filename): A dictionary of Bundles key=language, value=[Bundle] """ bundles = {} - installers_txt_file = codecs.open(installers_txt_filename, 'r', 'utf16') - for line in installers_txt_file.readlines(): + for line in ReadLinesFromFile(installers_txt_filename): line = line.strip() if len(line) and not line.startswith('#'): (exe_name, needs_admin, language, browser, usage, diff --git a/omaha/internal/chrome_recovery_improved/README.md b/omaha/internal/chrome_recovery_improved/README.md new file mode 100644 index 000000000..64dcdd404 --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/README.md @@ -0,0 +1,9 @@ +DO NOT TRY TO REUSE THIS CODE BEFORE CHANGING THE SYMBOLS +SPECIFIC TO GOOGLE CHROME AND GOOGLE UPDATER. + +These files don't build as part of this source tree and they are provided for +reference purposes. + +This module builds the ChromeRecovery binary, which is distributed through +the component updater in Chrome. This binary repairs the updater when +certain conditions are met. diff --git a/omaha/internal/chrome_recovery_improved/build.scons b/omaha/internal/chrome_recovery_improved/build.scons new file mode 100644 index 000000000..227d01bca --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/build.scons @@ -0,0 +1,109 @@ +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +Import('env') + +exe_env = env.Clone() +omaha_version_info = exe_env['omaha_versions_info'][0] +exe_env.Append( + CCFLAGS = [ + '/wd4548', + ], + LIBS = [ + 'advapi32.lib', + 'bits.lib', + 'comctl32.lib', + 'crypt32.lib', + 'delayimp.lib', + 'imagehlp.lib', + 'iphlpapi.lib', + 'kernel32.lib', + 'msimg32.lib', + 'mstask.lib', + 'netapi32.lib', + 'ole32.lib', + 'psapi.lib', + 'rpcns4.lib', + 'rpcrt4.lib', + 'shell32.lib', + 'shlwapi.lib', + 'taskschd.lib', + 'user32.lib', + 'userenv.lib', + 'uxtheme.lib', + 'version.lib', + 'wininet.lib', + 'wintrust.lib', + 'ws2_32.lib', + 'wtsapi32.lib', + exe_env['atls_libs'][exe_env.Bit('debug')], + exe_env['crt_libs'][exe_env.Bit('debug')], + exe_env.GetMultiarchLibName('base'), + exe_env.GetMultiarchLibName('breakpad'), + exe_env.GetMultiarchLibName('client'), + exe_env.GetMultiarchLibName('common'), + exe_env.GetMultiarchLibName('core'), + exe_env.GetMultiarchLibName('crash_handler'), + exe_env.GetMultiarchLibName('crx_file'), + exe_env.GetMultiarchLibName('google_update_recovery'), + exe_env.GetMultiarchLibName('goopdate_lib'), + exe_env.GetMultiarchLibName('libprotobuf'), + exe_env.GetMultiarchLibName('logging'), + exe_env.GetMultiarchLibName('net'), + exe_env.GetMultiarchLibName('omaha3_idl'), + exe_env.GetMultiarchLibName('security'), + exe_env.GetMultiarchLibName('service'), + exe_env.GetMultiarchLibName('setup'), + exe_env.GetMultiarchLibName('statsreport'), + exe_env.GetMultiarchLibName('ui'), + ], + RCFLAGS = [ + '/DVERSION_MAJOR=%d' % omaha_version_info.version_major, + '/DVERSION_MINOR=%d' % omaha_version_info.version_minor, + '/DVERSION_BUILD=%d' % omaha_version_info.version_build, + '/DVERSION_PATCH=%d' % omaha_version_info.version_patch, + '/DVERSION_NUMBER_STRING=\\"%s\\"' % ( + omaha_version_info.GetVersionString()), + ], +) +if exe_env.Bit('has_device_management'): + exe_env.Append( + LIBS = [ + exe_env.GetMultiarchLibName('dm_proto'), + ], + ) + +exe_res = exe_env.RES(source = 'resource.rc', + target = 'resource.res') +exe_env.Depends(exe_res, '../../google_update/GoogleUpdate.manifest') + +exe_inputs = [ + 'command_line.cc', + 'recovery.cc', + 'winmain.cc', + exe_res, + ] +exe_name = 'ChromeRecoveryImproved' +unsigned_exe = exe_env.ComponentProgram( + prog_name='%s_unsigned.exe' % exe_name, + source=exe_inputs, +) +signed_exe = exe_env.SignedBinary( + target='%s.exe' % exe_name, + source=unsigned_exe, +) + +env.Replicate('$STAGING_DIR', signed_exe) + diff --git a/omaha/internal/chrome_recovery_improved/command_line.cc b/omaha/internal/chrome_recovery_improved/command_line.cc new file mode 100644 index 000000000..928cef416 --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/command_line.cc @@ -0,0 +1,404 @@ +// Copyright 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "omaha/internal/chrome_recovery_improved/command_line.h" + +#include +#include + +#include + +#include "base/basictypes.h" +#include "base/string.h" +#include "omaha/base/debug.h" + +namespace omaha { + +namespace { + +#define WHITESPACE_UNICODE \ + 0x0009, /* CHARACTER TABULATION */ \ + 0x000A, /* LINE FEED (LF) */ \ + 0x000B, /* LINE TABULATION */ \ + 0x000C, /* FORM FEED (FF) */ \ + 0x000D, /* CARRIAGE RETURN (CR) */ \ + 0x0020, /* SPACE */ \ + 0x0085, /* NEXT LINE (NEL) */ \ + 0x00A0, /* NO-BREAK SPACE */ \ + 0x1680, /* OGHAM SPACE MARK */ \ + 0x2000, /* EN QUAD */ \ + 0x2001, /* EM QUAD */ \ + 0x2002, /* EN SPACE */ \ + 0x2003, /* EM SPACE */ \ + 0x2004, /* THREE-PER-EM SPACE */ \ + 0x2005, /* FOUR-PER-EM SPACE */ \ + 0x2006, /* SIX-PER-EM SPACE */ \ + 0x2007, /* FIGURE SPACE */ \ + 0x2008, /* PUNCTUATION SPACE */ \ + 0x2009, /* THIN SPACE */ \ + 0x200A, /* HAIR SPACE */ \ + 0x2028, /* LINE SEPARATOR */ \ + 0x2029, /* PARAGRAPH SEPARATOR */ \ + 0x202F, /* NARROW NO-BREAK SPACE */ \ + 0x205F, /* MEDIUM MATHEMATICAL SPACE */ \ + 0x3000, /* IDEOGRAPHIC SPACE */ \ + 0 + +const wchar_t kWhitespaceWide[] = { + WHITESPACE_UNICODE +}; + +const wchar_t kSwitchTerminator[] = L"--"; +const wchar_t kSwitchValueSeparator[] = L"="; + +// Since we use a lazy match, make sure that longer versions (like "--") are +// listed before shorter versions (like "-") of similar prefixes. + +// By putting slash last, we can control whether it is treaded as a switch +// value by changing the value of switch_prefix_count to be one less than +// the array size. +const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; + +const size_t switch_prefix_count = arraysize(kSwitchPrefixes); + +size_t GetSwitchPrefixLength(const std::wstring& s) { + for (size_t i = 0; i < switch_prefix_count; ++i) { + const std::wstring prefix(kSwitchPrefixes[i]); + if (s.compare(0, prefix.length(), prefix) == 0) + return prefix.length(); + } + return 0; +} + +// Fills in |switch_string| and |switch_value| if |string| is a switch. +// This will preserve the input switch prefix in the output |switch_string|. +bool IsSwitch(const std::wstring& string, + std::wstring* switch_string, + std::wstring* switch_value) { + switch_string->clear(); + switch_value->clear(); + const size_t prefix_length = GetSwitchPrefixLength(string); + if (prefix_length == 0 || prefix_length == string.length()) + return false; + + const size_t equals_position = string.find(kSwitchValueSeparator); + *switch_string = string.substr(0, equals_position); + if (equals_position != std::wstring::npos) + *switch_value = string.substr(equals_position + 1); + return true; +} + +bool TrimWhitespace(const std::wstring& input, std::wstring* output) { + if (input.empty()) { + output->clear(); + return false; + } + const std::wstring::size_type left = input.find_first_not_of(kWhitespaceWide); + const std::wstring::size_type right = input.find_last_not_of(kWhitespaceWide); + + if (left == std::wstring::npos || right == std::wstring::npos) { + output->clear(); + return true; + } + + *output = input.substr(left, right - left + 1); + return true; +} + +// Append switches and arguments, keeping switches before arguments. +void AppendSwitchesAndArguments(CommandLine* command_line, + const CommandLine::StringVector& argv) { + bool parse_switches = true; + for (size_t i = 1; i < argv.size(); ++i) { + std::wstring arg = argv[i]; + + TrimWhitespace(arg, &arg); + + std::wstring switch_string; + std::wstring switch_value; + parse_switches &= (arg != kSwitchTerminator); + if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { + command_line->AppendSwitch(switch_string, switch_value); + } else { + command_line->AppendArg(arg); + } + } +} + +// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. +std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg, + bool quote_placeholders) { + // We follow the quoting rules of CommandLineToArgvW. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + std::wstring quotable_chars(L" \\\""); + // We may also be required to quote '%', which is commonly used in a command + // line as a placeholder. (It may be substituted for a string with spaces.) + if (quote_placeholders) + quotable_chars.push_back(L'%'); + if (arg.find_first_of(quotable_chars) == std::wstring::npos) { + // No quoting necessary. + return arg; + } + + std::wstring out; + out.push_back(L'"'); + for (size_t i = 0; i < arg.size(); ++i) { + if (arg[i] == '\\') { + // Find the extent of this run of backslashes. + size_t start = i, end = start + 1; + for (; end < arg.size() && arg[end] == '\\'; ++end) {} + size_t backslash_count = end - start; + + // Backslashes are escapes only if the run is followed by a double quote. + // Since we also will end the string with a double quote, we escape for + // either a double quote or the end of the string. + if (end == arg.size() || arg[end] == '"') { + // To quote, we need to output 2x as many backslashes. + backslash_count *= 2; + } + for (size_t j = 0; j < backslash_count; ++j) + out.push_back('\\'); + + // Advance i to one before the end to balance i++ in loop. + i = end - 1; + } else if (arg[i] == '"') { + out.push_back('\\'); + out.push_back('"'); + } else { + out.push_back(arg[i]); + } + } + out.push_back('"'); + + return out; +} + +} // namespace + + +CommandLine* CommandLine::current_process_commandline_ = NULL; + +CommandLine::CommandLine(NoProgram /*no_program*/) + : argv_(1), + begin_args_(1) { +} + +CommandLine::CommandLine(const std::wstring& program) + : argv_(1), + begin_args_(1) { + SetProgram(program); +} + +CommandLine::CommandLine(int argc, const wchar_t* const* argv) + : argv_(1), + begin_args_(1) { + InitFromArgv(argc, argv); +} + +CommandLine::CommandLine(const StringVector& argv) + : argv_(1), + begin_args_(1) { + InitFromArgv(argv); +} + +CommandLine::~CommandLine() { +} + +bool CommandLine::Init(int /*argc*/, const wchar_t* const* /*argv*/) { + if (current_process_commandline_) { + // If this is intentional, Reset() must be called first. If we are using + // the shared build mode, we have to share a single object across multiple + // shared libraries. + return false; + } + + current_process_commandline_ = new CommandLine(NO_PROGRAM); + current_process_commandline_->ParseFromString(::GetCommandLineW()); + + return true; +} + +void CommandLine::Reset() { + ASSERT1(current_process_commandline_); + delete current_process_commandline_; + current_process_commandline_ = NULL; +} + +CommandLine* CommandLine::ForCurrentProcess() { + ASSERT1(current_process_commandline_); + return current_process_commandline_; +} + + +bool CommandLine::InitializedForCurrentProcess() { + return !!current_process_commandline_; +} + +CommandLine CommandLine::FromString(const std::wstring& command_line) { + CommandLine cmd(NO_PROGRAM); + cmd.ParseFromString(command_line); + return cmd; +} + +void CommandLine::InitFromArgv(int argc, + const wchar_t* const* argv) { + StringVector new_argv; + for (int i = 0; i < argc; ++i) + new_argv.push_back(argv[i]); + InitFromArgv(new_argv); +} + +void CommandLine::InitFromArgv(const StringVector& argv) { + argv_ = StringVector(1); + switches_.clear(); + begin_args_ = 1; + SetProgram(argv.empty() ? std::wstring() : std::wstring(argv[0])); + AppendSwitchesAndArguments(this, argv); +} + +std::wstring CommandLine::GetProgram() const { + return std::wstring(argv_[0]); +} + +void CommandLine::SetProgram(const std::wstring& program) { + TrimWhitespace(program, &argv_[0]); +} + +bool CommandLine::HasSwitch(const std::wstring& switch_string) const { + return switches_.find(switch_string) != switches_.end(); +} + +bool CommandLine::HasSwitch(const wchar_t switch_constant[]) const { + return HasSwitch(std::wstring(switch_constant)); +} + +std::wstring CommandLine::GetSwitchValue( + const std::wstring& switch_string) const { + SwitchMap::const_iterator it = switches_.find(switch_string); + return it != switches_.end() ? it->second : std::wstring(); +} + +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + AppendSwitch(switch_string, std::wstring()); +} + +void CommandLine::AppendSwitch(const std::wstring& switch_string, + const std::wstring& value) { + std::wstring switch_key = std::wstring( + CString(switch_string.c_str()).MakeLower()); + + std::wstring combined_switch_string(switch_key); + size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); + std::pair insertion = + switches_.insert(make_pair(switch_key.substr(prefix_length), value)); + if (!insertion.second) + insertion.first->second = value; + // Preserve existing switch prefixes in |argv_|; only append one if necessary. + if (prefix_length == 0) + combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; + if (!value.empty()) + combined_switch_string += kSwitchValueSeparator + value; + // Append the switch and update the switches/arguments divider |begin_args_|. + argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); +} + +void CommandLine::CopySwitchesFrom(const CommandLine& source, + const wchar_t* const switches[], + size_t count) { + for (size_t i = 0; i < count; ++i) { + if (source.HasSwitch(switches[i])) + AppendSwitch(switches[i], source.GetSwitchValue(switches[i])); + } +} + +CommandLine::StringVector CommandLine::GetArgs() const { + // Gather all arguments after the last switch (may include kSwitchTerminator). + StringVector args(argv_.begin() + begin_args_, argv_.end()); + // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) + StringVector::iterator switch_terminator = + std::find(args.begin(), args.end(), kSwitchTerminator); + if (switch_terminator != args.end()) + args.erase(switch_terminator); + return args; +} + +void CommandLine::AppendArg(const std::wstring& value) { + argv_.push_back(value); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + if (include_program) + SetProgram(other.GetProgram()); + AppendSwitchesAndArguments(this, other.argv()); +} + +void CommandLine::ParseFromString(const std::wstring& command_line) { + std::wstring command_line_string; + TrimWhitespace(command_line, &command_line_string); + if (command_line_string.empty()) + return; + + int num_args = 0; + wchar_t** args = NULL; + args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); + + InitFromArgv(num_args, args); + ::LocalFree(args); +} + +std::wstring CommandLine::GetCommandLineStringInternal( + bool quote_placeholders) const { + std::wstring string(argv_[0]); + string = QuoteForCommandLineToArgvW(string, quote_placeholders); + std::wstring params(GetArgumentsStringInternal(quote_placeholders)); + if (!params.empty()) { + string.append(L" "); + string.append(params); + } + return string; +} + +std::wstring CommandLine::GetArgumentsStringInternal( + bool quote_placeholders) const { + std::wstring params; + // Append switches and arguments. + bool parse_switches = true; + for (size_t i = 1; i < argv_.size(); ++i) { + std::wstring arg = argv_[i]; + std::wstring switch_string; + std::wstring switch_value; + parse_switches &= arg != kSwitchTerminator; + if (i > 1) + params.append(L" "); + if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { + params.append(switch_string); + if (!switch_value.empty()) { + switch_value = + QuoteForCommandLineToArgvW(switch_value, quote_placeholders); + params.append(kSwitchValueSeparator + switch_value); + } + } else { + arg = QuoteForCommandLineToArgvW(arg, quote_placeholders); + params.append(arg); + } + } + return params; +} + +} // namespace omaha diff --git a/omaha/internal/chrome_recovery_improved/command_line.h b/omaha/internal/chrome_recovery_improved/command_line.h new file mode 100644 index 000000000..0aabcba57 --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/command_line.h @@ -0,0 +1,195 @@ +// Copyright 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This class works with command lines: building and parsing. +// Arguments with prefixes ('--', '-', and on Windows, '/') are switches. +// Switches will precede all other arguments without switch prefixes. +// Switches can optionally have values, delimited by '=', e.g., "-switch=value". +// An argument of "--" will terminate switch parsing during initialization, +// interpreting subsequent tokens as non-switch arguments, regardless of prefix. + +// There is a singleton read-only CommandLine that represents the command line +// that the current process was started with. It must be initialized in main(). + +#ifndef OMAHA_INTERNAL_CHROME_RECOVERY_IMPROVED_COMMAND_LINE_H_ +#define OMAHA_INTERNAL_CHROME_RECOVERY_IMPROVED_COMMAND_LINE_H_ + +#include +#include +#include +#include + +namespace omaha { + +class CommandLine { + public: + using StringVector = std::vector; + using SwitchMap = std::map; + + // A constructor for CommandLines that only carry switches and arguments. + enum NoProgram { NO_PROGRAM }; + explicit CommandLine(NoProgram no_program); + + // Construct a new command line with |program| as argv[0]. + explicit CommandLine(const std::wstring& program); + + // Construct a new command line from an argument list. + CommandLine(int argc, const wchar_t* const* argv); + explicit CommandLine(const StringVector& argv); + + ~CommandLine(); + + // Initialize the current process CommandLine singleton. On Windows, ignores + // its arguments (we instead parse GetCommandLineW() directly) because we + // don't trust the CRT's parsing of the command line, but it still must be + // called to set up the command line. Returns false if initialization has + // already occurred, and true otherwise. Only the caller receiving a 'true' + // return value should take responsibility for calling Reset. + static bool Init(int argc, const wchar_t* const* argv); + + // Destroys the current process CommandLine singleton. This is necessary if + // you want to reset the base library to its initial state (for example, in an + // outer library that needs to be able to terminate, and be re-initialized). + // If Init is called only once, as in main(), Reset() is not necessary. + static void Reset(); + + // Get the singleton CommandLine representing the current process's + // command line. Note: returned value is mutable, but not thread safe; + // only mutate if you know what you're doing! + static CommandLine* ForCurrentProcess(); + + // Returns true if the CommandLine has been initialized for the given process. + static bool InitializedForCurrentProcess(); + + static CommandLine FromString(const std::wstring& command_line); + + // Initialize from an argv vector. + void InitFromArgv(int argc, const wchar_t* const* argv); + void InitFromArgv(const StringVector& argv); + + // Constructs and returns the represented command line string. + std::wstring GetCommandLineString() const { + return GetCommandLineStringInternal(false); + } + + // Constructs and returns the represented command line string. Assumes the + // command line contains placeholders (eg, %1) and quotes any program or + // argument with a '%' in it. This should be avoided unless the placeholder is + // required by an external interface (eg, the Windows registry), because it is + // not generally safe to replace it with an arbitrary string. If possible, + // placeholders should be replaced *before* converting the command line to a + // string. + std::wstring GetCommandLineStringWithPlaceholders() const { + return GetCommandLineStringInternal(true); + } + + // Constructs and returns the represented arguments string. + std::wstring GetArgumentsString() const { + return GetArgumentsStringInternal(false); + } + + // Constructs and returns the represented arguments string. Assumes the + // command line contains placeholders (eg, %1) and quotes any argument with a + // '%' in it. This should be avoided unless the placeholder is required by an + // external interface (eg, the Windows registry), because it is not generally + // safe to replace it with an arbitrary string. If possible, placeholders + // should be replaced *before* converting the arguments to a string. + std::wstring GetArgumentsStringWithPlaceholders() const { + return GetArgumentsStringInternal(true); + } + + // Returns the original command line string as a vector of strings. + const StringVector& argv() const { return argv_; } + + // Get and Set the program part of the command line string (the first item). + std::wstring GetProgram() const; + void SetProgram(const std::wstring& program); + + // Returns true if this command line contains the given switch. + // Switch names must be lowercase. + bool HasSwitch(const std::wstring& switch_string) const; + bool HasSwitch(const wchar_t switch_constant[]) const; + + // Returns the value associated with the given switch. If the switch has no + // value or isn't present, this method returns the empty string. + // Switch names must be lowercase. + std::wstring GetSwitchValue(const std::wstring& switch_string) const; + + // Get a copy of all switches, along with their values. + const SwitchMap& GetSwitches() const { return switches_; } + + // Append a switch [with optional value] to the command line. + // Note: Switches will precede arguments regardless of appending order. + void AppendSwitch(const std::wstring& switch_string); + void AppendSwitch(const std::wstring& switch_string, + const std::wstring& value); + + // Copy a set of switches (and any values) from another command line. + // Commonly used when launching a subprocess. + void CopySwitchesFrom(const CommandLine& source, + const wchar_t* const switches[], + size_t count); + + // Get the remaining arguments to the command. + StringVector GetArgs() const; + + // Append an argument to the command line. Note that the argument is quoted + // properly such that it is interpreted as one argument to the target command. + // Note: Switches will precede arguments regardless of appending order. + void AppendArg(const std::wstring& value); + + // Append the switches and arguments from another command line to this one. + // If |include_program| is true, include |other|'s program as well. + void AppendArguments(const CommandLine& other, bool include_program); + + // Initialize by parsing the given command line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const std::wstring& command_line); + + private: + // Disallow default constructor; a program name must be explicitly specified. + CommandLine(); + // Allow the copy constructor. A common pattern is to copy of the current + // process's command line and then add some flags to it. For example: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); + + // Internal version of GetCommandLineString. If |quote_placeholders| is true, + // also quotes parts with '%' in them. + std::wstring GetCommandLineStringInternal(bool quote_placeholders) const; + + // Internal version of GetArgumentsString. If |quote_placeholders| is true, + // also quotes parts with '%' in them. + std::wstring GetArgumentsStringInternal(bool quote_placeholders) const; + + // The singleton CommandLine representing the current process's command line. + static CommandLine* current_process_commandline_; + + // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* } + StringVector argv_; + + // Parsed-out switch keys and values. + SwitchMap switches_; + + // The index after the program and switches, any arguments start here. + size_t begin_args_; +}; + +} // namespace omaha + +#endif // OMAHA_INTERNAL_CHROME_RECOVERY_IMPROVED_COMMAND_LINE_H_ diff --git a/omaha/internal/chrome_recovery_improved/command_line_unittest.cc b/omaha/internal/chrome_recovery_improved/command_line_unittest.cc new file mode 100644 index 000000000..1dafbbb55 --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/command_line_unittest.cc @@ -0,0 +1,372 @@ +// Copyright 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "omaha/internal/chrome_recovery_improved/command_line.h" + +#include +#include +#include + +#include "base/basictypes.h" +#include "omaha/testing/unit_test.h" + +namespace omaha { + +// To test Windows quoting behavior, we use a string that has some backslashes +// and quotes. +// Consider the command-line argument: q\"bs1\bs2\\bs3q\\\" +static const std::wstring kTrickyQuoted = L"q\\\"bs1\\bs2\\\\bs3q\\\\\\\""; +// It should be parsed by Windows as: q"bs1\bs2\\bs3q\" +// Here that is with C-style escapes. +static const std::wstring kTricky = L"q\"bs1\\bs2\\\\bs3q\\\""; + +TEST(CommandLineTest, CommandLineConstructor) { + const wchar_t* argv[] = { + L"program", + L"--foo=", + L"-bAr", + L"-spaetzel=pierogi", + L"-baz", + L"flim", + L"--other-switches=--dog=canine --cat=feline", + L"-spaetzle=Crepe", + L"-=loosevalue", + L"-", + L"FLAN", + L"a", + L"--input-translation=45--output-rotation", + L"--", + L"--", + L"--not-a-switch", + L"\"in the time of submarines...\"", + L"unquoted arg-with-space"}; + CommandLine cl(arraysize(argv), argv); + + EXPECT_FALSE(cl.GetCommandLineString().empty()); + EXPECT_FALSE(cl.HasSwitch(L"cruller")); + EXPECT_FALSE(cl.HasSwitch(L"flim")); + EXPECT_FALSE(cl.HasSwitch(L"program")); + EXPECT_FALSE(cl.HasSwitch(L"dog")); + EXPECT_FALSE(cl.HasSwitch(L"cat")); + EXPECT_FALSE(cl.HasSwitch(L"output-rotation")); + EXPECT_FALSE(cl.HasSwitch(L"not-a-switch")); + EXPECT_FALSE(cl.HasSwitch(L"--")); + + EXPECT_EQ(L"program", cl.GetProgram()); + + EXPECT_TRUE(cl.HasSwitch(L"foo")); + EXPECT_TRUE(cl.HasSwitch(L"bar")); + EXPECT_TRUE(cl.HasSwitch(L"baz")); + EXPECT_TRUE(cl.HasSwitch(L"spaetzle")); + EXPECT_TRUE(cl.HasSwitch(L"other-switches")); + EXPECT_TRUE(cl.HasSwitch(L"input-translation")); + + EXPECT_EQ(L"Crepe", cl.GetSwitchValue(L"spaetzle")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"foo")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"bar")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"cruller")); + EXPECT_EQ(L"--dog=canine --cat=feline", cl.GetSwitchValue( + L"other-switches")); + EXPECT_EQ(L"45--output-rotation", cl.GetSwitchValue(L"input-translation")); + + const CommandLine::StringVector& args = cl.GetArgs(); + ASSERT_EQ(8U, args.size()); + + std::vector::const_iterator iter = args.begin(); + EXPECT_EQ(L"flim", *iter); + ++iter; + EXPECT_EQ(L"-", *iter); + ++iter; + EXPECT_EQ(L"FLAN", *iter); + ++iter; + EXPECT_EQ(L"a", *iter); + ++iter; + EXPECT_EQ(L"--", *iter); + ++iter; + EXPECT_EQ(L"--not-a-switch", *iter); + ++iter; + EXPECT_EQ(L"\"in the time of submarines...\"", *iter); + ++iter; + EXPECT_EQ(L"unquoted arg-with-space", *iter); + ++iter; + EXPECT_TRUE(iter == args.end()); +} + +TEST(CommandLineTest, CommandLineFromString) { + CommandLine cl = CommandLine::FromString( + L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " + L"--other-switches=\"--dog=canine --cat=feline\" " + L"-spaetzle=Crepe -=loosevalue FLAN " + L"--input-translation=\"45\"--output-rotation " + L"--quotes=" + kTrickyQuoted + L" " + L"-- -- --not-a-switch " + L"\"in the time of submarines...\""); + + EXPECT_FALSE(cl.GetCommandLineString().empty()); + EXPECT_FALSE(cl.HasSwitch(L"cruller")); + EXPECT_FALSE(cl.HasSwitch(L"flim")); + EXPECT_FALSE(cl.HasSwitch(L"program")); + EXPECT_FALSE(cl.HasSwitch(L"dog")); + EXPECT_FALSE(cl.HasSwitch(L"cat")); + EXPECT_FALSE(cl.HasSwitch(L"output-rotation")); + EXPECT_FALSE(cl.HasSwitch(L"not-a-switch")); + EXPECT_FALSE(cl.HasSwitch(L"--")); + + EXPECT_EQ(L"program", cl.GetProgram()); + + EXPECT_TRUE(cl.HasSwitch(L"foo")); + EXPECT_TRUE(cl.HasSwitch(L"bar")); + EXPECT_TRUE(cl.HasSwitch(L"baz")); + EXPECT_TRUE(cl.HasSwitch(L"spaetzle")); + EXPECT_TRUE(cl.HasSwitch(L"other-switches")); + EXPECT_TRUE(cl.HasSwitch(L"input-translation")); + EXPECT_TRUE(cl.HasSwitch(L"quotes")); + + EXPECT_EQ(L"Crepe", cl.GetSwitchValue(L"spaetzle")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"foo")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"bar")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"cruller")); + EXPECT_EQ(L"--dog=canine --cat=feline", cl.GetSwitchValue( + L"other-switches")); + EXPECT_EQ(L"45--output-rotation", cl.GetSwitchValue(L"input-translation")); + EXPECT_EQ(kTricky, cl.GetSwitchValue(L"quotes")); + + const CommandLine::StringVector& args = cl.GetArgs(); + ASSERT_EQ(5U, args.size()); + + std::vector::const_iterator iter = args.begin(); + EXPECT_EQ(L"flim", *iter); + ++iter; + EXPECT_EQ(L"FLAN", *iter); + ++iter; + EXPECT_EQ(L"--", *iter); + ++iter; + EXPECT_EQ(L"--not-a-switch", *iter); + ++iter; + EXPECT_EQ(L"in the time of submarines...", *iter); + ++iter; + EXPECT_TRUE(iter == args.end()); + + // Check that a generated string produces an equivalent command line. + CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString()); + EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString()); +} + +// Tests behavior with an empty input string. +TEST(CommandLineTest, EmptyString) { + CommandLine cl_from_string = CommandLine::FromString(L""); + EXPECT_TRUE(cl_from_string.GetCommandLineString().empty()); + EXPECT_TRUE(cl_from_string.GetProgram().empty()); + EXPECT_EQ(1U, cl_from_string.argv().size()); + EXPECT_TRUE(cl_from_string.GetArgs().empty()); + CommandLine cl_from_argv(0, NULL); + EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty()); + EXPECT_TRUE(cl_from_argv.GetProgram().empty()); + EXPECT_EQ(1U, cl_from_argv.argv().size()); + EXPECT_TRUE(cl_from_argv.GetArgs().empty()); +} + +TEST(CommandLineTest, GetArgumentsString) { + static const wchar_t kPath1[] = + L"C:\\Some File\\With Spaces.ggg"; + static const wchar_t kPath2[] = + L"C:\\no\\spaces.ggg"; + + static const wchar_t kFirstArgName[] = L"first-arg"; + static const wchar_t kSecondArgName[] = L"arg2"; + static const wchar_t kThirdArgName[] = L"arg with space"; + static const wchar_t kFourthArgName[] = L"nospace"; + static const wchar_t kFifthArgName[] = L"%1"; + + CommandLine cl(CommandLine::NO_PROGRAM); + cl.AppendSwitch(kFirstArgName, kPath1); + cl.AppendSwitch(kSecondArgName, kPath2); + cl.AppendArg(kThirdArgName); + cl.AppendArg(kFourthArgName); + cl.AppendArg(kFifthArgName); + + std::wstring expected_first_arg(kFirstArgName); + std::wstring expected_second_arg(kSecondArgName); + std::wstring expected_third_arg(kThirdArgName); + std::wstring expected_fourth_arg(kFourthArgName); + std::wstring expected_fifth_arg(kFifthArgName); + +#define QUOTE_ON_WIN L"\"" + + std::wstring expected_str; + expected_str.append(L"--") + .append(expected_first_arg) + .append(L"=") + .append(QUOTE_ON_WIN) + .append(kPath1) + .append(QUOTE_ON_WIN) + .append(L" ") + .append(L"--") + .append(expected_second_arg) + .append(L"=") + .append(QUOTE_ON_WIN) + .append(kPath2) + .append(QUOTE_ON_WIN) + .append(L" ") + .append(QUOTE_ON_WIN) + .append(expected_third_arg) + .append(QUOTE_ON_WIN) + .append(L" ") + .append(expected_fourth_arg) + .append(L" "); + + std::wstring expected_str_no_quote_placeholders(expected_str); + expected_str_no_quote_placeholders.append(expected_fifth_arg); + EXPECT_EQ(expected_str_no_quote_placeholders, cl.GetArgumentsString()); + + std::wstring expected_str_quote_placeholders(expected_str); + expected_str_quote_placeholders.append(QUOTE_ON_WIN) + .append(expected_fifth_arg) + .append(QUOTE_ON_WIN); + EXPECT_EQ(expected_str_quote_placeholders, + cl.GetArgumentsStringWithPlaceholders()); +} + +// Test methods for appending switches to a command line. +TEST(CommandLineTest, AppendSwitches) { + std::wstring switch1 = L"switch1"; + std::wstring switch2 = L"switch2"; + std::wstring value2 = L"value"; + std::wstring switch3 = L"switch3"; + std::wstring value3 = L"a value with spaces"; + std::wstring switch4 = L"switch4"; + std::wstring value4 = L"\"a value with quotes\""; + std::wstring switch5 = L"quotes"; + std::wstring value5 = kTricky; + + CommandLine cl(L"Program"); + + cl.AppendSwitch(switch1); + cl.AppendSwitch(switch2, value2); + cl.AppendSwitch(switch3, value3); + cl.AppendSwitch(switch4, value4); + cl.AppendSwitch(switch5, value4); + cl.AppendSwitch(switch5, value5); + + EXPECT_TRUE(cl.HasSwitch(switch1)); + EXPECT_TRUE(cl.HasSwitch(switch2)); + EXPECT_EQ(value2, cl.GetSwitchValue(switch2)); + EXPECT_TRUE(cl.HasSwitch(switch3)); + EXPECT_EQ(value3, cl.GetSwitchValue(switch3)); + EXPECT_TRUE(cl.HasSwitch(switch4)); + EXPECT_EQ(value4, cl.GetSwitchValue(switch4)); + EXPECT_TRUE(cl.HasSwitch(switch5)); + EXPECT_EQ(value5, cl.GetSwitchValue(switch5)); + + EXPECT_EQ(L"Program " + L"--switch1 " + L"--switch2=value " + L"--switch3=\"a value with spaces\" " + L"--switch4=\"\\\"a value with quotes\\\"\" " + // Even though the switches are unique, appending can add repeat + // switches to argv. + L"--quotes=\"\\\"a value with quotes\\\"\" " + L"--quotes=\"" + kTrickyQuoted + L"\"", + cl.GetCommandLineString()); +} + +TEST(CommandLineTest, AppendSwitchesDashDash) { + const wchar_t* raw_argv[] = { L"prog", + L"--", + L"--arg1" }; + CommandLine cl(arraysize(raw_argv), raw_argv); + + cl.AppendSwitch(L"switch1"); + cl.AppendSwitch(L"switch2", L"foo"); + + cl.AppendArg(L"--arg2"); + + EXPECT_EQ(L"prog --switch1 --switch2=foo -- --arg1 --arg2", + cl.GetCommandLineString()); + CommandLine::StringVector cl_argv = cl.argv(); + EXPECT_EQ(L"prog", cl_argv[0]); + EXPECT_EQ(L"--switch1", cl_argv[1]); + EXPECT_EQ(L"--switch2=foo", cl_argv[2]); + EXPECT_EQ(L"--", cl_argv[3]); + EXPECT_EQ(L"--arg1", cl_argv[4]); + EXPECT_EQ(L"--arg2", cl_argv[5]); +} + +// Tests that when AppendArguments is called that the program is set correctly +// on the target CommandLine object and the switches from the source +// CommandLine are added to the target. +TEST(CommandLineTest, AppendArguments) { + CommandLine cl1(L"Program"); + cl1.AppendSwitch(L"switch1"); + cl1.AppendSwitch(L"switch2", L"foo"); + + CommandLine cl2(CommandLine::NO_PROGRAM); + cl2.AppendArguments(cl1, true); + EXPECT_EQ(cl1.GetProgram(), cl2.GetProgram()); + EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString()); + + CommandLine c1(L"Program1"); + c1.AppendSwitch(L"switch1"); + CommandLine c2(L"Program2"); + c2.AppendSwitch(L"switch2"); + + c1.AppendArguments(c2, true); + EXPECT_EQ(c1.GetProgram(), c2.GetProgram()); + EXPECT_TRUE(c1.HasSwitch(L"switch1")); + EXPECT_TRUE(c1.HasSwitch(L"switch2")); +} + +// Make sure that the command line string program paths are quoted as necessary. +// This only makes sense on Windows and the test is basically here to guard +// against regressions. +TEST(CommandLineTest, ProgramQuotes) { + // Check that quotes are not added for paths without spaces. + const std::wstring kProgram(L"Program"); + CommandLine cl_program(kProgram); + EXPECT_EQ(kProgram, cl_program.GetProgram()); + EXPECT_EQ(kProgram, cl_program.GetCommandLineString()); + + const std::wstring kProgramPath(L"Program Path"); + + // Check that quotes are not returned from GetProgram(). + CommandLine cl_program_path(kProgramPath); + EXPECT_EQ(kProgramPath, cl_program_path.GetProgram()); + + // Check that quotes are added to command line string paths containing spaces. + std::wstring cmd_string(cl_program_path.GetCommandLineString()); + EXPECT_EQ(L"\"Program Path\"", cmd_string); + + // Check the optional quoting of placeholders in programs. + CommandLine cl_quote_placeholder(L"%1"); + EXPECT_EQ(L"%1", cl_quote_placeholder.GetCommandLineString()); + EXPECT_EQ(L"\"%1\"", + cl_quote_placeholder.GetCommandLineStringWithPlaceholders()); +} + +// Calling Init multiple times should not modify the previous CommandLine. +TEST(CommandLineTest, Init) { + // Call Init without checking output once so we know it's been called + // whether or not the test runner does so. + CommandLine::Init(0, NULL); + CommandLine* initial = CommandLine::ForCurrentProcess(); + EXPECT_FALSE(CommandLine::Init(0, NULL)); + CommandLine* current = CommandLine::ForCurrentProcess(); + EXPECT_EQ(initial, current); +} + +} // namespace omaha diff --git a/omaha/internal/chrome_recovery_improved/recovery.cc b/omaha/internal/chrome_recovery_improved/recovery.cc new file mode 100644 index 000000000..3de011b6a --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/recovery.cc @@ -0,0 +1,339 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +#include "omaha/internal/chrome_recovery_improved/recovery.h" + +#include "omaha/base/app_util.h" +#include "omaha/base/debug.h" +#include "omaha/base/error.h" +#include "omaha/base/file.h" +#include "omaha/base/logging.h" +#include "omaha/base/reg_key.h" +#include "omaha/base/safe_format.h" +#include "omaha/base/system.h" +#include "omaha/base/time.h" +#include "omaha/common/app_registry_utils.h" +#include "omaha/common/command_line_builder.h" +#include "omaha/common/const_cmd_line.h" +#include "omaha/common/const_goopdate.h" +#include "omaha/common/const_group_policy.h" +#include "omaha/common/experiment_labels.h" +#include "omaha/common/goopdate_utils.h" +#include "omaha/common/ping.h" +#include "omaha/third_party/smartany/scoped_any.h" + +namespace omaha { + +const TCHAR ChromeRecoveryImproved::kChromeName[] = _T("Google Chrome"); +const TCHAR ChromeRecoveryImproved::kLongLabelName[] = _T("chromerec3"); +const TCHAR ChromeRecoveryImproved::kExtraLabelName[] = _T("chromrec3extra"); + +const int ChromeRecoveryImproved::kPingResult_NoRecovery = 0; +const int ChromeRecoveryImproved::kPingResult_RecoverySuccessful = 2; + +ChromeRecoveryImproved::ChromeRecoveryImproved(bool is_machine, + const CString& browser_guid, + const CString& browser_version, + const CString& session_id) + : is_machine_(is_machine), + browser_guid_(browser_guid), + browser_version_(browser_version), + session_id_(session_id), + omaha_install_error_code_(0) {} + +RecoveryResultCode ChromeRecoveryImproved::Repair() { + const HRESULT hr = DoRepair(); + + CString experiment_label; + SafeCStringFormat(&experiment_label, + _T("%6d%s"), + GetRunCohort(), + SUCCEEDED(hr) ? _T("R") : _T("F")); + const int result_code = SUCCEEDED(hr) ? + kPingResult_RecoverySuccessful : kPingResult_NoRecovery; + const int error_code = static_cast(hr); + const int extra_code = static_cast(omaha_install_error_code_); + + SetExperimentLabel(experiment_label); + SendResultPing(result_code, error_code, extra_code); + + return SUCCEEDED(hr) ? RECOVERY_SUCCEEDED : RECOVERY_NO_RECOVERY; +} + +HRESULT ChromeRecoveryImproved::DoRepair() { + ResetInstallState(); + + HRESULT hr = RestoreChromeRegEntries(); + if (FAILED(hr)) { + OPT_LOG(L1, (_T("[RestoreChromeRegEntries failed][%#x]"), hr)); + return hr; + } + + hr = InstallOmaha(); + if (FAILED(hr)) { + OPT_LOG(L1, (_T("[InstallOmaha failed][%#x]"), hr)); + return hr; + } + + + VERIFY_SUCCEEDED(TriggerUpdateCheck()); + + return S_OK; +} + +void ChromeRecoveryImproved::ResetInstallState() { + ResetChromeInstallState(); + ResetOmahaInstallState(); +} + +void ChromeRecoveryImproved::ResetChromeInstallState() { + // Nothing to do for now. +} + +void ChromeRecoveryImproved::ResetOmahaInstallState() { + // Omaha installer checks 'pv' value to see whether Omaha already exists, + // and skips installation if existing version is higher. + // Delete 'pv' in case the broken existing Omaha blocks new install. + const CString state_key = + app_registry_utils::GetAppClientsKey(is_machine_, GOOPDATE_APP_ID); + RegKey::DeleteValue(state_key, kRegValueProductVersion); +} + +HRESULT ChromeRecoveryImproved::RestoreChromeRegEntries() { + OPT_LOG(L1, (_T("[RestoreChromeRegEntries][%d][%s][%s]"), + is_machine_, browser_guid_, browser_version_)); + + // Create a minimal Clients / ClientState. The Chrome installer + // recreates the remaining state when it runs at some point in the future. + HRESULT hr = BuildChromeClientKey(); + if (FAILED(hr)) { + return hr; + } + + hr = BuildChromeClientStateKey(); + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +HRESULT ChromeRecoveryImproved::BuildChromeClientKey() { + const CString kClientsKey = + app_registry_utils::GetAppClientsKey(is_machine_, browser_guid_); + + RegKey key; + HRESULT hr = key.Create(kClientsKey); + if (FAILED(hr)) { + return hr; + } + + hr = key.SetValue(kRegValueProductVersion, browser_version_); + if (FAILED(hr)) { + return hr; + } + + key.SetValue(kRegValueAppName, kChromeName); + + return S_OK; +} + +HRESULT ChromeRecoveryImproved::BuildChromeClientStateKey() { + const CString kClientStateKey = + app_registry_utils::GetAppClientStateKey(is_machine_, browser_guid_); + + RegKey key; + HRESULT hr = key.Create(kClientStateKey); + if (FAILED(hr)) { + return hr; + } + + key.SetValue(kRegValueAdditionalParams, _T("")); + key.SetValue(kRegValueBrandCode, kDefaultGoogleUpdateBrandCode); + + return S_OK; +} + +HRESULT ChromeRecoveryImproved::InstallOmaha() { + CPath metainstaller_path(app_util::GetCurrentModuleDirectory()); + VERIFY1(metainstaller_path.Append(kOmahaMetainstallerFileName)); + if (!File::Exists(metainstaller_path)) { + OPT_LOG(LE, (_T("[InstallOmaha][couldn't find metainstaller]"))); + return GOOPDATE_E_METAINSTALLER_NOT_FOUND; + } + + // Generate arguments for a silent runtime install. + CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL); + builder.set_is_silent_set(true); + builder.set_install_source(kCmdLineInstallSource_ChromeRecovery); + CString extra_args; + SafeCStringFormat(&extra_args, + _T("runtime=true&needsadmin=%s"), + is_machine_ ? _T("true") : _T("false")); + builder.set_extra_args(extra_args); + const CString cmd_line_args = builder.GetCommandLineArgs(); + + // Start the process and wait for it to exit. + scoped_process install_process; + HRESULT hr = LaunchProcess(metainstaller_path, + cmd_line_args, + address(install_process)); + if (FAILED(hr)) { + OPT_LOG(LE, (_T("[InstallOmaha][couldn't start MI][%s][%s][%#x]"), + metainstaller_path, cmd_line_args, hr)); + return hr; + } + + OPT_LOG(L1, (_T("[InstallOmaha][launched MI, waiting for exit code]"))); + + DWORD error_code = 0; + hr = WaitAndGetExitCode(get(install_process), &error_code); + OPT_LOG(L1, (_T("[InstallOmaha][finished][%#x][%#x]"), hr, error_code)); + + omaha_install_error_code_ = error_code; + + return SUCCEEDED(hr) ? error_code : hr; +} + +HRESULT ChromeRecoveryImproved::SetLabelOnApp(const CString& label_value, + const CString& app_id) { + const time64 k90Days100ns = static_cast(kDaysTo100ns) * 90; + const time64 now = GetCurrent100NSTime(); + + CString label(ExperimentLabels::CreateLabel( + kLongLabelName, label_value, now + k90Days100ns)); + return ExperimentLabels::WriteRegistry(is_machine_, app_id, label); +} + +HRESULT ChromeRecoveryImproved::TriggerUpdateCheck() { + OPT_LOG(L1, (_T("[TriggerUpdateCheck][is_machine: %d]"), is_machine_)); + + // TODO(sorin): consider deleting LastChecked. + + CommandLineBuilder builder(COMMANDLINE_MODE_UA); + builder.set_is_machine_set(is_machine_); + builder.set_install_source(kCmdLineInstallSource_ChromeRecovery); + const CString cmd_line_args = builder.GetCommandLineArgs(); + + HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_, + StartMode::kBackground, + cmd_line_args, + NULL); + if (FAILED(hr)) { + OPT_LOG(LE, (_T("[couldn't start UA][%s][%#x]"), cmd_line_args, hr)); + } + + return hr; +} + + +HRESULT ChromeRecoveryImproved::SendResultPing( + int result_code, + int error_code, + int extra_code) { + OPT_LOG(L1, (_T("[SendResultPing][%d][%d/%d/%d]"), + is_machine_, result_code, error_code, extra_code)); + + CString omaha_pv; + app_registry_utils::GetAppVersion(is_machine_, kGoogleUpdateAppId, &omaha_pv); + + Ping ping(is_machine_, session_id_, kCmdLineInstallSource_ChromeRecovery); + PingEventPtr ping_event( + new PingEvent(PingEvent::EVENT_CHROME_RECOVERY_COMPONENT, + static_cast(result_code), + error_code, + extra_code)); + ping.LoadOmahaDataFromRegistry(); + + ping.BuildOmahaPing(omaha_pv, omaha_pv, ping_event); + return SendReliablePing(&ping, false); +} + +int ChromeRecoveryImproved::GetRunCohort() { + const time64 kNow = GetCurrent100NSTime(); + + SYSTEMTIME st = Time64ToSystemTime(kNow); + const int year = static_cast(st.wYear); + + // Compute the ordinal week count (typically 1-52; will be 53 for 12/31 on + // a leap year). On failure, use zero for a week. + // + // This algorithm does not need to be particularly precise wrt the + // Gregorian calendar, nor locale-specific. It just needs to operate the + // same on all machines for the purpose of getting cohort groups. + int week = 0; + if (st.wYear != 0) { + st.wMonth = 1; + st.wDay = 1; + st.wHour = 0; + st.wMinute = 0; + st.wSecond = 0; + st.wMilliseconds = 0; + time64 year_start = SystemTimeToTime64(&st); + if (year_start != 0) { + week = static_cast(((kNow - year_start) / kDaysTo100ns) / 7) + 1; + } + } + + return 100 * year + week; +} + +HRESULT ChromeRecoveryImproved::LaunchProcess(const CString& exe_path, + const CString& args, + HANDLE* process_out) { + PROCESS_INFORMATION pi = {0}; + + HRESULT hr = System::StartProcessWithArgsAndInfo(exe_path, args, &pi); + if (FAILED(hr)) { + CORE_LOG(LE, (_T("[LaunchProcess][StartProcessWithArgsAndInfo][%#x]"), + hr)); + return hr; + } + + ASSERT1(pi.hProcess); + VERIFY1(::CloseHandle(pi.hThread)); + + *process_out = pi.hProcess; + return hr; +} + +HRESULT ChromeRecoveryImproved::WaitAndGetExitCode(HANDLE process, + DWORD* exit_code) { + ASSERT1(exit_code); + + if (::WaitForSingleObject(process, INFINITE) == WAIT_FAILED) { + const DWORD error = ::GetLastError(); + CORE_LOG(LE, (_T("[WaitAndGetExitCode][WaitForSingleObject failed][%u]"), + error)); + return HRESULT_FROM_WIN32(error); + } + + if (!::GetExitCodeProcess(process, exit_code)) { + const DWORD error = ::GetLastError(); + CORE_LOG(LE, (_T("[WaitAndGetExitCode][GetExitCodeProcess failed][%u]"), + error)); + return HRESULT_FROM_WIN32(error); + } + + return S_OK; +} + +void ChromeRecoveryImproved::SetExperimentLabel(const CString& label) { + OPT_LOG(L1, (_T("[SetExperimentLabel][%s]"), label)); + VERIFY_SUCCEEDED(SetLabelOnApp(label, kGoogleUpdateAppId)); + VERIFY_SUCCEEDED(SetLabelOnApp(label, browser_guid_)); +} + +} // namespace omaha diff --git a/omaha/internal/chrome_recovery_improved/recovery.h b/omaha/internal/chrome_recovery_improved/recovery.h new file mode 100644 index 000000000..b2f33bd8a --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/recovery.h @@ -0,0 +1,101 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +#ifndef OMAHA_INTERNAL_CHROME_RECOVERY_IMPROVED_RECOVERY_H_ +#define OMAHA_INTERNAL_CHROME_RECOVERY_IMPROVED_RECOVERY_H_ + +#include +#include + +#include "base/basictypes.h" + +namespace omaha { + +enum RecoveryResultCode { + RECOVERY_SUCCEEDED, + RECOVERY_NO_RECOVERY, + RECOVERY_NEEDS_ADMIN, +}; + +class ChromeRecoveryImproved { + public: + ChromeRecoveryImproved(bool is_machine, + const CString& browser_guid, + const CString& browser_version, + const CString& session_id); + + RecoveryResultCode Repair(); + + private: + // Cleans up the machine by resetting Chrome and Omaha install state for + // upcoming recovery with best effort. This is to reduce the possibility of + // existing (broken) Omaha or Chrome from affecting a new install. + void ResetInstallState(); + void ResetChromeInstallState(); + void ResetOmahaInstallState(); + + // Synthesizes Clients and ClientState entries for Chrome. + HRESULT RestoreChromeRegEntries(); + HRESULT BuildChromeClientKey(); + HRESULT BuildChromeClientStateKey(); + + // Sets a long-lived experiment label on Omaha, for Omaha and Chrome browser. + void SetExperimentLabel(const CString& label); + HRESULT SetLabelOnApp(const CString& label_value, + const CString& app_id); + + // Installs Omaha in runtime mode. + HRESULT InstallOmaha(); + + // Sends a result ping. + HRESULT SendResultPing(int result_code, + int error_code, + int extra_code); + + // Triggers an update check with Omaha. + HRESULT TriggerUpdateCheck(); + + HRESULT DoRepair(); + + // Returns an integer representing the cohort encoded as YYYYWW where WW is + // the week the year. + static int GetRunCohort(); + + static HRESULT LaunchProcess(const CString& exe_path, + const CString& args, + HANDLE* process_out); + static HRESULT WaitAndGetExitCode(HANDLE process, DWORD* exit_code); + + const bool is_machine_; + const CString browser_guid_; + const CString browser_version_; + const CString session_id_; + + // The error code returned by the Omaha metainstaller. + DWORD omaha_install_error_code_; + + static const TCHAR kChromeName[]; + static const TCHAR kLongLabelName[]; + static const TCHAR kExtraLabelName[]; + + static const int kPingResult_NoRecovery; + static const int kPingResult_RecoverySuccessful; + + DISALLOW_COPY_AND_ASSIGN(ChromeRecoveryImproved); +}; + +} // namespace omaha + +#endif // OMAHA_INTERNAL_CHROME_RECOVERY_IMPROVED_RECOVERY_H_ diff --git a/omaha/plugins/plugin_version.rc b/omaha/internal/chrome_recovery_improved/resource.rc similarity index 68% rename from omaha/plugins/plugin_version.rc rename to omaha/internal/chrome_recovery_improved/resource.rc index 15bfcd1ad..e7c1b0a18 100644 --- a/omaha/plugins/plugin_version.rc +++ b/omaha/internal/chrome_recovery_improved/resource.rc @@ -1,4 +1,4 @@ -// Copyright 2007 Google Inc. +// Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,20 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -// -// This file contains the unlocalized plugin version resources. #include +#include +#include +#include "../../google_update/resource.h" -// The "040904e4" block (for codepage 1252 Ansi Latin) is required for -// the Netscape Plugin stuff to see the MIMETYPE value in the resource block. +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) -// TODO(omaha): FileDescription is what shows up in "about:plugins" for -// Mozilla. Format the description to include the url of the project. -// Update: July 2010. I'm not sure why this comment requests the URL. We should -// include the version, though, since that is used by version-checking websites. +1 RT_MANIFEST "../../google_update/GoogleUpdate.manifest" -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP ICON "../../google_update/google_update.ico" VS_VERSION_INFO VERSIONINFO // Resource Editor does not handle constants from main.scons. @@ -44,22 +44,34 @@ VS_VERSION_INFO VERSIONINFO FILEFLAGS 0x0L #endif FILEOS VOS_NT_WINDOWS32 - FILETYPE VFT_DLL + FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "040904e4" + BLOCK "040904b0" BEGIN // Requires constants from mains.scons that cannot be loaded in Resource Editor. #ifndef APSTUDIO_INVOKED + + // Strings that get localized must appear as literals so the tools can replace + // them. For open source builds, which don't use these tools, use constants. + #ifdef GOOGLE_UPDATE_BUILD + VALUE "CompanyName", "Google LLC" + VALUE "FileDescription", "Google Update Recovery Probe" + #else VALUE "CompanyName", FULL_COMPANY_NAME_ANSI VALUE "FileDescription", OMAHA_APP_NAME_ANSI + #endif VALUE "FileVersion", VERSION_NUMBER_STRING VALUE "InternalName", OMAHA_APP_NAME_ANSI - VALUE "LegalCopyright", OMAHA_COPYRIGHT_STRING_ENGLISH - VALUE "OriginalFilename", PLUGIN_FILENAME + VALUE "LegalCopyright", "Copyright 2018 Google LLC" + VALUE "OriginalFilename", MAIN_EXE_BASE_NAME_ANSI ".exe" + #ifdef GOOGLE_UPDATE_BUILD + VALUE "ProductName", "Google Update" + #else VALUE "ProductName", OMAHA_APP_NAME_ANSI + #endif VALUE "ProductVersion", VERSION_NUMBER_STRING #ifdef _DEBUG VALUE "Debug", "" @@ -68,9 +80,6 @@ BEGIN VALUE "PrivateBuild", BUILD_NUMBER #endif - #ifdef MERGED_MIME_TYPE - VALUE "MIMEType", MERGED_MIME_TYPE - #endif #else VALUE "_SpecialView", "Most values are not shown in Resource Editor because they " @@ -80,6 +89,6 @@ BEGIN END BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x409, 1252 + VALUE "Translation", 0x0409, 1200 END END diff --git a/omaha/internal/chrome_recovery_improved/winmain.cc b/omaha/internal/chrome_recovery_improved/winmain.cc new file mode 100644 index 000000000..9714f11e5 --- /dev/null +++ b/omaha/internal/chrome_recovery_improved/winmain.cc @@ -0,0 +1,108 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// Probe that gets delivered to machines via Chrome's recovery component. + +#include + +#include + +#include "omaha/base/const_object_names.h" +#include "omaha/base/debug.h" +#include "omaha/base/error.h" +#include "omaha/base/logging.h" +#include "omaha/base/omaha_version.h" +#include "base/program_instance.h" +#include "omaha/base/utils.h" +#include "omaha/common/command_line.h" +#include "omaha/common/crash_utils.h" +#include "omaha/common/exception_handler.h" +#include "omaha/common/goopdate_utils.h" +#include "omaha/internal/chrome_recovery_improved/command_line.h" +#include "omaha/internal/chrome_recovery_improved/recovery.h" +#include "omaha/net/network_config.h" +#include "omaha/third_party/smartany/scoped_any.h" + +namespace { + +// Called by operator new or operator new[] when they cannot satisfy a request +// for additional storage. +void OutOfMemoryHandler() { + ::RaiseException(EXCEPTION_ACCESS_VIOLATION, + EXCEPTION_NONCONTINUABLE, + 0, + NULL); +} + +} // namespace + +namespace omaha { + +int ChromeRecoveryImprovedMain() { + OPT_LOG(L3, (_T("[ChromeRecoveryMain]"))); + + // Initialize the command line for this process. + CommandLine::Init(0, NULL); + const CommandLine* cl = CommandLine::ForCurrentProcess(); + ASSERT1(cl); + OPT_LOG(L3, (_T("[command line][%s]"), cl->GetCommandLineString().c_str())); + + const bool is_machine = cl->HasSwitch(_T("system")); + const CString app_guid = cl->GetSwitchValue(_T("appguid")).c_str(); + const CString browser_version = cl->GetSwitchValue( + _T("browser-version")).c_str(); + const CString session_id = cl->GetSwitchValue(_T("sessionid")).c_str(); + + CustomInfoMap custom_info_map; + CString command_line_mode; + SafeCStringFormat(&command_line_mode, _T("%d"), COMMANDLINE_MODE_RECOVER); + custom_info_map[kCrashCustomInfoCommandLineMode] = command_line_mode; + + std::unique_ptr crash_handler; + VERIFY_SUCCEEDED(OmahaExceptionHandler::Create(is_machine, + custom_info_map, + &crash_handler)); + NamedObjectAttributes attrs; + GetNamedObjectAttributes(kRecoveryProbeSingleInstance, is_machine, &attrs); + ProgramInstance instance(attrs.name); + const bool is_already_running = !instance.EnsureSingleInstance(); + if (is_already_running) { + OPT_LOG(L1, (_T("[Another recovery probe is already running]"))); + return GOOPDATE_E_PROBE_ALREADY_RUNNING; + } + + // Initialize the network. + NetworkConfigManager::set_is_machine(is_machine); + NetworkConfigManager::Instance(); + + return omaha::ChromeRecoveryImproved(is_machine, + app_guid, + browser_version, + session_id).Repair(); +} + +} // namespace omaha + +int WINAPI _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int) { + omaha::EnableSecureDllLoading(); + + // Install an error-handling mechanism which gets called when new operator + // fails to allocate memory. + VERIFY1(set_new_handler(&OutOfMemoryHandler) == 0); + + omaha::InitializeShellVersion(); + omaha::InitializeVersionFromModule(instance); + + return omaha::ChromeRecoveryImprovedMain(); +} diff --git a/omaha/internal/grit/README.md b/omaha/internal/grit/README.md index 6d445d5b2..dfafc716e 100644 --- a/omaha/internal/grit/README.md +++ b/omaha/internal/grit/README.md @@ -1,5 +1,5 @@ -The GRIT source files here are provided for reference purposes or for developers -who would like to change the UI strings, then use GRIT to localize the +The GRIT source files here are provided for reference purposes or for developers +who would like to change the UI strings, then use GRIT to localize the resource files. These files don't build as part of this source tree. diff --git a/omaha/internal/grit/goopdateres.grd b/omaha/internal/grit/goopdateres.grd index a59bda3a1..f2855625a 100644 --- a/omaha/internal/grit/goopdateres.grd +++ b/omaha/internal/grit/goopdateres.grd @@ -189,7 +189,7 @@ - Unable to connect to the Internet. If you use a firewall, please whitelist %1!s!GoogleUpdate.exe. + Unable to connect to the Internet. If you use a firewall, please allowlist %1!s!GoogleUpdate.exe. diff --git a/omaha/internal/tools/update3web_demo.js b/omaha/internal/tools/update3web_demo.js new file mode 100644 index 000000000..6db209d7d --- /dev/null +++ b/omaha/internal/tools/update3web_demo.js @@ -0,0 +1,407 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +// DISCLAIMER: This code is provided as a reference implementation only, to +// demonstrate how Omaha COM interfaces may be scripted. Since this script is +// not used for anything, it is not maintained, and it could break at any time. + +// Operations. +var CHECK_FOR_UPDATE = 0; +var DOWNLOAD = 1; +var INSTALL = 2; +var UPDATE = 3; +var LAUNCH_COMMAND = 4; + +// The following states need to be kept in sync with the CurrentState enum in +// omaha3_idl.idl. +var STATE_INIT = 1; +var STATE_WAITING_TO_CHECK_FOR_UPDATE = STATE_INIT + 1; +var STATE_CHECKING_FOR_UPDATE = STATE_WAITING_TO_CHECK_FOR_UPDATE + 1; +var STATE_UPDATE_AVAILABLE = STATE_CHECKING_FOR_UPDATE + 1; +var STATE_WAITING_TO_DOWNLOAD = STATE_UPDATE_AVAILABLE + 1; +var STATE_RETRYING_DOWNLOAD = STATE_WAITING_TO_DOWNLOAD + 1; +var STATE_DOWNLOADING = STATE_RETRYING_DOWNLOAD + 1; +var STATE_DOWNLOAD_COMPLETE = STATE_DOWNLOADING + 1; +var STATE_EXTRACTING = STATE_DOWNLOAD_COMPLETE + 1; +var STATE_APPLYING_DIFFERENTIAL_PATCH = STATE_EXTRACTING + 1; +var STATE_READY_TO_INSTALL = STATE_APPLYING_DIFFERENTIAL_PATCH + 1; +var STATE_WAITING_TO_INSTALL = STATE_READY_TO_INSTALL + 1; +var STATE_INSTALLING = STATE_WAITING_TO_INSTALL + 1; +var STATE_INSTALL_COMPLETE = STATE_INSTALLING + 1; +var STATE_PAUSED = STATE_INSTALL_COMPLETE + 1; +var STATE_NO_UPDATE = STATE_PAUSED + 1; +var STATE_ERROR = STATE_NO_UPDATE + 1; + +// The following states need to be kept in sync with the AppCommandStatus enum +// in omaha3_idl.idl. +var COMMAND_STATUS_INIT = 1; +var COMMAND_STATUS_RUNNING = COMMAND_STATUS_INIT + 1; +var COMMAND_STATUS_ERROR = COMMAND_STATUS_RUNNING + 1; +var COMMAND_STATUS_COMPLETE = COMMAND_STATUS_ERROR + 1; + +function update3webProgId(is_machine) { + return 'GoogleUpdate.Update3Web' + (is_machine ? 'Machine' : 'User'); +} + +function initializeBundle(is_machine) { + var update3web = WScript.CreateObject(update3webProgId(is_machine)); + var bundle = update3web.createAppBundleWeb(); + bundle.initialize(); + return bundle; +} + +function initializeBundleForInstall(is_machine) { + return initializeBundle(is_machine); +} + +function doCheckForUpdate(appId, is_machine) { + var bundle = initializeBundle(is_machine); + + var app = bundle.createInstalledApp(appId); + bundle.checkForUpdate(); + doLoopUntilDone(CHECK_FOR_UPDATE, bundle); + + app = null; + bundle = null; + CollectGarbage(); +} + +function doDownload(appId, is_machine) { + var bundle = initializeBundle(is_machine); + + bundle.createApp(appId, 'GPEZ', 'en', ''); + bundle.checkForUpdate(); + doLoopUntilDone(DOWNLOAD, bundle); +} + +function doInstall(appId, is_machine) { + var bundle = initializeBundleForInstall(is_machine); + + bundle.createApp(appId, 'GPEZ', 'en', ''); + bundle.checkForUpdate(); + doLoopUntilDone(INSTALL, bundle); +} + +function doUpdate(appId, is_machine) { + var bundle = initializeBundleForInstall(is_machine); + + bundle.createInstalledApp(appId); + bundle.checkForUpdate(); + doLoopUntilDone(UPDATE, bundle); +} + +function doLaunchCommand(appId, is_machine, command, argument_list) { + var bundle = initializeBundle(is_machine); + bundle.createInstalledApp(appId); + + var app = bundle.appWeb(0); + if (!app) { + WScript.Echo('App not found.'); + return; + } + + var cmd = app.command(command); + if (!cmd) { + WScript.Echo('Command not found.'); + return; + } + + try { + WScript.Echo('Launching command.'); + + switch (argument_list.length) { + case 0: + cmd.execute(); + break; + case 1: + cmd.execute(argument_list[0]); + break; + case 2: + cmd.execute(argument_list[0], argument_list[1]); + break; + case 3: + cmd.execute(argument_list[0], argument_list[1], argument_list[2]); + break; + case 4: + cmd.execute(argument_list[0], + argument_list[1], + argument_list[2], + argument_list[3]); + break; + case 5: + cmd.execute(argument_list[0], + argument_list[1], + argument_list[2], + argument_list[3], + argument_list[4]); + case 6: + cmd.execute(argument_list[0], + argument_list[1], + argument_list[2], + argument_list[3], + argument_list[4], + argument_list[5]); + case 7: + cmd.execute(argument_list[0], + argument_list[1], + argument_list[2], + argument_list[3], + argument_list[4], + argument_list[5], + argument_list[6]); + case 8: + cmd.execute(argument_list[0], + argument_list[1], + argument_list[2], + argument_list[3], + argument_list[4], + argument_list[5], + argument_list[6], + argument_list[7]); + case 9: + cmd.execute(argument_list[0], + argument_list[1], + argument_list[2], + argument_list[3], + argument_list[4], + argument_list[5], + argument_list[6], + argument_list[7], + argument_list[8]); + break; + default: WScript.Echo('Too many arguments.'); return; + } + WScript.Echo('Command launched.'); + } catch (ex) { + WScript.Echo('Error: ' + ex.description + ': ' + ex.number); + return; + } + + while (true) { + var status = cmd.status; + var stateDescription = ''; + + switch (status) { + case COMMAND_STATUS_RUNNING: + stateDescription = 'Running...'; + break; + + case COMMAND_STATUS_ERROR: + stateDescription = 'Error!'; + break; + + case COMMAND_STATUS_COMPLETE: + stateDescription = 'Exited with code ' + cmd.exitCode + '.'; + break; + + default: + stateDescription = 'Unhandled state!'; + break; + } + WScript.Echo('[State][' + status + '][' + stateDescription + ']'); + + if (status != COMMAND_STATUS_RUNNING) { + return; + } + WScript.Sleep(100); + } +} + +function doLoopUntilDone(operation, bundle) { + function operationDone() { + if (!bundle) { + WScript.Echo('No bundle is defined!'); + return false; + } + + var done = false; + var app = bundle.appWeb(0); + var state = app.currentState; + var stateDescription = ''; + var extraData = ''; + + switch (state.stateValue) { + case STATE_INIT: + stateDescription = 'Initializating...'; + break; + + case STATE_WAITING_TO_CHECK_FOR_UPDATE: + case STATE_CHECKING_FOR_UPDATE: + stateDescription = 'Checking for update...'; + extraData = '[Current Version][' + app.currentVersionWeb.version + ']'; + break; + + case STATE_UPDATE_AVAILABLE: + stateDescription = 'Update available!'; + extraData = '[Next Version][' + app.nextVersionWeb.version + ']'; + if (operation == CHECK_FOR_UPDATE) { + done = true; + break; + } + + bundle.download(); + break; + + case STATE_WAITING_TO_DOWNLOAD: + case STATE_RETRYING_DOWNLOAD: + stateDescription = 'Contacting server...'; + break; + + case STATE_DOWNLOADING: + stateDescription = 'Downloading...'; + extraData = '[Bytes downloaded][' + state.bytesDownloaded + ']' + + '[Bytes total][' + state.totalBytesToDownload + ']' + + '[Time remaining][' + state.downloadTimeRemainingMs + ']'; + break; + + case STATE_DOWNLOAD_COMPLETE: + case STATE_EXTRACTING: + case STATE_APPLYING_DIFFERENTIAL_PATCH: + case STATE_READY_TO_INSTALL: + stateDescription = 'Download completed!'; + extraData = '[Bytes downloaded][' + state.bytesDownloaded + ']' + + '[Bytes total][' + state.totalBytesToDownload + ']'; + if (operation == DOWNLOAD) { + done = true; + break; + } + + bundle.install(); + break; + + case STATE_WAITING_TO_INSTALL: + case STATE_INSTALLING: + stateDescription = 'Installing...'; + extraData = '[Install Progress][' + state.installProgress + ']' + + '[Time remaining][' + state.installTimeRemainingMs + ']'; + break; + + case STATE_INSTALL_COMPLETE: + stateDescription = 'Done!'; + done = true; + break; + + case STATE_PAUSED: + stateDescription = 'Paused...'; + break; + + case STATE_NO_UPDATE: + stateDescription = 'No update available!'; + done = true; + break; + + case STATE_ERROR: + stateDescription = 'Error!'; + extraData = '[errorCode][' + state.errorCode + ']' + + '[completionMessage][' + state.completionMessage + ']' + + '[installerResultCode][' + state.installerResultCode + ']'; + done = true; + break; + + default: + stateDescription = 'Unhandled state...'; + break; + } + + WScript.Echo('[State][' + state.stateValue + '][' + stateDescription + ']'); + if (extraData.length > 0) { + WScript.Echo('[Data][' + extraData + ']'); + } + + return done; + } + + while (!operationDone()) { + WScript.Sleep(100); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Main +//////////////////////////////////////////////////////////////////////////////// + +function parseAndRun() { + if (WScript.Arguments.length < 3) { + return false; + } + + var app_guid = WScript.Arguments(0); + var is_machine = Boolean(parseInt(WScript.Arguments(1))); + var operation = parseInt(WScript.Arguments(2)); + + switch (operation) { + case CHECK_FOR_UPDATE: + if (WScript.Arguments.length != 3) { + return false; + } + doCheckForUpdate(app_guid, is_machine); + break; + + case DOWNLOAD: + if (WScript.Arguments.length != 3) { + return false; + } + doDownload(app_guid, is_machine); + break; + + case INSTALL: + if (WScript.Arguments.length != 3) { + return false; + } + doInstall(app_guid, is_machine); + break; + + case UPDATE: + if (WScript.Arguments.length != 3) { + return false; + } + doUpdate(app_guid, is_machine); + break; + + case LAUNCH_COMMAND: + if (WScript.Arguments.length < 4) { + return false; + } + var command = WScript.Arguments(3); + var argument_list = []; + for (var i = 4; i < WScript.Arguments.length; ++i) { + argument_list.push(WScript.Arguments(i)); + } + doLaunchCommand(app_guid, is_machine, command, argument_list); + break; + + default: + return false; + } + + return true; +} + +try { + if (!parseAndRun()) { + WScript.Echo( + 'Usage: {GUID} ' + + '{is_machine: 0|1} ' + + '{0|1|2|3|4==CheckForUpdate|Download|Install|Update|LaunchCommand} ' + + '{command_id?}'); + } +} catch (ex) { + if (ex.number == -2147024703) { + WScript.Echo('ERROR_BAD_EXE_FORMAT: Try the SysWOW64 Script Host: ' + + ex.description); + } else { + WScript.Echo('Error: ' + ex.description + ': ' + ex.number); + } +} diff --git a/omaha/main.scons b/omaha/main.scons index eae3da6f5..db77dbfce 100644 --- a/omaha/main.scons +++ b/omaha/main.scons @@ -67,6 +67,7 @@ _is_google_update_build = False # *generated_resources_*.rc files with localized strings. _FULL_COMPANY_NAME = 'OmahaCompanyName Inc.' _SHORT_COMPANY_NAME = 'OmahaCompanyName' +_PATH_COMPANY_NAME = 'OmahaCompanyName' _PRODUCT_NAME = 'Update' _COMPANY_DOMAIN_BASE = 'google' _COMPANY_DOMAIN = _COMPANY_DOMAIN_BASE + '.com' @@ -100,7 +101,7 @@ _msc_ver = int(os.getenv('OMAHA_MSC_VER', "1920")) _sdk_dir = os.getenv('WindowsSdkDir', os.getenv('OMAHA_PLATFORM_SDK_DIR')) if (_sdk_dir is None): - _sdk_dir = ('$THIRD_PARTY/%s/files' % + _sdk_dir = ('$GOOGLE3/third_party/%s/files' % { omaha_version_utils.VC160:'windows_sdk/windows_sdk_10', omaha_version_utils.VC150:'windows_sdk/windows_sdk_10', omaha_version_utils.VC140:'windows_sdk/windows_sdk_10', @@ -109,12 +110,8 @@ if (_sdk_dir is None): omaha_version_utils.VC80:'platformsdk/v6_1' }[_msc_ver]) _sdk_version = os.getenv('OMAHA_WINDOWS_SDK_10_0_VERSION', None) - -_atl_server_dir = os.getenv('OMAHA_ATL_SERVER_DIR', - '$THIRD_PARTY/atl_server/files') -_wtl_dir = os.getenv('OMAHA_WTL_DIR', '$THIRD_PARTY/wtl/files') - -_signtool_path = ('python $MAIN_DIR/tools/retry.py 10 5 \"%s/%s\"' % +_wtl_dir = os.getenv('OMAHA_WTL_DIR', '$GOOGLE3/third_party/wtl/files') +_signtool_path = ('python "$MAIN_DIR/tools/retry.py" 10 5 \"%s/%s\"' % (os.getenv('OMAHA_SIGNTOOL_SDK_DIR'), 'signtool.exe')) # Windows is the only environment we bulid for, so create a specialized base @@ -128,7 +125,8 @@ win_env = Environment( tools=[ 'component_setup', 'target_platform_windows', - { omaha_version_utils.VC160:'windows_vc16_0_host64_x86', + { omaha_version_utils.VC170:'windows_vc17_0_host64_x86', + omaha_version_utils.VC160:'windows_vc16_0_host64_x86', omaha_version_utils.VC150:'windows_vc15_0_host64_x86', omaha_version_utils.VC140:'windows_vc14_0', omaha_version_utils.VC120:'windows_vc12_0', @@ -136,7 +134,8 @@ win_env = Environment( omaha_version_utils.VC80:'target_platform_windows' }[_msc_ver], 'masm', # Use 'masm' to override the 'as' tool currently # used by default in 'target_platform_windows' - { omaha_version_utils.VC160:'atlmfc_vc16_0', + { omaha_version_utils.VC170:'atlmfc_vc17_0', + omaha_version_utils.VC160:'atlmfc_vc16_0', omaha_version_utils.VC150:'atlmfc_vc15_0', omaha_version_utils.VC140:'atlmfc_vc14_0', omaha_version_utils.VC120:'atlmfc_vc12_0', @@ -160,9 +159,8 @@ win_env['ENV']['GOROOT'] = os.getenv('GOROOT', '\\..\\third_party\\go\\files') win_env['ENV']['LocalAppData'] = os.getenv('LocalAppData') -# Append paths for supporting tools such as uuidgen.exe and mage.exe. +# Append paths for supporting tools such as uuidgen.exe. win_env.AppendENVPath('PATH', (os.environ['WindowsSdkVerBinPath'] + '\\x86\\')) -win_env.AppendENVPath('PATH', os.getenv('OMAHA_NETFX_TOOLS_DIR')) # Remove this value because it conflicts with a #define # in shlwapi.h in the Vista SDK @@ -184,10 +182,8 @@ win_env['COMPONENT_TEST_SUBSYSTEM_WINDOWS'] = 1 # Declare command line options relating to code signing # authenticode_file and authenticode_password are used by the normal signing -# tool and to sign manifests for ClickOnce. -# patching_certificate is used to create patchable MSI installers and MSPs. +# tool. # authenticode_file and authenticode_password are only used if !build_server. -# patching_certificate is used in all cases. AddOption( '--authenticode_file', action='store', @@ -205,23 +201,6 @@ AddOption( default=default_cert_password ) -AddOption( - '--sha1_authenticode_file', - action='store', - nargs=1, - type='string', - default='$MAIN_DIR/data/Sha1OmahaTestCert.pfx' -) - -default_sha1_cert_password = '888' -AddOption( - '--sha1_authenticode_password', - action='store', - nargs=1, - type='string', - default=default_sha1_cert_password -) - AddOption( '--sha2_authenticode_file', action='store', @@ -239,14 +218,6 @@ AddOption( default=default_sha2_cert_password ) -AddOption( - '--patching_certificate', - action='store', - nargs=1, - type='string', - default='$MAIN_DIR/data/OmahaTestCert.cer' -) - # Declare option for specifying path to new official build files AddOption( '--official_build_path', @@ -256,25 +227,6 @@ AddOption( default=None ) -# Declare option for specifying the set of official app installers to build. -# The value describes the name of the directory containing the official -# installer manifests and definitions. -AddOption( - '--official_installer_app', - action='store', - nargs=1, - type='string', - default=None -) - -AddOption( - '--official_installer_file', - action='store', - nargs=1, - type='string', - default=None -) - AddOption( '--build_number', action='store', @@ -301,6 +253,8 @@ DeclareBit('analysis', 'Turns on static analysis') DeclareBit('suppress_light_validation', 'Suppress WiX Linker ICE validation') DeclareBit('has_device_management', 'True if including cloud-based device management') +DeclareBit('verify_payload_authenticode_signature', + 'Whether to verify the Authenticode signature of payloads') # Build official installers if --official_installers is on the command line. win_env.SetBitFromOption('official_installers', False) @@ -355,13 +309,9 @@ if not win_env.Bit('build_server'): CERTIFICATE_HASH='', CERTIFICATE_PASSWORD=GetOption('authenticode_password'), CERTIFICATE_PATH=GetOption('authenticode_file'), - SHA1_CERTIFICATE_HASH='', SHA2_CERTIFICATE_HASH='', - SHA1_CERTIFICATE_ISSUER='', SHA2_CERTIFICATE_ISSUER='', - SHA1_CERTIFICATE_PASSWORD=GetOption('sha1_authenticode_password'), SHA2_CERTIFICATE_PASSWORD=GetOption('sha2_authenticode_password'), - SHA1_CERTIFICATE_PATH=GetOption('sha1_authenticode_file'), SHA2_CERTIFICATE_PATH=GetOption('sha2_authenticode_file'), ) @@ -380,6 +330,9 @@ win_env.SetBitFromOption('analysis', False) # non-interactive account or a non-Admin account: http://goo.gl/UyDzeP. win_env.SetBitFromOption('suppress_light_validation', False) +# Whether the Authenticode signature of update payloads should be verified. +win_env.SetBitFromOption('verify_payload_authenticode_signature', False) + # # Set up version info. # @@ -428,6 +381,11 @@ if win_env.Bit('has_device_management'): CPPDEFINES = ['HAS_LEGACY_DM_CLIENT'], ) +if win_env.Bit('verify_payload_authenticode_signature'): + win_env.Append( + CPPDEFINES = ['VERIFY_PAYLOAD_AUTHENTICODE_SIGNATURE'], + ) + # Make sure python.exe can be located. win_env.AppendENVPath('PATH', os.environ['OMAHA_PYTHON_DIR']) @@ -447,7 +405,6 @@ if _msc_ver >= omaha_version_utils.VC140: win_env.FilterOut(CCFLAGS=['/W3']) win_env.AppendUnique( - # Add Windows specific compiler flags. CCFLAGS = [ '/arch:IA32', ('', '/analyze:WX-')[win_env.Bit('analysis')], @@ -467,7 +424,6 @@ win_env.AppendUnique( '/W4', '/Wall', '/WX', # warnings as errors - '/Zc:twoPhase-', # avoids an ICE in service/service_main.h with 16.1.0. '/Zo', # better debugging support for optimized code # @@ -524,9 +480,33 @@ win_env.AppendUnique( '/wd5032', # detected #pragma warning(push) with no corresponding # #pragma warning(pop) - due to a WinSDK bug in winioctl.h. '/wd5038', # data member '' will be initialized after data member '' + '/wd5041', # out-of-line definition for constexpr static data member is + # not needed and is deprecated in C++17'. '/wd5042', # function declarations at block scope cannot be specified # 'inline' in standard C++ '/wd5045', # Compiler will insert Spectre mitigation for memory load + '/wd5054', # operator '*': deprecated between enumerations of different + # types + '/wd5204', # class has virtual functions, but its trivial destructor + # is not virtual; instances of objects derived from this + # class may not be destructed correctly + '/wd5205', # delete of an abstract class ... that has a non-virtual + # destructor results in undefined behavior + '/wd5214', # applying '++' to an operand with a volatile qualified type + # is deprecated in C++20' + '/wd5219', # implicit conversion from 'size_t' to 'const float', + # possible loss of data + '/wd5220', # a non-static data member with a volatile qualified type no + # longer implies that compiler generated copy/move + # constructors and copy/move assignment operators are non + # trivial + '/wd5245', # 'function': unreferenced function with internal linkage + # has been removed + '/wd5246', # 'member': the initialization of a subobject should be + # wrapped in braces + '/wd5267', # definition of implicit copy constructor/assignment + # operator for 'type' is deprecated because it has a + # user-provided assignment operator/copy constructor # # Disable static analysis warnings. # @@ -535,30 +515,20 @@ win_env.AppendUnique( '/wd28251', # Inconsistent SAL annotations. ], - # Where to look for include files. CPPPATH = [ - _atl_server_dir + '/include', _wtl_dir +'/include', + '$GOOGLE3', '$GOOGLECLIENT', '$MAIN_DIR', '$MAIN_DIR/third_party/chrome/files/src', - '$THIRD_PARTY/breakpad/src', - '$THIRD_PARTY/googletest/googlemock/include', - '$THIRD_PARTY/googletest/googletest/include', - '$THIRD_PARTY/libzip/lib', + '$OBJ_ROOT', # Contains .h files generated by the midl compiler. + '$GOOGLE3/third_party/breakpad/src', + '$GOOGLE3/third_party/googletest/googlemock/include', + '$GOOGLE3/third_party/googletest/googletest/include', + '$GOOGLE3/third_party/libzip/lib', ], - # Defines for windows environment. CPPDEFINES = [ - 'PSAPI_VERSION=1', - # Use _WIN32_WINNT_WIN7 (0x0601) - 'WINVER=0x0601', - '_WIN32_WINNT=0x0601', - 'WIN32', '_WINDOWS', - 'UNICODE', '_UNICODE', - 'WIN32_LEAN_AND_MEAN', - 'STRICT', - 'SECURITY_WIN32', '_ATL_ALL_WARNINGS', '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS', '_ATL_CSTRING_NO_CRT', @@ -568,34 +538,35 @@ win_env.AppendUnique( '_ATL_NO_GLOBAL_SOCKET_STARTUP', '_ATL_NO_PERF_SUPPORT', '_ATL_NO_TRACK_HEAP', - '_ATL_NO_UUIDOF', '_ATL_STATIC_REGISTRY', '_ATL_XP_TARGETING', '_SECURE_ATL=1', '_WTL_NO_CSTRING', # WTL uses ATL CString instead. - 'ZIP_STATIC', - # '_ATL_NO_CONNECTION_POINTS', - # '_ATL_NO_DOCHOSTUIHANDLER', - # '_ATL_NO_HOSTING', + 'GUNIT_NO_GOOGLE3', - # Set these C_DEFINES when APPVER=5.0 - # (see win32.mak in Platform SDK) - # Target Windows XP SP3. The minimum platform supported is still - # Windows XP. Therefore, the code needs to gracefully handle the - # cases where some of the functionality enabled by the platform SDK - # headers is not available. - # Use NTDDI_WIN7 (0x06010000) - 'NTDDI_VERSION=0x06010000', + 'ARCH_CPU_X86_FAMILY', # For Chromium code. + 'LOGGING', # Logging is enabled in all modes for diagnostics purposes. - # don't define min and max in windef.h - 'NOMINMAX', + # For the CRX Verifier protocol buffers code. + 'GOOGLE_PROTOBUF_NO_RTTI', + 'GOOGLE_PROTOBUF_SUPPORT_WINDOWS_XP', - # Logging is enabled in all modes for diagnostics purposes. - 'LOGGING', + 'NOMINMAX', + 'PSAPI_VERSION=1', + '_UNICODE', + 'UNICODE', + 'SECURITY_WIN32', + 'STRICT', + '_WINDOWS', + 'WIN32', + 'WIN32_LEAN_AND_MEAN', + 'WINVER=0x0601', # Windows 7. + 'ZIP_STATIC', 'FULL_COMPANY_NAME_ANSI=\\"%s\\"' % _FULL_COMPANY_NAME, 'SHORT_COMPANY_NAME_ANSI=\\"%s\\"' % _SHORT_COMPANY_NAME, + 'PATH_COMPANY_NAME_ANSI=\\"%s\\"' % _PATH_COMPANY_NAME, 'PRODUCT_NAME_ANSI=\\"%s\\"' % _PRODUCT_NAME, 'COMPANY_DOMAIN_BASE_ANSI=\\"%s\\"' % _COMPANY_DOMAIN_BASE, 'COMPANY_DOMAIN_ANSI=\\"%s\\"' % _COMPANY_DOMAIN, @@ -607,33 +578,18 @@ win_env.AppendUnique( 'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate'), 'ONECLICK_PLUGIN_NAME=_T(\\"%s\\")' % ( omaha_version_utils.GetONECLICK_PLUGIN_NAME()), - 'ONECLICK_PLUGIN_VERSION_ANSI=\\"%d\\"' % ( - omaha_version_info.oneclick_plugin_version), - 'ONECLICK_PLUGIN_FILENAME=_T(\\"%s\\")' % ( - omaha_version_info.oneclick_plugin_filename), 'UPDATE_PLUGIN_NAME=_T(\\"%s\\")' % ( omaha_version_utils.GetUPDATE_PLUGIN_NAME()), - 'UPDATE_PLUGIN_VERSION_ANSI=\\"%d\\"' % ( - omaha_version_info.update_plugin_version), - 'UPDATE_PLUGIN_FILENAME=_T(\\"%s\\")' % ( - omaha_version_info.update_plugin_filename), 'CRASH_HANDLER_NAME=_T(\\"%s\\")' % omaha_version_utils.GetCRASH_HANDLER_NAME(), - - # for Chromium code. - 'ARCH_CPU_X86_FAMILY', - - # for the CRX Verifier protocol buffers code. - 'GOOGLE_PROTOBUF_NO_RTTI', - 'GOOGLE_PROTOBUF_SUPPORT_WINDOWS_XP', ], - # Link in some windows libraries. LIBS = [ 'advapi32', 'comdlg32', 'gdi32', 'kernel32', + 'ntdll', 'odbc32', 'odbccp32', 'ole32', @@ -644,7 +600,6 @@ win_env.AppendUnique( 'winspool', ], - # Common linker flags. LINKFLAGS = [ '/nologo', '/SUBSYSTEM:WINDOWS,5.01', @@ -655,9 +610,9 @@ win_env.AppendUnique( '/NXCOMPAT', # Enable NX support. See http://goo.gl/k2IE. '/SAFESEH', # '/VERBOSE:LIB', # Uncomment to get verbose link info for debugging. + '/WX', # Treat warnings as errors. ], - # Resource compiler flags. # Defines in CCFLAGS are automatically included. RCFLAGS = [ '/l 1033', # /l == default language ID @@ -718,14 +673,11 @@ for mid_dir in ['', 'vc']: win_env.PrependENVPath(env_var, var_path) if not win_env.Bit('official_installers'): - # Make sure csc.exe can be located. - win_env.AppendENVPath('PATH', os.environ['OMAHA_NET_DIR']) - sys.path.append('tools') # for import proxy_clsid_utils.py import proxy_clsid_utils # Generate uniqe proxy CLSIDs for each build. - win_env.Execute('python $MAIN_DIR\\tools\\proxy_clsid_utils.py') + win_env.Execute('python "$MAIN_DIR\\tools\\proxy_clsid_utils.py"') win_env.Append( CPPDEFINES = [ 'PROXY_CLSID_IS_MACHINE=%s' % proxy_clsid_utils.GetMachineProxyClsid(), @@ -744,7 +696,6 @@ if win_env.Bit('suppress_light_validation'): _base_dirs = [ '.', 'base', - 'clickonce', 'client', 'common', 'core', @@ -763,7 +714,6 @@ _base_dirs = [ _normal_dirs = [ 'installers', 'mi_exe_stub', - 'plugins', 'recovery', ] @@ -861,7 +811,8 @@ windows_optimized_env.AppendUnique( '/O1', # Optimize for small size. '/Oy-', # Disable frame pointer omission. '/GL', # Global optimization goes with link flag '/LTCG' - '/MT', + '/MT', # Use the multithread, + # static version of the run-time library. ], CPPDEFINES = [ 'NDEBUG', @@ -871,10 +822,10 @@ windows_optimized_env.AppendUnique( '/LTCG', # Set LTCG for creation of .lib files too. ], LINKFLAGS = [ - '/incremental:no', - '/opt:ref', - '/opt:icf=32', - '/LTCG', # Link-time code generation goes with cl flag '/GL' + '/INCREMENTAL:NO', # Disable incremental linking. + '/OPT:REF', # Eliminates functions and data that are never referenced. + '/OPT:ICF=32',# Perform identical COMDAT folding (32 iterations). + '/LTCG', # Link-time code generation goes with cl flag '/GL'. ], ) diff --git a/omaha/mi_exe_stub/build.scons b/omaha/mi_exe_stub/build.scons index 24e438bdd..65f7d5bb6 100644 --- a/omaha/mi_exe_stub/build.scons +++ b/omaha/mi_exe_stub/build.scons @@ -44,6 +44,9 @@ for omaha_version_info in env['omaha_versions_info']: 'shlwapi', 'version', ], + LINKFLAGS = [ + '/LARGEADDRESSAWARE', # Enable addresses larger than 2 GB. + ], RCFLAGS = [ '/DVERSION_MAJOR=%d' % omaha_version_info.version_major, '/DVERSION_MINOR=%d' % omaha_version_info.version_minor, diff --git a/omaha/mi_exe_stub/mi.cc b/omaha/mi_exe_stub/mi.cc index 4888be92b..c9c4c6ec2 100644 --- a/omaha/mi_exe_stub/mi.cc +++ b/omaha/mi_exe_stub/mi.cc @@ -122,7 +122,7 @@ char* ReadTag(TagExtractor* extractor) { return NULL; } - // Do a sanity check of the tag string. The double quote '"' + // Do a check of the tag string. The double quote '"' // is a special character that should not be included in the tag string. for (const char* tag_char = tag_buffer.get(); *tag_char; ++tag_char) { if (*tag_char == '"') { @@ -310,15 +310,8 @@ class MetaInstaller { return false; } - // TODO(omaha3): When we refactor base into minibase, replace this with a - // call to GetTempFilenameAt() from utils.h. Do the same for the other call - // to GetTempFileName() in this module. - CString temp_dir; - DWORD result = ::GetTempFileName(parent_dir, - _T("GUM"), - 0, // form a unique filename - CStrBuf(temp_dir, MAX_PATH)); - if (!result || (result == ERROR_BUFFER_OVERFLOW)) { + const CString temp_dir = GetTempFilenameAt(parent_dir, _T("GUM")); + if (temp_dir.IsEmpty()) { return false; } @@ -331,29 +324,20 @@ class MetaInstaller { return true; } - // Create a temp directory under %ProgramFiles%. To avoid possible issues with - // anti-malware heuristics, this function returns early and does not try to - // create a directory if the user is not an admin. - bool CreateProgramFilesTempDir() { - if (!::IsUserAnAdmin()) { + // Create a temp directory under %ProgramFiles%\Google\Temp. To avoid possible + // issues with anti-malware heuristics, this function returns early and does + // not try to create a directory if the user is not an admin. + bool CreateMachineTempDir() { + const CString google_update_temp_dir(GetSecureSystemTempDir()); + if (google_update_temp_dir.IsEmpty()) { return false; } - CString program_files_dir; - HRESULT hr = ::SHGetFolderPath(NULL, - CSIDL_PROGRAM_FILES, - NULL, - SHGFP_TYPE_CURRENT, - CStrBuf(program_files_dir, MAX_PATH)); - if (FAILED(hr)) { + if (!CreateTempSubdirectory(google_update_temp_dir)) { return false; } - if (!CreateTempSubdirectory(program_files_dir)) { - return false; - } - - temp_root_dir_ = program_files_dir; + temp_root_dir_ = google_update_temp_dir; return true; } @@ -376,19 +360,20 @@ class MetaInstaller { } // Creates a temp directory to hold the embedded setup files. First attempts - // creating the directory under %ProgramFiles%, and if that fails, creates + // creating the directory under a secure path, and if that fails, creates // under the user's %TMP% directory. int CreateUniqueTempDirectory() { - return CreateProgramFilesTempDir() || CreateUserTempDir() ? 0 : -1; + return (::IsUserAnAdmin() ? CreateMachineTempDir() + : CreateUserTempDir()) + ? 0 + : -1; } HANDLE ExtractTarballToTempLocation() { HANDLE tarball_file = INVALID_HANDLE_VALUE; - TCHAR tarball_filename[MAX_PATH] = {0}; - if (::GetTempFileName(temp_root_dir_, - _T("GUT"), - 0, // form a unique filename - tarball_filename)) { + const CString tarball_filename = + GetTempFilenameAt(temp_root_dir_, _T("GUT")); + if (!tarball_filename.IsEmpty()) { files_to_delete_.Add(tarball_filename); HRSRC res_info = ::FindResource(NULL, MAKEINTRESOURCE(IDR_PAYLOAD), @@ -657,4 +642,3 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int) { int result = mi.ExtractAndRun(); return result; } - diff --git a/omaha/mi_exe_stub/tar.h b/omaha/mi_exe_stub/tar.h index 6c89782d3..410d48f77 100644 --- a/omaha/mi_exe_stub/tar.h +++ b/omaha/mi_exe_stub/tar.h @@ -43,7 +43,7 @@ typedef struct { char devmajor[8]; char devminor[8]; char prefix[155]; - char dummy[12]; // make it exactly 512 bytes + char padding[12]; // make it exactly 512 bytes } USTARHeader; // Supports untarring of files from a tar-format archive. Pretty minimal; diff --git a/omaha/mi_exe_stub/x86_encoder/bcj2.cc b/omaha/mi_exe_stub/x86_encoder/bcj2.cc index af9ccbbf8..f27e9ab71 100644 --- a/omaha/mi_exe_stub/x86_encoder/bcj2.cc +++ b/omaha/mi_exe_stub/x86_encoder/bcj2.cc @@ -15,7 +15,6 @@ // // BCJ encodes a file to increase its compressibility. -#include #include #include #include @@ -33,8 +32,13 @@ int wmain(int argc, WCHAR* argv[], WCHAR* env[]) { } // argv[1] is the input file, argv[2] is the output file. - scoped_hfile file(::CreateFile(argv[1], GENERIC_READ, 0, - NULL, OPEN_EXISTING, 0, NULL)); + scoped_hfile file(::CreateFile(argv[1], + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL)); if (!valid(file)) { return 2; } @@ -45,21 +49,23 @@ int wmain(int argc, WCHAR* argv[], WCHAR* env[]) { } DWORD file_size = static_cast(file_size_data.QuadPart); - std::unique_ptr buffer(new uint8[file_size]); DWORD bytes_read = 0; - if (!::ReadFile(get(file), buffer.get(), file_size, &bytes_read, NULL) || - bytes_read != file_size) { - return 4; - } std::string out1; std::string out2; std::string out3; std::string out4; - if (!omaha::Bcj2Encode(std::string(reinterpret_cast(buffer.get()), - file_size), - &out1, &out2, &out3, &out4)) { - return 5; + + { + std::string buffer(file_size, '\0'); + if (!::ReadFile(get(file), buffer.data(), file_size, &bytes_read, NULL) || + bytes_read != file_size) { + return 4; + } + + if (!omaha::Bcj2Encode(buffer, &out1, &out2, &out3, &out4)) { + return 5; + } } // The format of BCJ2 file is very primitive. @@ -69,75 +75,51 @@ int wmain(int argc, WCHAR* argv[], WCHAR* env[]) { // size of stream 2 // size of stream 3 // size of stream 4 - const size_t output_buffer_length = 5 * sizeof(uint32) + // NOLINT - out1.size() + out2.size() + out3.size() + out4.size(); - if (output_buffer_length > DWORD_MAX) { - return 13; - } + const size_t output_buffer_header[] = { + bytes_read, + out1.size(), + out2.size(), + out3.size(), + out4.size(), + }; - size_t buffer_remaining = output_buffer_length; - std::unique_ptr output_buffer(new uint8[output_buffer_length]); - uint8* p = output_buffer.get(); - *reinterpret_cast(p) = bytes_read; - p += sizeof(uint32); // NOLINT - buffer_remaining -= sizeof(uint32); // NOLINT - *reinterpret_cast(p) = static_cast(out1.size()); - p += sizeof(uint32); // NOLINT - buffer_remaining -= sizeof(uint32); // NOLINT - *reinterpret_cast(p) = static_cast(out2.size()); - p += sizeof(uint32); // NOLINT - buffer_remaining -= sizeof(uint32); // NOLINT - *reinterpret_cast(p) = static_cast(out3.size()); - p += sizeof(uint32); // NOLINT - buffer_remaining -= sizeof(uint32); // NOLINT - *reinterpret_cast(p) = static_cast(out4.size()); - p += sizeof(uint32); // NOLINT - buffer_remaining -= sizeof(uint32); // NOLINT - if (!out1.empty()) { - if (0 != memcpy_s(p, buffer_remaining, &out1[0], out1.size())) { - return 9; - } - p += out1.size(); - buffer_remaining -= out1.size(); - } - if (!out2.empty()) { - if (0 != memcpy_s(p, buffer_remaining, &out2[0], out2.size())) { - return 10; - } - p += out2.size(); - buffer_remaining -= out2.size(); - } - if (!out3.empty()) { - if (0 != memcpy_s(p, buffer_remaining, &out3[0], out3.size())) { - return 11; - } - p += out3.size(); - buffer_remaining -= out3.size(); - } - if (!out4.empty()) { - if (0 != memcpy_s(p, buffer_remaining, &out4[0], out4.size())) { - return 12; - } - p += out4.size(); - buffer_remaining -= out4.size(); + std::string out0(sizeof(output_buffer_header), '\0'); + if (0 != memcpy_s(out0.data(), + out0.size(), + &output_buffer_header[0], + sizeof(output_buffer_header))) { + return 6; } - if (p != output_buffer.get() + output_buffer_length || - 0 != buffer_remaining) { - return 8; + + if ((out0.size() + out1.size() + out2.size() + out3.size() + out4.size()) > + DWORD_MAX) { + return 7; } - reset(file, ::CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, - NULL)); + reset(file, + ::CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)); if (!valid(file)) { - return 6; + return 8; } - DWORD bytes_written = 0; - if (!::WriteFile(get(file), - output_buffer.get(), - static_cast(output_buffer_length), - &bytes_written, NULL)) { - return 7; + for (const auto& out : { std::move(out0), + std::move(out1), + std::move(out2), + std::move(out3), + std::move(out4) }) { + if (out.empty()) { + continue; + } + + DWORD bytes_written = 0; + if (!::WriteFile(get(file), + out.data(), + out.size(), + &bytes_written, + NULL) || + bytes_written != out.size()) { + return 9; + } } return 0; diff --git a/omaha/mi_exe_stub/x86_encoder/bcj2_encoder.cc b/omaha/mi_exe_stub/x86_encoder/bcj2_encoder.cc index b8fe4642f..62d184ea0 100644 --- a/omaha/mi_exe_stub/x86_encoder/bcj2_encoder.cc +++ b/omaha/mi_exe_stub/x86_encoder/bcj2_encoder.cc @@ -98,6 +98,8 @@ bool Bcj2Encode(const std::string& input, return false; } + main_output->reserve(input.size()); + size_t input_position = 0; static const int kNumberOfMoveBits = 5; diff --git a/omaha/net/bits_request.cc b/omaha/net/bits_request.cc index af2b118c7..b73406b09 100644 --- a/omaha/net/bits_request.cc +++ b/omaha/net/bits_request.cc @@ -200,7 +200,7 @@ HRESULT BitsRequest::Cancel() { is_canceled_ = true; if (request_state_.get()) { - VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job))); + VERIFY_SUCCEEDED(CancelBitsJob(request_state_->bits_job)); } } @@ -212,7 +212,7 @@ HRESULT BitsRequest::Pause() { NET_LOG(L3, (_T("[BitsRequest::Pause]"))); __mutexBlock(lock_) { if (request_state_.get()) { - VERIFY1(SUCCEEDED(PauseBitsJob(request_state_->bits_job))); + VERIFY_SUCCEEDED(PauseBitsJob(request_state_->bits_job)); } } return S_OK; @@ -222,7 +222,7 @@ HRESULT BitsRequest::Resume() { NET_LOG(L3, (_T("[BitsRequest::Resume]"))); __mutexBlock(lock_) { if (request_state_.get()) { - VERIFY1(SUCCEEDED(ResumeBitsJob(request_state_->bits_job))); + VERIFY_SUCCEEDED(ResumeBitsJob(request_state_->bits_job)); } } return S_OK; @@ -235,7 +235,7 @@ HRESULT BitsRequest::Send() { __mutexBlock(lock_) { if (request_state_.get()) { - VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job))); + VERIFY_SUCCEEDED(CancelBitsJob(request_state_->bits_job)); } request_state_.reset(new TransientRequestState); } @@ -356,9 +356,11 @@ HRESULT BitsRequest::SetJobProperties() { } } - // Always set no_progress_timeout to 0 for foreground jobs which means the - // jobs in transient error state will be immediately moved to error state. - int no_progress_timeout = low_priority_ ? no_progress_timeout_ : 0; + // This sets `no_progress_timeout` to 0 for all jobs to prevent retries and + // forces the jobs into the BG_JOB_STATE_ERROR state immediately if any errors + // are encountered. + // This also implies that the `MinimumRetryDelay` set above will be ignored. + int no_progress_timeout = 0; if (no_progress_timeout != -1) { ASSERT1(no_progress_timeout >= 0); @@ -549,6 +551,7 @@ HRESULT BitsRequest::DoSend() { // we resume the job. There is an assumption, so far true, that calling // Resume on a job, the state changes right away from SUSPENDED to QUEUED. + bool job_reached_transfering_once = false; for (;;) { if (is_canceled_) { return GOOPDATE_E_CANCELLED; @@ -560,12 +563,24 @@ HRESULT BitsRequest::DoSend() { return hr; } - NET_LOG(L3, (_T("[job %s][state %s]"), + OPT_LOG(L3, (_T("[BitsRequest::DoSend][url %s][job %s][state %s]"), url_, GuidToString(request_state_->bits_job_id), JobStateToString(job_state))); switch (job_state) { case BG_JOB_STATE_QUEUED: + if (job_reached_transfering_once) { + // BITS moves a job from transferring to queued if the user being + // impersonated logs off while the job is transferring. This is not + // desired for system level Omaha as the job will not make progress + // until that same user logs back in. If this happens, let the + // operation fallback to other downloaders instead of blocking. + OPT_LOG(LW, (L"[BITS job %s moved from transferring to queued][fail " + L"the job to fallback]", + GuidToString(request_state_->bits_job_id))); + return CI_E_BITS_REQUEUED; + + } break; case BG_JOB_STATE_CONNECTING: @@ -579,6 +594,7 @@ HRESULT BitsRequest::DoSend() { case BG_JOB_STATE_TRANSFERRING: OnStateTransferring(); + job_reached_transfering_once = true; break; case BG_JOB_STATE_TRANSIENT_ERROR: @@ -619,8 +635,8 @@ HRESULT BitsRequest::DoSend() { return hr; } - VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme( - proxy_config_.proxy, is_https, win_http_scheme))); + VERIFY_SUCCEEDED(network_config->SetProxyAuthScheme( + proxy_config_.proxy, is_https, win_http_scheme)); } return S_OK; @@ -637,7 +653,7 @@ HRESULT BitsRequest::DoSend() { case BG_JOB_STATE_CANCELLED: return GOOPDATE_E_CANCELLED; - }; + } // Check to see if we've been redirected to a different URL. CString current_url; diff --git a/omaha/net/build.scons b/omaha/net/build.scons index f3b6c404f..9064cd1cb 100644 --- a/omaha/net/build.scons +++ b/omaha/net/build.scons @@ -19,15 +19,6 @@ Import('env') # unaffected by changes made here. local_env = env.Clone() -local_env.Append( - CPPPATH = [ - # Need to look in output dir to find .h files generated by midl compiler. - # This also allows Hammer to understand dependencies between this subdir - # and the .idl files in the goopdate folder. - '$OBJ_ROOT', - ], -) - inputs = [ 'bits_request.cc', 'bits_job_callback.cc', @@ -38,7 +29,6 @@ inputs = [ 'detector.cc', 'http_client.cc', 'simple_request.cc', - 'net_diags.cc', 'net_utils.cc', 'network_config.cc', 'network_request.cc', diff --git a/omaha/net/cup_ecdsa_pubkey.5.h b/omaha/net/cup_ecdsa_pubkey.14.h similarity index 69% rename from omaha/net/cup_ecdsa_pubkey.5.h rename to omaha/net/cup_ecdsa_pubkey.14.h index d6a325852..30869bf3c 100644 --- a/omaha/net/cup_ecdsa_pubkey.5.h +++ b/omaha/net/cup_ecdsa_pubkey.14.h @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. +// Copyright 2024 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,13 +19,13 @@ // * Uncompressed header byte (0x04) // * Gx coordinate (256-bit integer, big-endian) // * Gy coordinate (256-bit integer, big-endian) -{0x05, +{0x0e, 0x04, -0x07, 0xe6, 0x22, 0xfb, 0x74, 0x9d, 0x24, 0xa0, -0xb2, 0x14, 0x99, 0xa6, 0xfa, 0xcb, 0x96, 0xdc, -0x2c, 0x97, 0xca, 0x0b, 0xd5, 0xb1, 0xb0, 0xdb, -0x3e, 0x72, 0x60, 0xa0, 0x25, 0xf8, 0x19, 0xe4, -0xed, 0xa0, 0xbf, 0x10, 0xfd, 0x68, 0xcf, 0xc7, -0xb0, 0x86, 0xb5, 0x73, 0x8b, 0xd5, 0x78, 0xdb, -0xc7, 0x45, 0x4f, 0x3c, 0xdd, 0xc3, 0xae, 0xfa, -0x99, 0x48, 0xbf, 0xe8, 0x5f, 0x2f, 0x61, 0x57}; +0x9d, 0x0f, 0x34, 0x55, 0x70, 0x0e, 0x3d, 0xb3, +0x60, 0x06, 0x21, 0xb2, 0x20, 0xf6, 0xdb, 0x7d, +0xb5, 0x24, 0x71, 0x6e, 0xf9, 0xe5, 0xc3, 0xfb, +0xc2, 0xa8, 0xa7, 0xad, 0x87, 0x01, 0x7a, 0x1d, +0x26, 0xb6, 0x22, 0x03, 0x04, 0x69, 0xd3, 0xad, +0x32, 0xf1, 0x33, 0x8b, 0xa1, 0x5e, 0x33, 0x81, +0xd0, 0x4b, 0x32, 0xf5, 0x81, 0xcc, 0xfe, 0x8b, +0x9b, 0x1f, 0x9c, 0x39, 0x2a, 0x38, 0xa7, 0x60}; diff --git a/omaha/net/cup_ecdsa_pubkey.6.h b/omaha/net/cup_ecdsa_pubkey.6.h deleted file mode 100644 index 6ec1d7521..000000000 --- a/omaha/net/cup_ecdsa_pubkey.6.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2016 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// CUP-ECDSA public keys consist of a byte array, 66 bytes long, containing: -// * The key ID (one byte) -// * The public key in X9.62 uncompressed encoding (65 bytes): -// * Uncompressed header byte (0x04) -// * Gx coordinate (256-bit integer, big-endian) -// * Gy coordinate (256-bit integer, big-endian) -{0x06, -0x04, -0x08, 0x40, 0xd2, 0x45, 0xc6, 0x4f, 0x2a, 0xff, -0x24, 0xe0, 0x91, 0x18, 0x6c, 0x5e, 0xcc, 0x27, -0x1c, 0x71, 0xe7, 0xa9, 0xbe, 0xc7, 0xc6, 0x7e, -0xb2, 0xf8, 0x39, 0x80, 0x19, 0x91, 0x5f, 0x52, -0x7c, 0x03, 0xd0, 0xd8, 0x44, 0xbe, 0x8d, 0xbc, -0x59, 0x0d, 0x72, 0x67, 0x11, 0xb5, 0x23, 0x5e, -0x99, 0x49, 0xbb, 0xfe, 0x2a, 0xab, 0x52, 0x7c, -0x5a, 0x69, 0xd9, 0x2a, 0x71, 0x64, 0x8e, 0x0f}; diff --git a/omaha/net/cup_ecdsa_pubkey.7.h b/omaha/net/cup_ecdsa_pubkey.7.h deleted file mode 100644 index 28ff8e660..000000000 --- a/omaha/net/cup_ecdsa_pubkey.7.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// CUP-ECDSA public keys consist of a byte array, 66 bytes long, containing: -// * The key ID (one byte) -// * The public key in X9.62 uncompressed encoding (65 bytes): -// * Uncompressed header byte (0x04) -// * Gx coordinate (256-bit integer, big-endian) -// * Gy coordinate (256-bit integer, big-endian) -{0x07, -0x04, -0x8f, 0x44, 0x0a, 0xb9, 0xf5, 0xc8, 0x38, 0x13, -0x77, 0xd0, 0x3b, 0x4a, 0x78, 0xe6, 0x00, 0xe4, -0xd5, 0x7a, 0xe0, 0x57, 0xd8, 0x1c, 0x3a, 0x2e, -0xe2, 0xc1, 0xaa, 0xb5, 0xc3, 0x54, 0x22, 0x5c, -0x69, 0x4f, 0x32, 0x1b, 0x3b, 0x8e, 0x6b, 0x07, -0x8e, 0x50, 0x20, 0xb8, 0x56, 0xe9, 0xa0, 0xd3, -0xc3, 0x08, 0xd6, 0x2d, 0x1d, 0x58, 0x0a, 0xaa, -0x44, 0x00, 0x62, 0x02, 0xbc, 0x5b, 0x3c, 0x75}; diff --git a/omaha/net/cup_ecdsa_pubkey.8.h b/omaha/net/cup_ecdsa_pubkey.8.h deleted file mode 100644 index 9156ab40d..000000000 --- a/omaha/net/cup_ecdsa_pubkey.8.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// CUP-ECDSA public keys consist of a byte array, 66 bytes long, containing: -// * The key ID (one byte) -// * The public key in X9.62 uncompressed encoding (65 bytes): -// * Uncompressed header byte (0x04) -// * Gx coordinate (256-bit integer, big-endian) -// * Gy coordinate (256-bit integer, big-endian) -{0x08, -0x04, -0xf8, 0x9d, 0xa2, 0x0a, 0x97, 0xe4, 0xf2, 0x54, -0xe1, 0x72, 0xe2, 0x94, 0x3f, 0x34, 0xda, 0x55, -0xc5, 0x23, 0x84, 0xd4, 0x77, 0x01, 0x81, 0xca, -0xfa, 0xd4, 0xde, 0x94, 0x67, 0x47, 0xbf, 0x21, -0x86, 0xc7, 0xb4, 0x4f, 0xec, 0x1a, 0x61, 0x61, -0x23, 0xe6, 0xa4, 0x7e, 0x8f, 0xe1, 0x5a, 0xfb, -0xd9, 0xa9, 0x34, 0x5b, 0x56, 0xb4, 0x6d, 0x6e, -0x79, 0x3b, 0xd1, 0xd6, 0xda, 0x8c, 0xf7, 0xad}; diff --git a/omaha/net/cup_ecdsa_pubkey.9.h b/omaha/net/cup_ecdsa_pubkey.9.h deleted file mode 100644 index 309c50402..000000000 --- a/omaha/net/cup_ecdsa_pubkey.9.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// CUP-ECDSA public keys consist of a byte array, 66 bytes long, containing: -// * The key ID (one byte) -// * The public key in X9.62 uncompressed encoding (65 bytes): -// * Uncompressed header byte (0x04) -// * Gx coordinate (256-bit integer, big-endian) -// * Gy coordinate (256-bit integer, big-endian) -{0x09, -0x04, -0xb1, 0x5c, 0x15, 0x32, 0x62, 0x09, 0x69, 0x60, -0x63, 0x92, 0xd4, 0xb1, 0xf6, 0x6d, 0x49, 0xad, -0x95, 0x98, 0x06, 0xf3, 0x26, 0x6e, 0xca, 0xc6, -0x19, 0x03, 0xe1, 0x49, 0xc0, 0xed, 0x6a, 0x8f, -0x83, 0x96, 0x80, 0xfc, 0xef, 0x56, 0x26, 0x11, -0xeb, 0xa7, 0x01, 0xa4, 0x2f, 0x44, 0xc6, 0x60, -0x0e, 0x1d, 0x67, 0x09, 0xa5, 0xa4, 0x54, 0x6e, -0xb0, 0xa3, 0xbe, 0x7e, 0xc8, 0x36, 0xca, 0x0c}; diff --git a/omaha/net/cup_ecdsa_request.cc b/omaha/net/cup_ecdsa_request.cc index c7119c20d..cf943d8cc 100644 --- a/omaha/net/cup_ecdsa_request.cc +++ b/omaha/net/cup_ecdsa_request.cc @@ -21,6 +21,7 @@ #include #include #include + #include "base/rand_util.h" #include "omaha/base/debug.h" #include "omaha/base/error.h" @@ -42,8 +43,8 @@ namespace omaha { namespace internal { const uint8 CupEcdsaRequestImpl::kCupProductionPublicKey[] = -#include "omaha/net/cup_ecdsa_pubkey.9.h" -; // NOLINT +#include "omaha/net/cup_ecdsa_pubkey.14.h" + ; // NOLINT const uint8 CupEcdsaRequestImpl::kCupTestPublicKey[] = #include "omaha/net/cup_ecdsa_pubkey.3.h" @@ -181,21 +182,25 @@ void CupEcdsaRequestImpl::set_proxy_auth_config(const ProxyAuthConfig& config) { } HRESULT CupEcdsaRequestImpl::BuildRequest() { - // Generate a random nonce. - if (!RandUint32(&cup_->nonce)) { + // Generate a random nonce of 256 bits. + char nonce[32] = {0}; + if (!RandBytes(&nonce, sizeof(nonce))) { metric_cup_ecdsa_other_errors++; return OMAHA_NET_E_CUP_NO_ENTROPY; } + CStringA nonce_string; + WebSafeBase64Escape(nonce, sizeof(nonce), &nonce_string, false); + // Compute the SHA-256 hash of the request body; we need it to verify the // response, and we can optionally send it to the server as well. VERIFY1(SafeSHA256Hash(request_buffer_, request_buffer_length_, &cup_->request_hash)); // Generate the values of our query parameters, cup2key and (opt) cup2hreq. - SafeCStringFormat(&cup_->cup2key, _T("%d:%u"), + SafeCStringFormat(&cup_->cup2key, _T("%d:%S"), static_cast(public_key_.version()), - cup_->nonce); + nonce_string); cup_->cup2hreq = BytesToHex(cup_->request_hash); // Compute the url of the CUP request -- append a query string, or append to @@ -401,8 +406,7 @@ bool CupEcdsaRequestImpl::ParseServerETag(const CString& etag_in, return true; } -CupEcdsaRequestImpl::TransientCupState::TransientCupState() : nonce(0) { -} +CupEcdsaRequestImpl::TransientCupState::TransientCupState() {} } // namespace internal diff --git a/omaha/net/cup_ecdsa_request_impl.h b/omaha/net/cup_ecdsa_request_impl.h index fc5838b43..a5f4aef05 100644 --- a/omaha/net/cup_ecdsa_request_impl.h +++ b/omaha/net/cup_ecdsa_request_impl.h @@ -76,7 +76,6 @@ class CupEcdsaRequestImpl { struct TransientCupState { TransientCupState(); - uint32 nonce; // Random nonce for request freshness. std::vector request_hash; // SHA-256 hash of the request body. CString cup2key; // Query parameter: key id and nonce diff --git a/omaha/net/cup_ecdsa_request_unittest.cc b/omaha/net/cup_ecdsa_request_unittest.cc index 1829e6eac..f0b7ac05b 100644 --- a/omaha/net/cup_ecdsa_request_unittest.cc +++ b/omaha/net/cup_ecdsa_request_unittest.cc @@ -33,14 +33,10 @@ namespace omaha { -const TCHAR* const kGetUrl = - _T("http://tools.google.com/service/update2/id"); -const TCHAR* const kGetUrlNoResponseBody = - _T("http://tools.google.com/service/update2/nil"); const TCHAR* const kPostUrl = - _T("http://tools.google.com/service/update2"); + _T("https://tools.") COMPANY_DOMAIN _T("/service/update2"); const TCHAR* const kPostHttpsUrl = - _T("https://tools.google.com/service/update2"); + _T("https://tools.") COMPANY_DOMAIN _T("/service/update2"); const uint8 kRequestBuffer[] = ""; // NOLINT class CupEcdsaRequestTest : public testing::Test { diff --git a/omaha/net/cup_ecdsa_utils_unittest.cc b/omaha/net/cup_ecdsa_utils_unittest.cc index 25c2e5af2..8333e7a1c 100644 --- a/omaha/net/cup_ecdsa_utils_unittest.cc +++ b/omaha/net/cup_ecdsa_utils_unittest.cc @@ -491,8 +491,8 @@ TEST(EcdsaPublicKey, DecodeFromBuffer_ProdKey) { EcdsaPublicKey key; uint8 kProdKey[] = -#include "omaha/net/cup_ecdsa_pubkey.9.h" - ; // NOLINT +#include "omaha/net/cup_ecdsa_pubkey.14.h" + ; // NOLINT key.DecodeFromBuffer(kProdKey); } diff --git a/omaha/net/detector.cc b/omaha/net/detector.cc index c4ed535e8..673b61cb1 100644 --- a/omaha/net/detector.cc +++ b/omaha/net/detector.cc @@ -15,12 +15,10 @@ // TODO(omaha): move EnclosePath and UnenclosePath functions from path.h to // string.h -// TODO(omaha): Firefox detector does not handle proxy bypass. #include "omaha/net/detector.h" #include -#include "omaha/base/atl_regexp.h" #include "omaha/base/browser_utils.h" #include "omaha/base/constants.h" #include "omaha/base/debug.h" @@ -32,9 +30,11 @@ #include "omaha/base/user_info.h" #include "omaha/base/utils.h" #include "omaha/base/time.h" +#include "omaha/common/config_manager.h" #include "omaha/common/const_group_policy.h" #include "omaha/net/http_client.h" #include "omaha/net/network_config.h" +#include "omaha/goopdate/dm_messages.h" namespace omaha { @@ -83,7 +83,7 @@ HRESULT IEProxyDetector::Detect(ProxyConfig* config) { ::GlobalFree(const_cast(ie_proxy_config.proxy_bypass)); return S_OK; -}; +} } // namespace internal @@ -115,23 +115,15 @@ UpdateDevProxyDetector::UpdateDevProxyDetector() : registry_detector_(MACHINE_REG_UPDATE_DEV) { } -HRESULT GroupPolicyProxyDetector::Detect(ProxyConfig* config) { +HRESULT PolicyProxyDetector::Detect(ProxyConfig* config) { ASSERT1(config); - if (!IsEnrolledToDomain()) { - OPT_LOG(L5, (_T("[GroupPolicyProxyDetector::Detect][Ignoring group policy]") - _T("[machine is not part of a domain]"))); - return E_FAIL; - } - CString proxy_mode; - HRESULT hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - &proxy_mode); + HRESULT hr = GetProxyMode(&proxy_mode); if (FAILED(hr)) { return hr; } - NET_LOG(L4, (_T("[group policy proxy mode][%s]"), proxy_mode)); + NET_LOG(L4, (_T("[%s][proxy mode][%s]"), source(), proxy_mode)); *config = ProxyConfig(); config->source = source(); @@ -143,13 +135,9 @@ HRESULT GroupPolicyProxyDetector::Detect(ProxyConfig* config) { config->auto_detect = true; return S_OK; } else if (proxy_mode.CompareNoCase(kProxyModePacScript) == 0) { - return RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyPacUrl, - &config->auto_config_url); + return GetProxyPacUrl(&config->auto_config_url); } else if (proxy_mode.CompareNoCase(kProxyModeFixedServers) == 0) { - return RegKey::GetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyServer, - &config->proxy); + return GetProxyServer(&config->proxy); } else if (proxy_mode.CompareNoCase(kProxyModeSystem) == 0) { // Fall through, and let the rest of the proxy detectors deal with it. return E_FAIL; @@ -159,265 +147,16 @@ HRESULT GroupPolicyProxyDetector::Detect(ProxyConfig* config) { } } -FirefoxProxyDetector::FirefoxProxyDetector() - : cached_prefs_last_modified_(0), - cached_config_(new ProxyConfig) { +HRESULT PolicyProxyDetector::GetProxyMode(CString* proxy_mode) { + return ConfigManager::Instance()->GetProxyMode(proxy_mode, NULL); } -HRESULT FirefoxProxyDetector::Detect(ProxyConfig* config) { - ASSERT1(config); - - // The Firefox profile is not available when running as a local system. - if (user_info::IsRunningAsSystem()) { - return E_FAIL; - } - - const TCHAR* const kFirefoxPrefsJsFile = _T("\\prefs.js"); - - CString name, path; - HRESULT hr = GetFirefoxDefaultProfile(&name, &path); - if (FAILED(hr)) { - return hr; - } - path.Append(kFirefoxPrefsJsFile); - - // Has the current profile been modified? Check the name, path, and - // last modified time of the profile are the same as their cached values. - FILETIME filetime_last_modified = {0}; - int64 last_modified = 0; - if (SUCCEEDED(File::GetFileTime(path, - NULL, - NULL, - &filetime_last_modified))) { - last_modified = FileTimeToInt64(filetime_last_modified); - } - if (name.CompareNoCase(cached_prefs_name_) == 0 && - path.CompareNoCase(cached_prefs_file_path_) == 0 && - last_modified == cached_prefs_last_modified_ && - last_modified) { - NET_LOG(L4, (_T("[using FF cached profile][%s]"), path)); - *config = *cached_config_; - return S_OK; - } - - hr = ParsePrefsFile(name, path, config); - if (SUCCEEDED(hr) && last_modified) { - // If FireFox is the default brower, promotes its proxy priority. - BrowserType browser_type(BROWSER_UNKNOWN); - if (SUCCEEDED(GetDefaultBrowserType(&browser_type)) && - browser_type == BROWSER_FIREFOX) { - config->priority = ProxyConfig::PROXY_PRIORITY_DEFAULT_BROWSER; - } - NET_LOG(L4, (_T("[cache FF profile][%s]"), path)); - cached_prefs_name_ = name; - cached_prefs_file_path_ = path; - cached_prefs_last_modified_ = last_modified; - *cached_config_ = *config; - } - return hr; +HRESULT PolicyProxyDetector::GetProxyPacUrl(CString* proxy_pac_url) { + return ConfigManager::Instance()->GetProxyPacUrl(proxy_pac_url, NULL); } -// This is what the proxy configuration in Firefox looks like: -// user_pref("network.proxy.autoconfig_url", "http://wpad/wpad.dat"); -// user_pref("network.proxy.ftp", "127.0.0.1"); -// user_pref("network.proxy.ftp_port", 8888); -// user_pref("network.proxy.gopher", "127.0.0.1"); -// user_pref("network.proxy.gopher_port", 8888); -// user_pref("network.proxy.http", "127.0.0.1"); -// user_pref("network.proxy.http_port", 8888); -// user_pref("network.proxy.share_proxy_settings", true); -// user_pref("network.proxy.socks", "127.0.0.1"); -// user_pref("network.proxy.socks_port", 8888); -// user_pref("network.proxy.ssl", "127.0.0.1"); -// user_pref("network.proxy.ssl_port", 8888); -// user_pref("network.proxy.type", 4); -HRESULT FirefoxProxyDetector::ParsePrefsFile(const TCHAR* name, - const TCHAR* file_path, - ProxyConfig* config) { - ASSERT1(name); - ASSERT1(file_path); - ASSERT1(config); - - *config = ProxyConfig(); - config->source = source(); - - // TODO(omaha): implement optimization not to parse the file again if it - // did not change. - UNREFERENCED_PARAMETER(name); - - // There were issues in production where the code fails to allocate - // the 1MB memory buffer as it had been initially requested by a previous - // version of the code. - // - // The assert below is somehow flaky but useful to detect the unlikely cases - // when the prefs file can't be opened. - FileReader prefs_file; - const size_t kBufferSize = 0x10000; // 64KB buffer. - HRESULT hr = prefs_file.Init(file_path, kBufferSize); - ASSERT1(SUCCEEDED(hr)); - if (FAILED(hr)) { - return hr; - } - - CString proxy_type; - CString proxy_config_url; - CString proxy_http_host; - CString proxy_http_port; - CString proxy_ssl_host; - CString proxy_ssl_port; - - // For each line in the prefs.js, try to parse the proxy information out. - char line[1024] = {0}; - while (SUCCEEDED(prefs_file.ReadLineAnsi(arraysize(line), line))) { - ParsePrefsLine(line, - &proxy_type, - &proxy_config_url, - &proxy_http_host, - &proxy_http_port, - &proxy_ssl_host, - &proxy_ssl_port); - } - - // The default in FireFox is direct connection so it may be that the - // network.proxy.type is missing. - int type = PROXY_TYPE_NO_PROXY; - if (!proxy_type.IsEmpty() && - !String_StringToDecimalIntChecked(proxy_type, &type)) { - return E_UNEXPECTED; - } - - // Direct connection. - if (type == PROXY_TYPE_NO_PROXY) { - return S_OK; - } - - // We look for both proxy auto-detect and proxy config url, to emulate - // the IE behavior, where when the auto-detect fails it defaults to the - // auto config url. Firefox remembers the auto config url even if not used, - // so it might not hurt to try it out. - if (type & PROXY_TYPE_AUTO_DETECT) { - config->auto_detect = true; - } - if ((type & PROXY_TYPE_AUTO_CONFIG_URL) && !proxy_config_url.IsEmpty()) { - UnenclosePath(&proxy_config_url); - config->auto_config_url = proxy_config_url; - } - - // Named proxy. - if (!(type & PROXY_TYPE_NAMED_PROXY)) { - return S_OK; - } - - CString proxy; - hr = BuildProxyString(proxy_http_host, - proxy_http_port, - proxy_ssl_host, - proxy_ssl_port, - &proxy); - if (FAILED(hr)) { - return hr; - } - - config->proxy = proxy; - return S_OK; -} - -HRESULT FirefoxProxyDetector::BuildProxyString(const CString& proxy_http_host, - const CString& http_port, - const CString& proxy_ssl_host, - const CString& ssl_port, - CString* proxy) { - ASSERT1(proxy); - - CString http_host = proxy_http_host; - CString ssl_host = proxy_ssl_host; - - // The host names in the prefs file are strings literals. - UnenclosePath(&http_host); - UnenclosePath(&ssl_host); - - // Validate the port values. - if (!http_port.IsEmpty()) { - int http_port_num = 0; - if (!String_StringToDecimalIntChecked(http_port, &http_port_num) || - http_port_num <= 0 && - http_port_num > INTERNET_MAX_PORT_NUMBER_VALUE) { - return E_INVALIDARG; - } - } - if (!ssl_port.IsEmpty()) { - int ssl_port_num = 0; - if (!String_StringToDecimalIntChecked(ssl_port, &ssl_port_num) || - ssl_port_num <= 0 || - ssl_port_num > INTERNET_MAX_PORT_NUMBER_VALUE) { - return E_INVALIDARG; - } - } - - // Format the proxy string. - CString str; - if (!http_host.IsEmpty()) { - SafeCStringAppendFormat(&str, _T("http=%s"), http_host); - if (!http_port.IsEmpty()) { - SafeCStringAppendFormat(&str, _T(":%s"), http_port); - } - } - if (!ssl_host.IsEmpty()) { - // Append a separator if needed. - if (!str.IsEmpty()) { - str += _T(';'); - } - SafeCStringAppendFormat(&str, _T("https=%s"), ssl_host); - if (!ssl_port.IsEmpty()) { - SafeCStringAppendFormat(&str, _T(":%s"), ssl_port); - } - } - - *proxy = str; - return S_OK; -} - -// Parses a line from the prefs.js. An example of line to parse is: -// user_pref("network.proxy.http", "foo"); -void FirefoxProxyDetector::ParsePrefsLine(const char* ansi_line, - CString* proxy_type, - CString* proxy_config_url, - CString* proxy_http_host, - CString* proxy_http_port, - CString* proxy_ssl_host, - CString* proxy_ssl_port) { - // Skip the lines that do not contain "network.proxy" to speed up the - // parsing. This is important for large prefs files. - if (strstr(ansi_line, "network.proxy.") == NULL) { - return; - } - - AtlRE proxy_type_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.type\\\"\\b*,\\b*{\\d+}\\)"), false); // NOLINT - AtlRE proxy_config_url_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.autoconfig_url\\\"\\b*,\\b*{\\q}\\)"), false); // NOLINT - AtlRE proxy_http_host_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.http\\\"\\b*,\\b*{\\q}\\)"), false); // NOLINT - AtlRE proxy_http_port_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.http_port\\\"\\b*,\\b*{\\d+}\\)"), false); // NOLINT - AtlRE proxy_ssl_host_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.ssl\\\"\\b*,\\b*{\\q}\\)"), false); // NOLINT - AtlRE proxy_ssl_port_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.ssl_port\\\"\\b*,\\b*{\\d+}\\)"), false); // NOLINT - - CString line(ansi_line); - if (AtlRE::PartialMatch(line, proxy_type_regex, proxy_type)) { - return; - } - if (AtlRE::PartialMatch(line, proxy_config_url_regex, proxy_config_url)) { - return; - } - if (AtlRE::PartialMatch(line, proxy_http_host_regex, proxy_http_host)) { - return; - } - if (AtlRE::PartialMatch(line, proxy_http_port_regex, proxy_http_port)) { - return; - } - if (AtlRE::PartialMatch(line, proxy_ssl_host_regex, proxy_ssl_host)) { - return; - } - if (AtlRE::PartialMatch(line, proxy_ssl_port_regex, proxy_ssl_port)) { - return; - } +HRESULT PolicyProxyDetector::GetProxyServer(CString* proxy_server) { + return ConfigManager::Instance()->GetProxyServer(proxy_server, NULL); } HRESULT DefaultProxyDetector::Detect(ProxyConfig* config) { @@ -468,7 +207,7 @@ HRESULT IEWPADProxyDetector::Detect(ProxyConfig* config) { config->auto_detect = ie_proxy_config.auto_detect; config->priority = ie_proxy_config.priority; return S_OK; -}; +} HRESULT IEPACProxyDetector::Detect(ProxyConfig* config) { ASSERT1(config); @@ -487,7 +226,7 @@ HRESULT IEPACProxyDetector::Detect(ProxyConfig* config) { config->auto_config_url = ie_proxy_config.auto_config_url; config->priority = ie_proxy_config.priority; return S_OK; -}; +} HRESULT IENamedProxyDetector::Detect(ProxyConfig* config) { ASSERT1(config); @@ -507,7 +246,7 @@ HRESULT IENamedProxyDetector::Detect(ProxyConfig* config) { config->proxy_bypass = ie_proxy_config.proxy_bypass; config->priority = ie_proxy_config.priority; return S_OK; -}; +} } // namespace omaha diff --git a/omaha/net/detector.h b/omaha/net/detector.h index 1d56c4300..43d1c8555 100644 --- a/omaha/net/detector.h +++ b/omaha/net/detector.h @@ -59,14 +59,19 @@ class UpdateDevProxyDetector : public ProxyDetectorInterface { DISALLOW_COPY_AND_ASSIGN(UpdateDevProxyDetector); }; -// A version that picks up proxy override from a group policy. -class GroupPolicyProxyDetector : public ProxyDetectorInterface { +// A version that picks up proxy override from Enterprise Policy. +class PolicyProxyDetector : public ProxyDetectorInterface { public: - GroupPolicyProxyDetector() {} + PolicyProxyDetector() {} HRESULT Detect(ProxyConfig* config) override; - const TCHAR* source() override { return _T("GroupPolicy"); } + const TCHAR* source() override { return _T("Policy"); } + private: - DISALLOW_COPY_AND_ASSIGN(GroupPolicyProxyDetector); + HRESULT GetProxyMode(CString* proxy_mode); + HRESULT GetProxyPacUrl(CString* proxy_pac_url); + HRESULT GetProxyServer(CString* proxy_server); + + DISALLOW_COPY_AND_ASSIGN(PolicyProxyDetector); }; // Detects winhttp proxy information. This is what the winhttp proxy @@ -82,54 +87,6 @@ class DefaultProxyDetector : public ProxyDetectorInterface { DISALLOW_COPY_AND_ASSIGN(DefaultProxyDetector); }; -// Detects proxy information for Firefox. -// http://www.mozilla.org/quality/networking/docs/netprefs.html -// It works only when the calling code runs as or it impersonates a user. -class FirefoxProxyDetector : public ProxyDetectorInterface { - public: - enum ProxyType { - PROXY_TYPE_NO_PROXY = 0, - PROXY_TYPE_NAMED_PROXY = 1, - PROXY_TYPE_AUTO_CONFIG_URL = 2, - PROXY_TYPE_AUTO_DETECT = 4 - }; - - FirefoxProxyDetector(); - - HRESULT Detect(ProxyConfig* config) override; - const TCHAR* source() override { return _T("Firefox"); } - private: - // Parses the prefs.js file. - HRESULT ParsePrefsFile(const TCHAR* name, - const TCHAR* file_path, - ProxyConfig* config); - - // Parse one line of the prefs file. - void ParsePrefsLine(const char* ansi_line, - CString* proxy_type, - CString* proxy_config_url, - CString* proxy_http_host, - CString* proxy_http_port, - CString* proxy_ssl_host, - CString* proxy_ssl_port); - - // Builds a proxy string out of individual components. - HRESULT BuildProxyString(const CString& http_host, - const CString& http_port, - const CString& ssl_host, - const CString& ssl_port, - CString* proxy); - - // Cached configuration values for the current FF profile. - CString cached_prefs_name_; - CString cached_prefs_file_path_; - int64 cached_prefs_last_modified_; - std::unique_ptr cached_config_; - - friend class FirefoxProxyDetectorTest; - DISALLOW_COPY_AND_ASSIGN(FirefoxProxyDetector); -}; - namespace internal { // Detects wininet proxy information for the current user. The caller must diff --git a/omaha/net/detector_unittest.cc b/omaha/net/detector_unittest.cc index e89101881..4596b120d 100644 --- a/omaha/net/detector_unittest.cc +++ b/omaha/net/detector_unittest.cc @@ -23,312 +23,23 @@ #include "omaha/base/app_util.h" #include "omaha/base/browser_utils.h" #include "omaha/base/reg_key.h" +#include "omaha/common/config_manager.h" #include "omaha/common/const_group_policy.h" +#include "omaha/goopdate/dm_messages.h" #include "omaha/net/network_config.h" #include "omaha/testing/unit_test.h" namespace omaha { -class FirefoxProxyDetectorTest : public testing::Test { - public: - FirefoxProxyDetectorTest() {} - - void SetUp() override { - detector_.reset(new FirefoxProxyDetector); - } - - void TearDown() override { - } - - HRESULT BuildProxyString(const CString& http_host, - const CString& http_port, - const CString& ssl_host, - const CString& ssl_port, - CString* proxy) { - return detector_->BuildProxyString(http_host, - http_port, - ssl_host, - ssl_port, - proxy); - } - - void ParsePrefsLine(const char* ansi_line, - CString* proxy_type, - CString* proxy_config_url, - CString* proxy_http_host, - CString* proxy_http_port, - CString* proxy_ssl_host, - CString* proxy_ssl_port) { - detector_->ParsePrefsLine(ansi_line, - proxy_type, - proxy_config_url, - proxy_http_host, - proxy_http_port, - proxy_ssl_host, - proxy_ssl_port); - } - - - HRESULT ParsePrefsFile(const TCHAR* name, - const TCHAR* file_path, - ProxyConfig* config) { - return detector_->ParsePrefsFile(name, file_path, config); - } - - // Builds a mock prefs file to test the parsing code. - bool BuildPrefsFile(const CString& type, - const CString& config_url, - const CString& http_host, - const CString& http_port, - const CString& ssl_host, - const CString& ssl_port, - CString* file_path); - - std::unique_ptr detector_; - - private: - DISALLOW_COPY_AND_ASSIGN(FirefoxProxyDetectorTest); -}; - -bool FirefoxProxyDetectorTest::BuildPrefsFile(const CString& type, - const CString& config_url, - const CString& http_host, - const CString& http_port, - const CString& ssl_host, - const CString& ssl_port, - CString* file_path) { - CString temp_dir(app_util::GetTempDir()); - file_path->Format(_T("%somaha_test_%x.js"), temp_dir, ::GetTickCount()); - - FILE* prefs_file = NULL; - fopen_s(&prefs_file, CStringA(*file_path), "w"); - if (!prefs_file) { - return false; - } - - fprintf(prefs_file, - "user_pref(\"network.proxy.type\", %s);\n", - CStringA(type).GetString()); - fprintf(prefs_file, - "user_pref(\"network.proxy.autoconfig_url\", \"%s\");\n", - CStringA(config_url).GetString()); - fprintf(prefs_file, - "user_pref(\"network.proxy.http\", \"%s\");\n", - CStringA(http_host).GetString()); - fprintf(prefs_file, - "user_pref(\"network.proxy.http_port\", %s);\n", - CStringA(http_port).GetString()); - fprintf(prefs_file, - "user_pref(\"network.proxy.ssl\", \"%s\");\n", - CStringA(ssl_host).GetString()); - fprintf(prefs_file, - "user_pref(\"network.proxy.ssl_port\", %s);\n", - CStringA(ssl_port).GetString()); - - fclose(prefs_file); - - return true; -} - -TEST_F(FirefoxProxyDetectorTest, BuildProxyString) { - CString http_host = _T("foo"); - CString http_port = _T("80"); - CString ssl_host; - CString ssl_port; - CString proxy; - - EXPECT_SUCCEEDED(BuildProxyString(http_host, - http_port, - ssl_host, - ssl_port, - &proxy)); - EXPECT_STREQ(proxy, _T("http=foo:80")); - - http_host = _T("foo"); - http_port = _T("80"); - ssl_host = _T("bar"); - ssl_port = _T("8080"); - - EXPECT_SUCCEEDED(BuildProxyString(http_host, - http_port, - ssl_host, - ssl_port, - &proxy)); - EXPECT_STREQ(proxy, _T("http=foo:80;https=bar:8080")); - - http_host.Empty(); - http_port.Empty(); - ssl_host = _T("bar"); - ssl_port = _T("8080"); - - EXPECT_SUCCEEDED(BuildProxyString(http_host, - http_port, - ssl_host, - ssl_port, - &proxy)); - EXPECT_STREQ(proxy, _T("https=bar:8080")); -} - -TEST_F(FirefoxProxyDetectorTest, ParsePrefsLine) { - CString type; - CString config_url; - CString http_host; - CString http_port; - CString ssl_host; - CString ssl_port; - - // Parse "type". - const char* ansi_line = "user_pref(\"network.proxy.type\", 4);"; - ParsePrefsLine(ansi_line, - &type, - &config_url, - &http_host, - &http_port, - &ssl_host, - &ssl_port); - EXPECT_STREQ(type, _T("4")); - - // Parse "config_url". - ansi_line = - "user_pref(\"network.proxy.autoconfig_url\", \"http://wpad/wpad.dat\");"; - ParsePrefsLine(ansi_line, - &type, - &config_url, - &http_host, - &http_port, - &ssl_host, - &ssl_port); - EXPECT_STREQ(config_url, _T("\"http://wpad/wpad.dat\"")); - - // Parse "http_host". - ansi_line = "user_pref(\"network.proxy.http\", \"127.0.0.1\");"; - ParsePrefsLine(ansi_line, - &type, - &config_url, - &http_host, - &http_port, - &ssl_host, - &ssl_port); - EXPECT_STREQ(http_host, _T("\"127.0.0.1\"")); - - // Parse "http_port". - ansi_line = "user_pref(\"network.proxy.http_port\", 8888);"; - ParsePrefsLine(ansi_line, - &type, - &config_url, - &http_host, - &http_port, - &ssl_host, - &ssl_port); - EXPECT_STREQ(http_port, _T("8888")); - - // Parse "ssl_host". - ansi_line = "user_pref(\"network.proxy.ssl\", \"10.0.0.1\");"; - ParsePrefsLine(ansi_line, - &type, - &config_url, - &http_host, - &http_port, - &ssl_host, - &ssl_port); - EXPECT_STREQ(ssl_host, _T("\"10.0.0.1\"")); - - // Parse "ssl_port". - ansi_line = "user_pref(\"network.proxy.ssl_port\", 8080);"; - ParsePrefsLine(ansi_line, - &type, - &config_url, - &http_host, - &http_port, - &ssl_host, - &ssl_port); - EXPECT_STREQ(ssl_port, _T("8080")); -} - -TEST_F(FirefoxProxyDetectorTest, ParsePrefsFile) { - // Direct connection - CString prefs_file; - bool res = BuildPrefsFile(_T("0"), - _T("http://foobar"), - _T("foo"), - _T("80"), - _T("bar"), - _T("8080"), - &prefs_file); - ASSERT_TRUE(res); - ProxyConfig config; - EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config)); - EXPECT_FALSE(config.auto_detect); - EXPECT_TRUE(config.auto_config_url.IsEmpty()); - EXPECT_TRUE(config.proxy.IsEmpty()); - EXPECT_TRUE(config.proxy_bypass.IsEmpty()); - EXPECT_TRUE(::DeleteFile(prefs_file)); - - // Named proxy. - res = BuildPrefsFile(_T("1"), - _T("http://foobar"), - _T("foo"), - _T("80"), - _T("bar"), - _T("8080"), - &prefs_file); - ASSERT_TRUE(res); - config = ProxyConfig(); - EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config)); - EXPECT_FALSE(config.auto_detect); - EXPECT_TRUE(config.auto_config_url.IsEmpty()); - EXPECT_STREQ(config.proxy, _T("http=foo:80;https=bar:8080")); - EXPECT_TRUE(config.proxy_bypass.IsEmpty()); - EXPECT_TRUE(::DeleteFile(prefs_file)); - - // Auto config url. - res = BuildPrefsFile(_T("2"), - _T("http://foobar"), - _T("foo"), - _T("80"), - _T("bar"), - _T("8080"), - &prefs_file); - ASSERT_TRUE(res); - config = ProxyConfig(); - EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config)); - EXPECT_FALSE(config.auto_detect); - EXPECT_STREQ(config.auto_config_url, _T("http://foobar")); - EXPECT_TRUE(config.proxy.IsEmpty()); - EXPECT_TRUE(config.proxy_bypass.IsEmpty()); - EXPECT_TRUE(::DeleteFile(prefs_file)); - - // Auto detect. - res = BuildPrefsFile(_T("4"), - _T("http://foobar"), - _T("foo"), - _T("80"), - _T("bar"), - _T("8080"), - &prefs_file); - ASSERT_TRUE(res); - config = ProxyConfig(); - EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config)); - EXPECT_TRUE(config.auto_detect); - EXPECT_TRUE(config.auto_config_url.IsEmpty()); - EXPECT_TRUE(config.proxy.IsEmpty()); - EXPECT_TRUE(config.proxy_bypass.IsEmpty()); - EXPECT_TRUE(::DeleteFile(prefs_file)); -} - -// Tries to detect the configuration if a profile is available. -TEST_F(FirefoxProxyDetectorTest, Detect) { - CString name, path; - if (FAILED(GetFirefoxDefaultProfile(&name, &path))) { - return; - } - ProxyConfig config; - EXPECT_SUCCEEDED(detector_->Detect(&config)); -} - -class GroupPolicyProxyDetectorTest : public testing::TestWithParam { +// This class is parameterized for Managed and whether the tests use the Device +// Management (DM) proxy detection. The first parameter is a bool that indicates +// "Managed", which when true means that the tests run with Policy turned on. +// The second parameter indicates whether the tests are using the DM proxy +// detection or the GPO proxy detection. +class PolicyProxyDetectorTest + : public testing::TestWithParam> { protected: - GroupPolicyProxyDetectorTest() + PolicyProxyDetectorTest() : hive_override_key_name_(kRegistryHiveOverrideRoot) { } @@ -336,14 +47,23 @@ class GroupPolicyProxyDetectorTest : public testing::TestWithParam { RegKey::DeleteKey(hive_override_key_name_, true); OverrideRegistryHives(hive_override_key_name_); - EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, - kRegValueIsEnrolledToDomain, - IsDomain() ? 1UL : 0UL)); + EXPECT_SUCCEEDED(RegKey::SetValue( + MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain, + IsManaged() && !IsUsingDMProxyDetection() ? 1UL : 0UL)); - detector_.reset(new GroupPolicyProxyDetector); + if (IsManaged() && !IsUsingDMProxyDetection()) { + RegKey::CreateKey(kRegKeyGoopdateGroupPolicy); + } else { + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + } + + detector_.reset(new PolicyProxyDetector); } virtual void TearDown() { + ResetCachedOmahaPolicy(); + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueIsEnrolledToDomain)); @@ -351,129 +71,134 @@ class GroupPolicyProxyDetectorTest : public testing::TestWithParam { EXPECT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true)); } - bool IsDomain() { - return GetParam(); + bool IsManaged() { + return std::get<0>(GetParam()); + } + + bool IsUsingDMProxyDetection() { + return std::get<1>(GetParam()); + } + + void SetValidGroupPolicyValue(const CString& entry, const CString& val) { + if (val.IsEmpty()) { + return; + } + + EXPECT_SUCCEEDED(SetPolicyString(entry, val)); + } + + void SetProxyPolicies(const CString& proxy_mode, const CString& proxy_pac_url, + const CString& proxy_server) { + if (IsManaged() && IsUsingDMProxyDetection()) { + CachedOmahaPolicy info; + info.is_managed = true; + info.is_initialized = true; + info.proxy_mode = proxy_mode; + info.proxy_pac_url = proxy_pac_url; + info.proxy_server = proxy_server; + ConfigManager::Instance()->SetOmahaDMPolicies(info); + } else { + SetValidGroupPolicyValue(kRegValueProxyMode, proxy_mode); + SetValidGroupPolicyValue(kRegValueProxyPacUrl, proxy_pac_url); + SetValidGroupPolicyValue(kRegValueProxyServer, proxy_server); + } + } + + void ResetCachedOmahaPolicy() { + ConfigManager::Instance()->SetOmahaDMPolicies(CachedOmahaPolicy()); } CString hive_override_key_name_; - std::unique_ptr detector_; + std::unique_ptr detector_; private: - DISALLOW_COPY_AND_ASSIGN(GroupPolicyProxyDetectorTest); + DISALLOW_COPY_AND_ASSIGN(PolicyProxyDetectorTest); }; -const TCHAR kGpoSourceString[] = _T("GroupPolicy"); - -INSTANTIATE_TEST_CASE_P(IsDomain, - GroupPolicyProxyDetectorTest, - ::testing::Bool()); +INSTANTIATE_TEST_CASE_P(IsManagedIsUsingDMProxyDetection, + PolicyProxyDetectorTest, + ::testing::Combine(::testing::Bool(), + ::testing::Bool())); -TEST_P(GroupPolicyProxyDetectorTest, NoPolicy) { +TEST_P(PolicyProxyDetectorTest, NoPolicy) { ProxyConfig config; EXPECT_FAILED(detector_->Detect(&config)); } -TEST_P(GroupPolicyProxyDetectorTest, InvalidPolicyString) { - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - _T("this_is_never_a_real_policy"))); +TEST_P(PolicyProxyDetectorTest, InvalidPolicyString) { + SetProxyPolicies(_T("this_is_never_a_real_policy"), CString(), CString()); ProxyConfig config; EXPECT_FAILED(detector_->Detect(&config)); } -TEST_P(GroupPolicyProxyDetectorTest, PolicyDirect) { - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModeDirect)); +TEST_P(PolicyProxyDetectorTest, PolicyDirect) { + SetProxyPolicies(kProxyModeDirect, CString(), CString()); ProxyConfig config; - EXPECT_EQ(IsDomain() ? S_OK : E_FAIL, detector_->Detect(&config)); - EXPECT_STREQ(IsDomain() ? kGpoSourceString : _T(""), config.source); + EXPECT_EQ(IsManaged() ? S_OK : E_FAIL, detector_->Detect(&config)); EXPECT_FALSE(config.auto_detect); EXPECT_TRUE(config.auto_config_url.IsEmpty()); EXPECT_TRUE(config.proxy.IsEmpty()); EXPECT_TRUE(config.proxy_bypass.IsEmpty()); } -TEST_P(GroupPolicyProxyDetectorTest, PolicyAutoDetect) { - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModeAutoDetect)); +TEST_P(PolicyProxyDetectorTest, PolicyAutoDetect) { + SetProxyPolicies(kProxyModeAutoDetect, CString(), CString()); ProxyConfig config; - EXPECT_EQ(IsDomain() ? S_OK : E_FAIL, detector_->Detect(&config)); - EXPECT_STREQ(IsDomain() ? kGpoSourceString : _T(""), config.source); - EXPECT_EQ(IsDomain(), config.auto_detect); + EXPECT_EQ(IsManaged() ? S_OK : E_FAIL, detector_->Detect(&config)); + EXPECT_EQ(IsManaged(), config.auto_detect); EXPECT_TRUE(config.auto_config_url.IsEmpty()); EXPECT_TRUE(config.proxy.IsEmpty()); EXPECT_TRUE(config.proxy_bypass.IsEmpty()); } -TEST_P(GroupPolicyProxyDetectorTest, PolicyPacUrlNoData) { - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModePacScript)); +TEST_P(PolicyProxyDetectorTest, PolicyPacUrlNoData) { + SetProxyPolicies(kProxyModePacScript, CString(), CString()); ProxyConfig config; EXPECT_FAILED(detector_->Detect(&config)); } -TEST_P(GroupPolicyProxyDetectorTest, PolicyPacUrlHasData) { +TEST_P(PolicyProxyDetectorTest, PolicyPacUrlHasData) { const TCHAR* const kUnitTestPacUrl = _T("http://unittest/testurl.pac"); - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModePacScript)); - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyPacUrl, - kUnitTestPacUrl)); + SetProxyPolicies(kProxyModePacScript, kUnitTestPacUrl, CString()); ProxyConfig config; - EXPECT_EQ(IsDomain() ? S_OK : E_FAIL, detector_->Detect(&config)); - EXPECT_STREQ(IsDomain() ? kGpoSourceString : _T(""), config.source); + EXPECT_EQ(IsManaged() ? S_OK : E_FAIL, detector_->Detect(&config)); EXPECT_FALSE(config.auto_detect); - EXPECT_STREQ(IsDomain() ? kUnitTestPacUrl : _T(""), config.auto_config_url); + EXPECT_STREQ(IsManaged() ? kUnitTestPacUrl : _T(""), config.auto_config_url); EXPECT_TRUE(config.proxy.IsEmpty()); EXPECT_TRUE(config.proxy_bypass.IsEmpty()); } -TEST_P(GroupPolicyProxyDetectorTest, PolicyFixedNoData) { - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModeFixedServers)); +TEST_P(PolicyProxyDetectorTest, PolicyFixedNoData) { + SetProxyPolicies(kProxyModeFixedServers, CString(), CString()); ProxyConfig config; EXPECT_FAILED(detector_->Detect(&config)); } -TEST_P(GroupPolicyProxyDetectorTest, PolicyFixedHasData) { - const TCHAR* const kUnitTestFixedServer = _T("unitTEST_Pixedserver:8080"); +TEST_P(PolicyProxyDetectorTest, PolicyFixedHasData) { + const TCHAR* const kUnitTestFixedServer = _T("unittest_fixedserver:8080"); - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModeFixedServers)); - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyServer, - kUnitTestFixedServer)); + SetProxyPolicies(kProxyModeFixedServers, CString(), kUnitTestFixedServer); ProxyConfig config; - EXPECT_EQ(IsDomain() ? S_OK : E_FAIL, detector_->Detect(&config)); - EXPECT_STREQ(IsDomain() ? kGpoSourceString : _T(""), config.source); + EXPECT_EQ(IsManaged() ? S_OK : E_FAIL, detector_->Detect(&config)); EXPECT_FALSE(config.auto_detect); EXPECT_TRUE(config.auto_config_url.IsEmpty()); - EXPECT_STREQ(IsDomain() ? kUnitTestFixedServer : _T(""), config.proxy); + EXPECT_STREQ(IsManaged() ? kUnitTestFixedServer : _T(""), config.proxy); EXPECT_TRUE(config.proxy_bypass.IsEmpty()); } -TEST_P(GroupPolicyProxyDetectorTest, PolicySystem) { - EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueProxyMode, - kProxyModeSystem)); +TEST_P(PolicyProxyDetectorTest, PolicySystem) { + SetProxyPolicies(kProxyModeSystem, CString(), CString()); ProxyConfig config; EXPECT_FAILED(detector_->Detect(&config)); } } // namespace omaha - diff --git a/omaha/net/net_diags.cc b/omaha/net/net_diags.cc deleted file mode 100644 index a17243f3a..000000000 --- a/omaha/net/net_diags.cc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2007-2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Some simple network diags. - -#include "omaha/net/net_diags.h" -#include -#include -#include -#include "omaha/base/browser_utils.h" -#include "omaha/base/debug.h" -#include "omaha/base/system_info.h" -#include "omaha/base/time.h" -#include "omaha/base/utils.h" -#include "omaha/net/network_config.h" -#include "omaha/net/network_request.h" - -namespace omaha { - -NetDiags::NetDiags() { - Initialize(); -} - -NetDiags::~NetDiags() { - ::CoUninitialize(); -} - -bool PrintToConsole(const TCHAR* format, ...) { - ASSERT1(format); - va_list arg_list; - va_start(arg_list, format); - CString msg; - msg.FormatV(format, arg_list); - ASSERT1(msg.GetLength() > 0); - va_end(arg_list); - - DWORD count = 0; - bool result = !!::WriteConsole(::GetStdHandle(STD_OUTPUT_HANDLE), - msg, - msg.GetLength(), - &count, - NULL); - ASSERT1(result); - ASSERT1(msg.GetLength() == static_cast(count)); - return result; -} - -void NetDiags::Initialize() { - if (!SystemInfo::IsRunningOnXPOrLater()) { - ::MessageBox(NULL, - _T("GoogleUpdate.exe"), - _T("\"GoogleUpdate.exe /NetDiags\" only runs on Windows XP or later."), - MB_OK); - ::ExitProcess(1); - } - - if (!AttachConsoleWrap(ATTACH_PARENT_PROCESS)) { - ::MessageBox(NULL, - _T("GoogleUpdate.exe"), - _T("Please run \"GoogleUpdate.exe /NetDiags\" from a cmd.exe window."), - MB_OK); - ::ExitProcess(1); - } - - PrintToConsole(_T("\n")); - HRESULT hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); - if (FAILED(hr)) { - PrintToConsole(_T("Failed to ::CoInitialize() [0x%x]\n"), hr); - ::ExitProcess(1); - } - - NetworkConfig* network_config = NULL; - hr = NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config); - if (FAILED(hr)) { - PrintToConsole(_T("Failed to GetUserNetworkConfig() [0x%x]\n"), hr); - ::ExitProcess(1); - } - - // Initialize the detection chain: GoogleProxy, FireFox if it is the - // default browser, and IE. - network_config->Clear(); - BrowserType browser_type(BROWSER_UNKNOWN); - GetDefaultBrowserType(&browser_type); - if (browser_type == BROWSER_FIREFOX) { - PrintToConsole(_T("Default browser is Firefox\n")); - network_config->Add(new FirefoxProxyDetector()); - } - network_config->Add(new IEWPADProxyDetector); - network_config->Add(new IEPACProxyDetector); - network_config->Add(new IENamedProxyDetector); - - std::vector configs = network_config->GetConfigurations(); - if (configs.empty()) { - PrintToConsole(_T("No Network Configurations to display\n")); - } else { - PrintToConsole(_T("[Detected Network Configurations][\n%s]\n"), - NetworkConfig::ToString(configs)); - } -} - -void NetDiags::OnProgress(int bytes, int bytes_total, int, const TCHAR*) { - PrintToConsole(_T("\n[Downloading %d of %d]\n"), bytes, bytes_total); -} - -void NetDiags::OnRequestBegin() { - PrintToConsole(_T("\n[Download begins]\n")); -} - -void NetDiags::OnRequestRetryScheduled(time64 next_retry_time) { - time64 now = GetCurrent100NSTime(); - ASSERT1(next_retry_time > now); - - if (next_retry_time > now) { - PrintToConsole(_T("\n[Download will retry in %d seconds]\n"), - CeilingDivide(next_retry_time - now, kSecsTo100ns)); - } -} - -// http get. -void NetDiags::DoGet(const CString& url) { - PrintToConsole(_T("\nGET request for [%s]\n"), url); - NetworkConfig* network_config = NULL; - NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); - HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); - if (FAILED(hr)) { - PrintToConsole(_T("Failed to GetUserNetworkConfig() [0x%x]\n"), hr); - return; - } - NetworkRequest network_request(network_config->session()); - - network_request.set_callback(this); - network_request.set_num_retries(2); - std::vector response; - hr = network_request.Get(url, &response); - int status = network_request.http_status_code(); - if (FAILED(hr)) { - PrintToConsole(_T("GET request failed. HRESULT=[0x%x], HTTP Status=[%d]\n"), - hr, status); - return; - } - - PrintToConsole(_T("HTTP Status=[%d]\n"), status); - PrintToConsole(_T("HTTP Response=\n[%s]\n"), Utf8BufferToWideChar(response)); -} - -// http download. -void NetDiags::DoDownload(const CString& url) { - PrintToConsole(_T("\nDownload request for [%s]\n"), url); - NetworkConfig* network_config = NULL; - NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); - HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); - if (FAILED(hr)) { - PrintToConsole(_T("Failed to GetUserNetworkConfig() [0x%x]\n"), hr); - return; - } - NetworkRequest network_request(network_config->session()); - - network_request.set_callback(this); - network_request.set_num_retries(2); - - CString temp_file = GetTempFilename(_T("tmp")); - if (temp_file.IsEmpty()) { - PrintToConsole(_T("::GetTempFilename Failed [%d]\n"), ::GetLastError()); - return; - } - - hr = network_request.DownloadFile(url, temp_file); - int status = network_request.http_status_code(); - if (FAILED(hr)) { - PrintToConsole(_T("Download failed. HRESULT=[0x%x], HTTP Status=[%d]\n"), - hr, status); - return; - } - - PrintToConsole(_T("HTTP Status=[%d]\n"), status); - PrintToConsole(_T("Downloaded File=[%s]\n"), temp_file); - if (!::DeleteFile(temp_file)) { - PrintToConsole(_T("::DeleteFile Failed [%s][%d]\n"), - temp_file, ::GetLastError()); - return; - } else { - PrintToConsole(_T("Deleted file [%s]\n"), temp_file); - } -} - -// Run the tests. -int NetDiags::Main() { - DoGet(_T("http://www.google.com/robots.txt")); - DoGet(_T("https://www.google.com/robots.txt")); - DoDownload(_T("http://www.google.com/intl/en_ALL/images/logo.gif")); - return 0; -} - -} // namespace omaha - diff --git a/omaha/net/net_diags.h b/omaha/net/net_diags.h deleted file mode 100644 index c145af89a..000000000 --- a/omaha/net/net_diags.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - - -#ifndef OMAHA_NET_NET_DIAGS_H__ -#define OMAHA_NET_NET_DIAGS_H__ - -#include -#include -#include "base/basictypes.h" -#include "base/time.h" -#include "omaha/net/network_request.h" - -namespace omaha { - -class NetDiags : public NetworkRequestCallback { - public: - NetDiags(); - ~NetDiags(); - - // Run the tests. - int Main(); - - private: - void Initialize(); - virtual void OnProgress(int bytes, int bytes_total, int, const TCHAR*); - virtual void OnRequestBegin(); - virtual void OnRequestRetryScheduled(time64 next_retry_time); - - // http get. - void DoGet(const CString& url); - void DoDownload(const CString& url); -}; - -} // namespace omaha - -#endif // OMAHA_NET_NET_DIAGS_H__ - diff --git a/omaha/net/network_config.cc b/omaha/net/network_config.cc index 4618c7089..a9d2685f7 100644 --- a/omaha/net/network_config.cc +++ b/omaha/net/network_config.cc @@ -15,12 +15,13 @@ #include "omaha/net/network_config.h" +#include #include #include #include #include #include -#include +#include // NOLINT #include #include "base/error.h" @@ -38,6 +39,7 @@ #include "omaha/base/safe_format.h" #include "omaha/base/string.h" #include "omaha/base/system.h" +#include "omaha/base/system_info.h" #include "omaha/base/user_info.h" #include "omaha/base/utils.h" #include "omaha/common/config_manager.h" @@ -50,19 +52,12 @@ using omaha::encrypt::DecryptData; namespace omaha { -#if _MSC_VER >= 1900 -using std::unordered_set; -#else -template using unordered_set = stdext::hash_set; -#endif - -// Computes the hash value of a ProxyConfig object. Names in the stdext -// namespace are not currently part of the ISO C++ standard. +// Computes the hash value of a ProxyConfig object. size_t hash_value(const ProxyConfig& config) { - size_t hash = stdext::hash_value(config.auto_detect) ^ - stdext::hash_value(config.auto_config_url.GetString()) ^ - stdext::hash_value(config.proxy.GetString()) ^ - stdext::hash_value(config.proxy_bypass.GetString()); + size_t hash = std::hash{}(config.auto_detect) ^ + std::hash{}(config.auto_config_url.GetString()) ^ + std::hash{}(config.proxy.GetString()) ^ + std::hash{}(config.proxy_bypass.GetString()); return hash; } @@ -120,14 +115,19 @@ HRESULT NetworkConfig::Initialize() { return hr; } - Add(new UpdateDevProxyDetector); - Add(new GroupPolicyProxyDetector); - BrowserType browser_type(BROWSER_UNKNOWN); - GetDefaultBrowserType(&browser_type); - if (browser_type == BROWSER_FIREFOX) { - Add(new FirefoxProxyDetector); + // Allow TLS1.2 on Windows 7 and Windows 8. See KB3140245. + // TLS 1.2 is enabled by default on Windows 8.1 and Windows 10. + if (::IsWindows7OrGreater() && !::IsWindows8Point1OrGreater()) { + constexpr int kSecureProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2; + http_client_->SetOptionInt(session_.session_handle, + WINHTTP_OPTION_SECURE_PROTOCOLS, + kSecureProtocols); } - // There is no Chrome detector because it uses the same proxy settings as IE. + + Add(new UpdateDevProxyDetector); + Add(new PolicyProxyDetector); Add(new IEWPADProxyDetector); Add(new IEPACProxyDetector); Add(new IENamedProxyDetector); @@ -449,15 +449,6 @@ CString NetworkConfig::GetMID() { return mid; } -CString NetworkConfig::JoinStrings(const TCHAR* s1, - const TCHAR* s2, - const TCHAR* delim) { - CString result; - const TCHAR* components[] = {s1, s2}; - JoinStringsInArray(components, arraysize(components), delim, &result); - return result; -} - // Using std::hash_set adds about 2K uncompressed code size. Using a CAtlMap // adds about 1.5K. Usually, there are only five detected configurations so // an O(n^2) algorithm would work well. The advantage of the current @@ -473,7 +464,7 @@ void NetworkConfig::RemoveDuplicates(std::vector* config) { std::vector input(*config); config->clear(); - typedef unordered_set Keys; + typedef std::unordered_set Keys; Keys keys; for (size_t i = 0; i != input.size(); ++i) { const size_t hash = hash_value(input[i]); @@ -603,7 +594,7 @@ GPA_WRAP(jsproxy.dll, HRESULT NetworkConfig::GetProxyForUrlLocal(const CString& url, const CString& path_to_pac_file, HttpClient::ProxyInfo* proxy_info) { - scoped_library jsproxy_lib(::LoadLibrary(_T("jsproxy.dll"))); + scoped_library jsproxy_lib(LoadSystemLibrary(_T("jsproxy.dll"))); ASSERT1(jsproxy_lib); if (!jsproxy_lib) { HRESULT hr = HRESULTFromLastError(); @@ -747,7 +738,7 @@ void NetworkConfigManager::DeleteInstance() { NetworkConfigManager& NetworkConfigManager::Instance() { __mutexScope(instance_lock_); if (!instance_) { - VERIFY1(SUCCEEDED(NetworkConfigManager::CreateInstance())); + VERIFY_SUCCEEDED(NetworkConfigManager::CreateInstance()); } return *instance_; } diff --git a/omaha/net/network_config.h b/omaha/net/network_config.h index b35d17b23..aa7f0051b 100644 --- a/omaha/net/network_config.h +++ b/omaha/net/network_config.h @@ -200,12 +200,6 @@ class NetworkConfig { static int GetAccessType(const ProxyConfig& config); - // Returns s1 + delim + s2. Consider making it an utility function if - // more usage patterns are found. - static CString JoinStrings(const TCHAR* s1, - const TCHAR* s2, - const TCHAR* delim); - // Uses jsproxy to use a PAC proxy configuration file stored on the local // drive, instead of one sourced from WPAD. static HRESULT GetProxyForUrlLocal(const CString& url, diff --git a/omaha/net/network_config_unittest.cc b/omaha/net/network_config_unittest.cc index 815e8b4af..56ebcf8a1 100644 --- a/omaha/net/network_config_unittest.cc +++ b/omaha/net/network_config_unittest.cc @@ -23,6 +23,9 @@ #include "omaha/base/reg_key.h" #include "omaha/base/utils.h" #include "omaha/base/vistautil.h" +#include "omaha/common/config_manager.h" +#include "omaha/common/const_group_policy.h" +#include "omaha/goopdate/dm_messages.h" #include "omaha/net/http_client.h" #include "omaha/net/network_config.h" #include "omaha/testing/unit_test.h" @@ -68,15 +71,6 @@ TEST_F(NetworkConfigTest, GetAccessType) { WINHTTP_ACCESS_TYPE_NAMED_PROXY); } -TEST_F(NetworkConfigTest, JoinStrings) { - EXPECT_STREQ(NetworkConfig::JoinStrings(NULL, NULL, NULL), _T("")); - - CString result; - EXPECT_STREQ(NetworkConfig::JoinStrings(NULL, NULL, _T("-")), _T("-")); - EXPECT_STREQ(NetworkConfig::JoinStrings(_T("foo"), _T("bar"), _T("-")), - _T("foo-bar")); -} - TEST_F(NetworkConfigTest, GetUserAgentTest) { CString version(GetVersionString()); EXPECT_FALSE(version.IsEmpty()); @@ -190,7 +184,9 @@ TEST_F(NetworkConfigTest, ConfigurationOverride) { EXPECT_EQ(E_FAIL, network_config->GetConfigurationOverride(&actual)); } -TEST_F(NetworkConfigTest, GetProxyForUrlLocal) { +// This test fails on and after Win 11 because ::InternetGetProxyInfo() is +// no longer supported. +TEST_F(NetworkConfigTest, DISABLED_GetProxyForUrlLocal) { CString pac_file_path = app_util::GetModuleDirectory(NULL); ASSERT_FALSE(pac_file_path.IsEmpty()); pac_file_path.Append(_T("\\unittest_support\\localproxytest.pac")); @@ -235,5 +231,87 @@ TEST_F(NetworkConfigTest, ToString) { EXPECT_STREQ(expected_tostring, NetworkConfig::ToString(config)); } -} // namespace omaha +// This class is parameterized for Domain, Device Management, and +// CloudPolicyOverridesPlatformPolicy using +// ::testing::TestWithParam>. The first parameter +// is the bool for Domain, the second the bool for DM (Device Management), and +// the third the bool for CloudPolicyOverridesPlatformPolicy. +class NetworkConfigPolicyTest : + public ::testing::TestWithParam> { + protected: + virtual void SetUp() { + EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain, + IsDomain() ? 1UL : 0UL)); + if (IsDomain()) { + SetPolicyString(kRegValueProxyMode, kProxyModeAutoDetect); + } + + if (IsCloudPolicyOverridesPlatformPolicy()) { + SetPolicy(kRegValueCloudPolicyOverridesPlatformPolicy, 1UL); + } + + // Delete the ConfigManager instance so it is recreated, since the registry + // entries above need to be accounted for within the ConfigManager + // constructor. + ConfigManager::DeleteInstance(); + + if (IsDM()) { + CachedOmahaPolicy info; + info.is_managed = true; + info.is_initialized = true; + info.proxy_mode = kProxyModePacScript; + info.proxy_pac_url = _T("https://PS/"); + ConfigManager::Instance()->SetOmahaDMPolicies(info); + } + } + + virtual void TearDown() { + ConfigManager::Instance()->SetOmahaDMPolicies(CachedOmahaPolicy()); + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain)); + NetworkConfigManager::DeleteInstance(); + } + + bool IsDomain() { + return std::get<0>(GetParam()); + } + + bool IsDM() { + return std::get<1>(GetParam()); + } + + bool IsCloudPolicyOverridesPlatformPolicy() { + return IsDomain() && std::get<2>(GetParam()); + } + + bool IsDomainPredominant() { + return IsDomain() && (!IsCloudPolicyOverridesPlatformPolicy() || !IsDM()); + } +}; +INSTANTIATE_TEST_CASE_P(IsDomainIsDMIsCloudPolicyOverridesPlatformPolicy, + NetworkConfigPolicyTest, + ::testing::Combine(::testing::Bool(), + ::testing::Bool(), + ::testing::Bool())); + +TEST_P(NetworkConfigPolicyTest, ProxyConfig) { + NetworkConfig* network_config = NULL; + ASSERT_HRESULT_SUCCEEDED( + NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config)); + + std::vector proxy_configurations; + + // Detect the configurations. + ASSERT_HRESULT_SUCCEEDED(network_config->Detect()); + network_config->GetConfigurations().swap(proxy_configurations); + if (IsDomainPredominant()) { + EXPECT_TRUE(proxy_configurations[0].auto_detect); + } else if (IsDM()) { + EXPECT_STREQ(_T("https://PS/"), proxy_configurations[0].auto_config_url); + } +} + +} // namespace omaha diff --git a/omaha/net/network_request_impl.cc b/omaha/net/network_request_impl.cc index b26de0046..9d07c75df 100644 --- a/omaha/net/network_request_impl.cc +++ b/omaha/net/network_request_impl.cc @@ -529,7 +529,7 @@ HRESULT NetworkRequestImpl::DoSendHttpRequest( } // The algorithm is very rough meaning it does not look at the error - // returned by the Send and it blindly retries the call. For some errors + // returned by the Send and it always retries the call. For some errors // it may not make sense to retry at all, for example, let's say the // error is ERROR_DISK_FULL. NET_LOG(L3, (_T("[%s]"), url_)); diff --git a/omaha/net/network_request_unittest.cc b/omaha/net/network_request_unittest.cc index 15fb05ec1..d0897cb0c 100644 --- a/omaha/net/network_request_unittest.cc +++ b/omaha/net/network_request_unittest.cc @@ -53,11 +53,6 @@ class NetworkRequestTest network_config->Clear(); network_config->Add(new UpdateDevProxyDetector); - BrowserType browser_type(BROWSER_UNKNOWN); - GetDefaultBrowserType(&browser_type); - if (browser_type == BROWSER_FIREFOX) { - network_config->Add(new FirefoxProxyDetector); - } network_config->Add(new IEWPADProxyDetector); network_config->Add(new IEPACProxyDetector); network_config->Add(new IENamedProxyDetector); diff --git a/omaha/net/proxy_auth.cc b/omaha/net/proxy_auth.cc index 8045573c6..140acd8ac 100644 --- a/omaha/net/proxy_auth.cc +++ b/omaha/net/proxy_auth.cc @@ -25,6 +25,7 @@ #include "omaha/base/smart_handle.h" #include "omaha/base/string.h" #include "omaha/base/system_info.h" +#include "omaha/base/utils.h" #include "omaha/goopdate/cred_dialog.h" #include "omaha/third_party/smartany/scoped_any.h" @@ -33,10 +34,6 @@ using omaha::encrypt::DecryptData; namespace omaha { -static const GUID kPreIE7CredTypeGuid = { 0x5e7e8100, 0x9138, 0x11d1, - { 0x94, 0x5a, 0x00, 0xc0, 0x4f, 0xc3, 0x08, 0xff } }; -static const GUID kPreIE7CredSubtypeGuid = { 0 }; - #define kIE7CredKey "abe2869f-9b47-4cd9-a358-c22904dba7f7" bool ProxyAuth::IsPromptAllowed() { @@ -248,7 +245,7 @@ HRESULT ProxyAuth::SetProxyAuthScheme(const CString& proxy_server, // This approach (the key in particular) comes from a securityfocus posting: // http://www.securityfocus.com/archive/1/458115/30/0/threaded bool ProxyAuth::ReadFromIE7(const CString& server) { - scoped_library crypt_lib(::LoadLibrary(L"crypt32.dll")); + scoped_library crypt_lib(LoadSystemLibrary(_T("crypt32.dll"))); ASSERT1(crypt_lib); if (!crypt_lib) return false; @@ -264,7 +261,7 @@ bool ProxyAuth::ReadFromIE7(const CString& server) { // Load CredEnumerate and CredFree dynamically because they don't exist on // Win2K and so loading the GoogleDesktopCommon.dll otherwise. - scoped_library advapi_lib(::LoadLibrary(L"advapi32.dll")); + scoped_library advapi_lib(LoadSystemLibrary(_T("advapi32.dll"))); ASSERT1(advapi_lib); if (!advapi_lib) return false; @@ -339,7 +336,7 @@ bool ProxyAuth::ReadFromIE7(const CString& server) { // reading credentials from the IE6 is not supported anymore. bool ProxyAuth::ReadFromPreIE7(const CString& server) { #if (_MSC_VER < 1800) - scoped_library pstore_lib(::LoadLibrary(L"pstorec.dll")); + scoped_library pstore_lib(LoadSystemLibrary(_T("pstorec.dll")); ASSERT1(pstore_lib); if (!pstore_lib) return false; @@ -364,11 +361,11 @@ bool ProxyAuth::ReadFromPreIE7(const CString& server) { // the item_name returned by this iterator was a microsoft patent application: // http://www.patentstorm.us/patents/6272631-description.html CComPtr pstore; - VERIFY1(SUCCEEDED(hr = PStoreCreateInstance_fn(&pstore, NULL, NULL, 0))); + VERIFY_SUCCEEDED(hr = PStoreCreateInstance_fn(&pstore, NULL, NULL, 0)); if (SUCCEEDED(hr)) { CComPtr enum_types; - VERIFY1(SUCCEEDED(hr = pstore->EnumTypes(PST_KEY_CURRENT_USER, 0, - &enum_types))); + VERIFY_SUCCEEDED(hr = pstore->EnumTypes(PST_KEY_CURRENT_USER, 0, + &enum_types)); if (SUCCEEDED(hr)) { GUID type_guid = { 0 }; // Get the types one at a time @@ -377,8 +374,8 @@ bool ProxyAuth::ReadFromPreIE7(const CString& server) { continue; CComPtr enum_subtypes; - VERIFY1(SUCCEEDED(hr = pstore->EnumSubtypes(PST_KEY_CURRENT_USER, - &type_guid, 0, &enum_subtypes))); + VERIFY_SUCCEEDED(hr = pstore->EnumSubtypes(PST_KEY_CURRENT_USER, + &type_guid, 0, &enum_subtypes)); if (SUCCEEDED(hr)) { GUID subtype_guid = { 0 }; // Get the subtypes one at a time @@ -387,8 +384,8 @@ bool ProxyAuth::ReadFromPreIE7(const CString& server) { continue; CComPtr enum_items; - VERIFY1(SUCCEEDED(hr = pstore->EnumItems(PST_KEY_CURRENT_USER, - &type_guid, &subtype_guid, 0, &enum_items))); + VERIFY_SUCCEEDED(hr = pstore->EnumItems(PST_KEY_CURRENT_USER, + &type_guid, &subtype_guid, 0, &enum_items)); if (SUCCEEDED(hr)) { wchar_t* item_name = NULL; // Get the items one at a time @@ -396,9 +393,9 @@ bool ProxyAuth::ReadFromPreIE7(const CString& server) { DWORD data_length = 0; byte* data = NULL; - VERIFY1(SUCCEEDED(hr = pstore->ReadItem(PST_KEY_CURRENT_USER, + VERIFY_SUCCEEDED(hr = pstore->ReadItem(PST_KEY_CURRENT_USER, &type_guid, &subtype_guid, item_name, &data_length, &data, - NULL, 0))); + NULL, 0)); if (SUCCEEDED(hr)) { found = ParseCredsFromRawBuffer(data, data_length, &username, &password); diff --git a/omaha/net/simple_request.cc b/omaha/net/simple_request.cc index 07ec60e2b..9fce5e47b 100644 --- a/omaha/net/simple_request.cc +++ b/omaha/net/simple_request.cc @@ -49,6 +49,13 @@ namespace omaha { +namespace { + +// How many times should we retry when we get ERROR_WINHTTP_RESEND_REQUEST. +constexpr const int kMaxResendAttempts = 3; + +} // namespace + SimpleRequest::TransientRequestState::TransientRequestState() : port(0), is_https(false), @@ -73,7 +80,8 @@ SimpleRequest::SimpleRequest() proxy_auth_config_(NULL, CString()), low_priority_(false), callback_(NULL), - download_completed_(false) { + download_completed_(false), + resend_count_(0) { SafeCStringFormat(&user_agent_, _T("%s;winhttp"), NetworkConfig::GetUserAgent()); @@ -338,9 +346,9 @@ HRESULT SimpleRequest::Connect() { // Disable redirects for POST requests. if (IsPostRequest()) { - VERIFY1(SUCCEEDED( + VERIFY_SUCCEEDED( winhttp_adapter_->SetRequestOptionInt(WINHTTP_OPTION_DISABLE_FEATURE, - WINHTTP_DISABLE_REDIRECTS))); + WINHTTP_DISABLE_REDIRECTS)); } CString additional_headers = additional_headers_; @@ -454,9 +462,9 @@ HRESULT SimpleRequest::SendRequest() { kHeaderXProxyManualAuth); } uint32 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE; - VERIFY1(SUCCEEDED(winhttp_adapter_->AddRequestHeaders(headers, + VERIFY_SUCCEEDED(winhttp_adapter_->AddRequestHeaders(headers, -1, - flags))); + flags)); } const DWORD bytes_to_send = static_cast(request_buffer_length_); @@ -477,14 +485,21 @@ HRESULT SimpleRequest::SendRequest() { #if DEBUG LogResponseHeaders(); #endif - if (hr == ERROR_WINHTTP_RESEND_REQUEST) { + if (hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_RESEND_REQUEST)) { // Resend the request if needed, likely because the authentication // scheme requires many transactions on the same handle. + + // Avoid infinite resend loop. + if (++resend_count_ >= kMaxResendAttempts) + return hr; + continue; } else if (FAILED(hr)) { return hr; } + resend_count_ = 0; + hr = winhttp_adapter_->QueryRequestHeadersInt( WINHTTP_QUERY_STATUS_CODE, NULL, @@ -572,9 +587,9 @@ HRESULT SimpleRequest::SendRequest() { NetworkConfig* network_config = NULL; hr = network_manager.GetUserNetworkConfig(&network_config); if (SUCCEEDED(hr)) { - VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme( + VERIFY_SUCCEEDED(network_config->SetProxyAuthScheme( request_state_->proxy, request_state_->is_https, - request_scheme))); + request_scheme)); } } done = true; @@ -700,6 +715,21 @@ HRESULT SimpleRequest::PrepareRequest(HANDLE* file_handle) { HRESULT SimpleRequest::RequestData(HANDLE file_handle) { HRESULT hr = SendRequest(); if (FAILED(hr)) { + // WININET_E_DECODING_FAILED is equivalent to + // HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_FAILURE) as well as + // HRESULT_FROM_WIN32(ERROR_INTERNET_DECODING_FAILED). Distinguish the two. + if (hr == WININET_E_DECODING_FAILED) { + HRESULT secure_status_hr = + winhttp_adapter_->GetErrorFromSecureStatusFlag(); + + if (FAILED(secure_status_hr)) { + OPT_LOG(LE, (L"[SimpleRequest::RequestData]" + L"[Changing hresult from: 0x%8x to: 0x%8x]", + hr, secure_status_hr)); + hr = secure_status_hr; + } + } + return hr; } diff --git a/omaha/net/simple_request.h b/omaha/net/simple_request.h index 2e3e46bae..9af4ed306 100644 --- a/omaha/net/simple_request.h +++ b/omaha/net/simple_request.h @@ -185,6 +185,7 @@ class SimpleRequest : public HttpRequestInterface { std::unique_ptr request_state_; scoped_event event_resume_; bool download_completed_; + int resend_count_; DISALLOW_COPY_AND_ASSIGN(SimpleRequest); }; diff --git a/omaha/net/simple_request_unittest.cc b/omaha/net/simple_request_unittest.cc index 08cab5169..b6af90975 100644 --- a/omaha/net/simple_request_unittest.cc +++ b/omaha/net/simple_request_unittest.cc @@ -173,7 +173,7 @@ void SimpleRequestTest::SimpleGet(const CString& url, &user_agent); EXPECT_STREQ(simple_request.user_agent(), user_agent); - // Sanity check of some of the download metrics. + // Check of some of the download metrics. DownloadMetrics dm; EXPECT_TRUE(simple_request.download_metrics(&dm)); EXPECT_STREQ(url, dm.url); diff --git a/omaha/net/winhttp.cc b/omaha/net/winhttp.cc index ed31e2822..bb6de6e33 100644 --- a/omaha/net/winhttp.cc +++ b/omaha/net/winhttp.cc @@ -505,11 +505,11 @@ HRESULT WinHttp::CreateUrl(const TCHAR* scheme, url_comp.dwExtraInfoLength = static_cast(_tcslen(extra_info)); } - DWORD url_length = 0; + DWORD url_length = INTERNET_MAX_URL_LENGTH; bool res = !!winhttp_.WinHttpCreateUrl( &url_comp, flags, - url->GetBuffer(INTERNET_MAX_URL_LENGTH), + url->GetBuffer(url_length), &url_length); if (!res) { return HRESULTFromLastError(); diff --git a/omaha/net/winhttp_adapter.cc b/omaha/net/winhttp_adapter.cc index d5dcf5bdf..a3799fd52 100644 --- a/omaha/net/winhttp_adapter.cc +++ b/omaha/net/winhttp_adapter.cc @@ -28,7 +28,8 @@ WinHttpAdapter::WinHttpAdapter() async_call_type_(0), async_call_is_error_(0), async_bytes_available_(0), - async_bytes_read_(0) { + async_bytes_read_(0), + secure_status_flag_(0) { memset(&async_call_result_, 0, sizeof(async_call_result_)); NET_LOG(L3, (_T("[WinHttpAdapter::WinHttpAdapter][0x%p]"), this)); } @@ -66,11 +67,11 @@ void WinHttpAdapter::CloseHandles() { __mutexScope(lock_); if (request_handle_) { - VERIFY1(SUCCEEDED(http_client_->Close(request_handle_))); + VERIFY_SUCCEEDED(http_client_->Close(request_handle_)); request_handle_ = NULL; } if (connection_handle_) { - VERIFY1(SUCCEEDED(http_client_->Close(connection_handle_))); + VERIFY_SUCCEEDED(http_client_->Close(connection_handle_)); connection_handle_ = NULL; } } @@ -505,12 +506,17 @@ void __stdcall WinHttpAdapter::WinHttpStatusCallback(HINTERNET handle, case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: status_string = _T("request error"); break; - case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: + case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: { + DWORD detail_flag = *reinterpret_cast(info); + OPT_LOG(LE, (L"[WinHTTP Secure failure: 0x%x", detail_flag)); + http_adapter->secure_status_flag_ = detail_flag; + status_string = _T("https failure"); ASSERT1(info); ASSERT1(info_len == sizeof(DWORD)); - SafeCStringFormat(&info_string, _T("0x%x"), *static_cast(info)); + SafeCStringFormat(&info_string, _T("0x%x"), detail_flag); break; + } default: break; } @@ -531,6 +537,32 @@ void __stdcall WinHttpAdapter::WinHttpStatusCallback(HINTERNET handle, http_adapter->StatusCallback(handle, status, info, info_len); } +// Return new error base on bit in |secure_status_flag| returned by WinHttp. +HRESULT WinHttpAdapter::GetErrorFromSecureStatusFlag() const { + DWORD status; + if (secure_status_flag() & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED) { + status = ERROR_WINHTTP_SECURE_CERT_REV_FAILED; + } else if (secure_status_flag() & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT) { + status = ERROR_WINHTTP_SECURE_INVALID_CERT; + } else if (secure_status_flag() & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED) { + status = ERROR_WINHTTP_SECURE_CERT_REVOKED; + } else if (secure_status_flag() & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA) { + status = ERROR_WINHTTP_SECURE_INVALID_CA; + } else if (secure_status_flag() & + WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID) { + status = ERROR_WINHTTP_SECURE_CERT_CN_INVALID; + } else if (secure_status_flag() & + WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID) { + status = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID; + } else if (secure_status_flag() & + WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR) { + status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; + } else { + return S_OK; + } + + return HRESULT_FROM_WIN32(status); +} } // namespace omaha diff --git a/omaha/net/winhttp_adapter.h b/omaha/net/winhttp_adapter.h index 2b5c39cb6..5ba1b88cb 100644 --- a/omaha/net/winhttp_adapter.h +++ b/omaha/net/winhttp_adapter.h @@ -26,6 +26,8 @@ namespace omaha { +class WinHttpAdapterTest; + // Provides a sync-async adapter between the caller and the asynchronous // WinHttp client. Solves the issue of reliably canceling of WinHttp calls by // closing the handles and avoding the race condition between handle closing @@ -114,6 +116,9 @@ class WinHttpAdapter { CString server_name() const { return server_name_; } CString server_ip() const { return server_ip_; } + DWORD secure_status_flag() const { return secure_status_flag_; } + + HRESULT GetErrorFromSecureStatusFlag() const; private: @@ -146,10 +151,13 @@ class WinHttpAdapter { DWORD async_bytes_read_; scoped_event async_completion_event_; scoped_event async_handle_closing_event_; + DWORD secure_status_flag_; LLock lock_; DISALLOW_COPY_AND_ASSIGN(WinHttpAdapter); + + friend class WinHttpAdapterTest; }; } // namespace omaha diff --git a/omaha/net/winhttp_adapter_unittest.cc b/omaha/net/winhttp_adapter_unittest.cc index 3c6580177..e28f51ab8 100644 --- a/omaha/net/winhttp_adapter_unittest.cc +++ b/omaha/net/winhttp_adapter_unittest.cc @@ -92,4 +92,54 @@ TEST(WinHttpAdapter, OpenRequestClose) { EXPECT_HRESULT_SUCCEEDED(http_client->Close(session_handle)); } +class WinHttpAdapterTest : public testing::Test { +public: + WinHttpAdapterTest() {} +protected: + void SetSecureStatus(WinHttpAdapter* adapter, uint32_t flag) { + adapter->secure_status_flag_ = flag; + } +}; + +TEST_F(WinHttpAdapterTest, ErrorFromSecureStatusFlag) { + WinHttpAdapter winhttp_adapter; + EXPECT_HRESULT_SUCCEEDED(winhttp_adapter.GetErrorFromSecureStatusFlag()); + + SetSecureStatus(&winhttp_adapter, + WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_CERT_REV_FAILED)); + + SetSecureStatus(&winhttp_adapter, WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_INVALID_CERT)); + + SetSecureStatus(&winhttp_adapter, WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_CERT_REVOKED)); + + SetSecureStatus(&winhttp_adapter, WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_INVALID_CA)); + + SetSecureStatus(&winhttp_adapter, + WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_CERT_CN_INVALID)); + + SetSecureStatus(&winhttp_adapter, + WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_CERT_DATE_INVALID)); + + SetSecureStatus(&winhttp_adapter, + WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR); + EXPECT_EQ(winhttp_adapter.GetErrorFromSecureStatusFlag(), + HRESULT_FROM_WIN32(ERROR_WINHTTP_SECURE_CHANNEL_ERROR)); + + // Not a valid WINHTTP_CALLBACK_STATUS_FLAG_* value. + SetSecureStatus(&winhttp_adapter, 0x00000400); + EXPECT_HRESULT_SUCCEEDED(winhttp_adapter.GetErrorFromSecureStatusFlag()); +} + } // namespace omaha diff --git a/omaha/net/winhttp_vtable.cc b/omaha/net/winhttp_vtable.cc index 4b1b8354f..fbfd7de5a 100644 --- a/omaha/net/winhttp_vtable.cc +++ b/omaha/net/winhttp_vtable.cc @@ -15,6 +15,8 @@ #include "omaha/net/winhttp_vtable.h" +#include "omaha/base/utils.h" + namespace omaha { bool WinHttpVTable::Load() { @@ -22,9 +24,9 @@ bool WinHttpVTable::Load() { return true; } Clear(); - library_ = ::LoadLibrary(_T("winhttp")); + library_ = LoadSystemLibrary(_T("winhttp")); if (!library_) { - library_ = ::LoadLibrary(_T("winhttp5")); + library_ = LoadSystemLibrary(_T("winhttp5")); if (!library_) { return false; } diff --git a/omaha/omaha_version_utils.py b/omaha/omaha_version_utils.py index 40c0fc434..cefc4be11 100644 --- a/omaha/omaha_version_utils.py +++ b/omaha/omaha_version_utils.py @@ -19,6 +19,7 @@ _ONECLICK_PLUGIN_NAME = 'npGoogleOneClick' _UPDATE_PLUGIN_NAME = 'npGoogleUpdate' +_MAIN_EXE_BASE_NAME = 'GoogleUpdate' _CRASH_HANDLER_NAME = 'GoogleCrashHandler' # List of languages that are fully supported in the current build. @@ -97,7 +98,9 @@ VC120 = 1800 # VC2013/VC12 VC140 = 1900 # VC2015/VC14 VC150 = 1910 # VC2017 version 15.0-15.9 / VC14.1-14.16 -VC160 = 1920 # VC2019 version 16.0 / VC14.2 +VC160 = 1920 # VC2019 version 16.0 / VC14.20 +VC170 = 1930 # VC2019 version 17.0 / VC14.30 + def _IsSupportedOmaha2Version(omaha_version): """Returns true if omaha_version is an Omaha 2 version and is supported.""" @@ -109,29 +112,22 @@ def _IsSupportedOmaha2Version(omaha_version): # All languages supported by this script currently have the same set of # languages, so the omaha_version_info parameter is unused. def _GetMetainstallerPayloadFilenames(prefix, - update_plugin_filename, languages, omaha_version): """Returns list of metainstaller payload files for specified Omaha version.""" - plugin_dll_name = '%s%s' % (prefix, update_plugin_filename) # The list of files below needs to be kept in sync with the list in # SetupFiles::BuildFileLists(). # TODO(omaha): Move the other filename defines in main.scons into this file - # and allow all filenames to be customized. At the moment, while the plugin - # names are generated in one place due to version numbers, most of the other - # files (googleupdate.exe, goopdateres_*.dll, etc.) are hardcoded all over - # the place, and require a ton of point fixes to customize. + # and allow all filenames to be customized. payload_files = [ - 'GoogleUpdate.exe', + '%s.exe' % _MAIN_EXE_BASE_NAME, '%s.exe' % _CRASH_HANDLER_NAME, '%sgoopdate.dll' % (prefix), - plugin_dll_name, - 'GoogleUpdateHelper.msi', - 'GoogleUpdateBroker.exe', - 'GoogleUpdateOnDemand.exe', - 'GoogleUpdateComRegisterShell64.exe', - 'GoogleUpdateWebPlugin.exe', + '%sHelper.msi' % _MAIN_EXE_BASE_NAME, + '%sBroker.exe' % _MAIN_EXE_BASE_NAME, + '%sOnDemand.exe' % _MAIN_EXE_BASE_NAME, + '%sComRegisterShell64.exe' % _MAIN_EXE_BASE_NAME, '%spsmachine.dll' % (prefix), '%spsmachine_64.dll' % (prefix), '%spsuser.dll' % (prefix), @@ -139,10 +135,9 @@ def _GetMetainstallerPayloadFilenames(prefix, ] if _IsSupportedOmaha2Version(omaha_version): - payload_files.remove(plugin_dll_name) - payload_files.remove('GoogleUpdateBroker.exe') - payload_files.remove('GoogleUpdateOnDemand.exe') - payload_files.remove('GoogleUpdateComRegisterShell64.exe') + payload_files.remove('%sBroker.exe' % _MAIN_EXE_BASE_NAME) + payload_files.remove('%sOnDemand.exe' % _MAIN_EXE_BASE_NAME) + payload_files.remove('%sComRegisterShell64.exe' % _MAIN_EXE_BASE_NAME) payload_files.remove('psmachine.dll') payload_files.remove('psmachine_64.dll') payload_files.remove('psuser.dll') @@ -164,7 +159,14 @@ def _GetMetainstallerPayloadFilenames(prefix, omaha_version[1] >= 3 and (omaha_version[2] >= 32)): # added with 1.3.32.1 and later - payload_files.append('GoogleUpdateCore.exe') + payload_files.append('%sCore.exe' % _MAIN_EXE_BASE_NAME) + + if (omaha_version[0] >= 1 and + omaha_version[1] >= 3 and + (omaha_version[2] > 36 or + (omaha_version[2] == 36 and omaha_version[3] >= 61))): + # GoogleUpdateHelper.msi was removed with version 1.3.36.61. + payload_files.remove('%sHelper.msi' % _MAIN_EXE_BASE_NAME) for language in languages: payload_files += ['%sgoopdateres_%s.dll' % (prefix, language)] @@ -236,12 +238,7 @@ class OmahaVersionInfo(object): version_minor: Minor version. version_build: Build version. version_patch: Patch version. - oneclick_plugin_version: Version of the OneClick plug-in. - oneclick_plugin_filename: Name of the signed OneClick DLL. - update_plugin_version: Version of the Omaha 3 plug-in. - update_plugin_filename: Name of the signed Omaha 3 plug-in DLL. crash_handler_filename: Name of the Crash Handler EXE. - oneclick_signed_file_info: SignedFileInfo object for the OneClick DLL. """ def __init__(self, version_file): @@ -250,31 +247,11 @@ def __init__(self, version_file): self.filename_prefix = '' - # Objects containing more properties used to build the file. - self.oneclick_signed_file_info = SignedFileInfo( - _ONECLICK_PLUGIN_NAME, - 'dll', - self.oneclick_plugin_version) - self.plugin_signed_file_info = SignedFileInfo( - _UPDATE_PLUGIN_NAME, - 'dll', - self.update_plugin_version) - - # Simple properties for callers that only need the final filename. Not - # affected by internal build changes. - self.oneclick_plugin_filename = self.oneclick_signed_file_info.filename - self.update_plugin_filename = self.plugin_signed_file_info.filename - self.crash_handler_filename = _CRASH_HANDLER_NAME - def _ReadFile(self, version_file): """Reads and stores data from a VERSION file.""" execfile(version_file, globals()) - # Silence Pylint. Values from version_file are not defined in this file. - # E0602: Undefined variable. - # pylint: disable-msg=E0602 - if version_patch > 0: incrementing_value = version_patch incrementing_value_name = 'patch' @@ -291,19 +268,6 @@ def _ReadFile(self, version_file): self.version_build = version_build self.version_patch = version_patch - self.oneclick_plugin_version = oneclick_plugin_version - - # update_plugin_version does not exist in Omaha 2 VERSION file. Handle this. - try: - self.update_plugin_version = update_plugin_version - except NameError: - if _IsSupportedOmaha2Version(self.GetVersion()): - self.update_plugin_version = -1 - else: - raise - - # pylint: enable-msg=E0602 - def MakeTestVersion(self, delta, prefix): """Changes this object to be for a TEST version of Omaha.""" @@ -340,7 +304,6 @@ def GetSupportedLanguages(self): def GetMetainstallerPayloadFilenames(self): """Returns list of metainstaller payload files for this version of Omaha.""" return _GetMetainstallerPayloadFilenames(self.filename_prefix, - self.update_plugin_filename, self.GetSupportedLanguages(), self.GetVersion()) @@ -361,4 +324,3 @@ def __init__(self, unversioned_name, extension, file_version=None): self.unsigned_filename_base = '%s_unsigned' % base_name self.unsigned_filename = '%s.%s' % (self.unsigned_filename_base, extension) - diff --git a/omaha/plugins/base/build.scons b/omaha/plugins/base/build.scons deleted file mode 100644 index 2c28bd491..000000000 --- a/omaha/plugins/base/build.scons +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -Import('env') - -# Build plugin base. -plugin_base_env = env.Clone() -plugin_base_env['CCFLAGS'] = [ - '/wd4100', -], -plugin_base_env.ComponentLibrary( - lib_name='plugin_base', - source=[ - 'np_entry.cc', - 'npn_gate.cc', - 'npp_gate.cc', - ], -) diff --git a/omaha/plugins/base/np_entry.cc b/omaha/plugins/base/np_entry.cc deleted file mode 100644 index c3ad7b3ad..000000000 --- a/omaha/plugins/base/np_entry.cc +++ /dev/null @@ -1,321 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: NPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Netscape Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the NPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the NPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -////////////////////////////////////////////////////////////// -// -// Main plugin entry point implementation -- exports from the -// plugin library -// - -#include -#include "omaha/plugins/base/npplat.h" -#include "omaha/plugins/base/pluginbase.h" - -#ifdef _WINDOWS -#define OSCALL WINAPI -#else -#define OSCALL -#endif - -NPNetscapeFuncs NPNFuncs = {sizeof(NPNFuncs)}; - -NPError OSCALL NP_Shutdown() -{ - NS_PluginShutdown(); - return NPERR_NO_ERROR; -} - -static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs) -{ - if (aNPPFuncs == NULL || aNPPFuncs->size < sizeof(NPPluginFuncs)) { - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - // Set up the plugin function table that Netscape will use to - // call us. Netscape needs to know about our version and size - // and have a UniversalProcPointer for every function we implement. - - aNPPFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; -#ifdef XP_MAC - aNPPFuncs->newp = NewNPP_NewProc(Private_New); - aNPPFuncs->destroy = NewNPP_DestroyProc(Private_Destroy); - aNPPFuncs->setwindow = NewNPP_SetWindowProc(Private_SetWindow); - aNPPFuncs->newstream = NewNPP_NewStreamProc(Private_NewStream); - aNPPFuncs->destroystream = NewNPP_DestroyStreamProc(Private_DestroyStream); - aNPPFuncs->asfile = NewNPP_StreamAsFileProc(Private_StreamAsFile); - aNPPFuncs->writeready = NewNPP_WriteReadyProc(Private_WriteReady); - aNPPFuncs->write = NewNPP_WriteProc(Private_Write); - aNPPFuncs->print = NewNPP_PrintProc(Private_Print); - aNPPFuncs->event = NewNPP_HandleEventProc(Private_HandleEvent); - aNPPFuncs->urlnotify = NewNPP_URLNotifyProc(Private_URLNotify); - aNPPFuncs->getvalue = NewNPP_GetValueProc(Private_GetValue); - aNPPFuncs->setvalue = NewNPP_SetValueProc(Private_SetValue); -#else - aNPPFuncs->newp = NPP_New; - aNPPFuncs->destroy = NPP_Destroy; - aNPPFuncs->setwindow = NPP_SetWindow; - aNPPFuncs->newstream = NPP_NewStream; - aNPPFuncs->destroystream = NPP_DestroyStream; - aNPPFuncs->asfile = NPP_StreamAsFile; - aNPPFuncs->writeready = NPP_WriteReady; - aNPPFuncs->write = NPP_Write; - aNPPFuncs->print = NPP_Print; - aNPPFuncs->event = NPP_HandleEvent; - aNPPFuncs->urlnotify = NPP_URLNotify; - aNPPFuncs->getvalue = NPP_GetValue; - aNPPFuncs->setvalue = NPP_SetValue; -#endif -#ifdef OJI - aNPPFuncs->javaClass = NULL; -#endif - - return NPERR_NO_ERROR; -} - -static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs) -{ - if (aNPNFuncs == NULL) { - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - if (HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR) { - return NPERR_INCOMPATIBLE_VERSION_ERROR; - } - - memcpy(&NPNFuncs, aNPNFuncs, std::min(static_cast(aNPNFuncs->size), - sizeof(NPNFuncs))); - - if (!NPNFuncs.memalloc || - !NPNFuncs.memfree || - !NPNFuncs.createobject || - !NPNFuncs.retainobject || - !NPNFuncs.releaseobject || - !NPNFuncs.utf8fromidentifier || - !NPNFuncs.releasevariantvalue || - !NPNFuncs.getstringidentifier || - !NPNFuncs.getvalue || - !NPNFuncs.setexception || - !NPNFuncs.getproperty || - // Used only by oneclick plugin. - !NPNFuncs.invokeDefault) { - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - return NPERR_NO_ERROR; -} - -// -// Some exports are different on different platforms -// - -/**************************************************/ -/* */ -/* Windows */ -/* */ -/**************************************************/ -#ifdef XP_WIN - -NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs) -{ - NPError rv = fillNetscapeFunctionTable(aNPNFuncs); - if(rv != NPERR_NO_ERROR) - return rv; - - return NS_PluginInitialize(); -} - -NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs) -{ - return fillPluginFunctionTable(aNPPFuncs); -} - -#endif //XP_WIN - -/**************************************************/ -/* */ -/* Unix */ -/* */ -/**************************************************/ -#ifdef XP_UNIX - -NPError NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs) -{ - NPError rv = fillNetscapeFunctionTable(aNPNFuncs); - if(rv != NPERR_NO_ERROR) - return rv; - - rv = fillPluginFunctionTable(aNPPFuncs); - if(rv != NPERR_NO_ERROR) - return rv; - - return NS_PluginInitialize(); -} - -char * NP_GetMIMEDescription(void) -{ - return NPP_GetMIMEDescription(); -} - -NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue) -{ - return NS_PluginGetValue(aVariable, aValue); -} - -#endif //XP_UNIX - -/**************************************************/ -/* */ -/* Mac */ -/* */ -/**************************************************/ -#ifdef XP_MAC - -#if !TARGET_API_MAC_CARBON -QDGlobals* gQDPtr; // Pointer to Netscape's QuickDraw globals -#endif - -short gResFile; // Refnum of the plugin's resource file - -NPError Private_Initialize(void) -{ - NPError rv = NS_PluginInitialize(); - return rv; -} - -void Private_Shutdown(void) -{ - NS_PluginShutdown(); - __destroy_global_chain(); -} - -void SetUpQD(void); - -void SetUpQD(void) -{ - ProcessSerialNumber PSN; - FSSpec myFSSpec; - Str63 name; - ProcessInfoRec infoRec; - OSErr result = noErr; - CFragConnectionID connID; - Str255 errName; - - // Memorize the plugin¹s resource file refnum for later use. - gResFile = CurResFile(); - -#if !TARGET_API_MAC_CARBON - // Ask the system if CFM is available. - long response; - OSErr err = Gestalt(gestaltCFMAttr, &response); - Boolean hasCFM = BitTst(&response, 31-gestaltCFMPresent); - - if (hasCFM) { - // GetProcessInformation takes a process serial number and - // will give us back the name and FSSpec of the application. - // See the Process Manager in IM. - infoRec.processInfoLength = sizeof(ProcessInfoRec); - infoRec.processName = name; - infoRec.processAppSpec = &myFSSpec; - - PSN.highLongOfPSN = 0; - PSN.lowLongOfPSN = kCurrentProcess; - - result = GetProcessInformation(&PSN, &infoRec); - } - else - // If no CFM installed, assume it must be a 68K app. - result = -1; - - if (result == noErr) { - // Now that we know the app name and FSSpec, we can call GetDiskFragment - // to get a connID to use in a subsequent call to FindSymbol (it will also - // return the address of ³main² in app, which we ignore). If GetDiskFragment - // returns an error, we assume the app must be 68K. - Ptr mainAddr; - result = GetDiskFragment(infoRec.processAppSpec, 0L, 0L, infoRec.processName, - kReferenceCFrag, &connID, (Ptr*)&mainAddr, errName); - } - - if (result == noErr) { - // The app is a PPC code fragment, so call FindSymbol - // to get the exported ³qd² symbol so we can access its - // QuickDraw globals. - CFragSymbolClass symClass; - result = FindSymbol(connID, "\pqd", (Ptr*)&gQDPtr, &symClass); - } - else { - // The app is 68K, so use its A5 to compute the address - // of its QuickDraw globals. - gQDPtr = (QDGlobals*)(*((long*)SetCurrentA5()) - (sizeof(QDGlobals) - sizeof(GrafPtr))); - } -#endif /* !TARGET_API_MAC_CARBON */ -} - -NPError main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp); - -#if !TARGET_API_MAC_CARBON -#pragma export on -#if GENERATINGCFM -RoutineDescriptor mainRD = BUILD_ROUTINE_DESCRIPTOR(uppNPP_MainEntryProcInfo, main); -#endif -#pragma export off -#endif /* !TARGET_API_MAC_CARBON */ - - -NPError main(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs, NPP_ShutdownUPP* aUnloadUpp) -{ - NPError rv = NPERR_NO_ERROR; - - if (aUnloadUpp == NULL) - rv = NPERR_INVALID_FUNCTABLE_ERROR; - - if (rv == NPERR_NO_ERROR) - rv = fillNetscapeFunctionTable(aNPNFuncs); - - if (rv == NPERR_NO_ERROR) { - // defer static constructors until the global functions are initialized. - __InitCode__(); - rv = fillPluginFunctionTable(aNPPFuncs); - } - - *aUnloadUpp = NewNPP_ShutdownProc(Private_Shutdown); - SetUpQD(); - rv = Private_Initialize(); - - return rv; -} -#endif //XP_MAC diff --git a/omaha/plugins/base/npn_gate.cc b/omaha/plugins/base/npn_gate.cc deleted file mode 100644 index 5aee8f3a3..000000000 --- a/omaha/plugins/base/npn_gate.cc +++ /dev/null @@ -1,259 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: NPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Netscape Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the NPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the NPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -//////////////////////////////////////////////////////////// -// -// Implementation of Netscape entry points (NPN_*) -// -#include "omaha/plugins/base/npplat.h" - -extern NPNetscapeFuncs NPNFuncs; - -/* -void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor) -{ - *plugin_major = NP_VERSION_MAJOR; - *plugin_minor = NP_VERSION_MINOR; - *netscape_major = HIBYTE(NPNFuncs.version); - *netscape_minor = LOBYTE(NPNFuncs.version); -} - -NPError NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData) -{ - int navMinorVers = NPNFuncs.version & 0xFF; - NPError rv = NPERR_NO_ERROR; - - if( navMinorVers >= NPVERS_HAS_NOTIFICATION ) - rv = CallNPN_GetURLNotifyProc(NPNFuncs.geturlnotify, instance, url, target, notifyData); - else - rv = NPERR_INCOMPATIBLE_VERSION_ERROR; - - return rv; -} - -NPError NPN_GetURL(NPP instance, const char *url, const char *target) -{ - NPError rv = CallNPN_GetURLProc(NPNFuncs.geturl, instance, url, target); - return rv; -} - -NPError NPN_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData) -{ - int navMinorVers = NPNFuncs.version & 0xFF; - NPError rv = NPERR_NO_ERROR; - - if( navMinorVers >= NPVERS_HAS_NOTIFICATION ) - rv = CallNPN_PostURLNotifyProc(NPNFuncs.posturlnotify, instance, url, window, len, buf, file, notifyData); - else - rv = NPERR_INCOMPATIBLE_VERSION_ERROR; - - return rv; -} - -NPError NPN_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file) -{ - NPError rv = CallNPN_PostURLProc(NPNFuncs.posturl, instance, url, window, len, buf, file); - return rv; -} - -NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) -{ - NPError rv = CallNPN_RequestReadProc(NPNFuncs.requestread, stream, rangeList); - return rv; -} - -NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream) -{ - int navMinorVersion = NPNFuncs.version & 0xFF; - - NPError rv = NPERR_NO_ERROR; - - if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT ) - rv = CallNPN_NewStreamProc(NPNFuncs.newstream, instance, type, target, stream); - else - rv = NPERR_INCOMPATIBLE_VERSION_ERROR; - - return rv; -} - -int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer) -{ - int navMinorVersion = NPNFuncs.version & 0xFF; - int32 rv = 0; - - if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT ) - rv = CallNPN_WriteProc(NPNFuncs.write, instance, stream, len, buffer); - else - rv = -1; - - return rv; -} - -NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason) -{ - int navMinorVersion = NPNFuncs.version & 0xFF; - NPError rv = NPERR_NO_ERROR; - - if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT ) - rv = CallNPN_DestroyStreamProc(NPNFuncs.destroystream, instance, stream, reason); - else - rv = NPERR_INCOMPATIBLE_VERSION_ERROR; - - return rv; -} - -void NPN_Status(NPP instance, const char *message) -{ - CallNPN_StatusProc(NPNFuncs.status, instance, message); -} - -const char* NPN_UserAgent(NPP instance) -{ - const char * rv = NULL; - rv = CallNPN_UserAgentProc(NPNFuncs.uagent, instance); - return rv; -} -*/ - -void* NPN_MemAlloc(uint32 size) -{ - void* rv = NULL; - return NPNFuncs.memalloc(size); -} - -void NPN_MemFree(void* ptr) -{ - NPNFuncs.memfree(ptr); -} - -/* -uint32 NPN_MemFlush(uint32 size) -{ - uint32 rv = CallNPN_MemFlushProc(NPNFuncs.memflush, size); - return rv; -} - -void NPN_ReloadPlugins(NPBool reloadPages) -{ - CallNPN_ReloadPluginsProc(NPNFuncs.reloadplugins, reloadPages); -} - -#ifdef OJI -JRIEnv* NPN_GetJavaEnv(void) -{ - JRIEnv * rv = NULL; - rv = CallNPN_GetJavaEnvProc(NPNFuncs.getJavaEnv); - return rv; -} - -jref NPN_GetJavaPeer(NPP instance) -{ - jref rv; - rv = CallNPN_GetJavaPeerProc(NPNFuncs.getJavaPeer, instance); - return rv; -} -#endif -*/ - -NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value) -{ - return NPNFuncs.getvalue(instance, variable, value); -} - -/* -NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value) -{ - NPError rv = CallNPN_SetValueProc(NPNFuncs.setvalue, instance, variable, value); - return rv; -} - -void NPN_InvalidateRect(NPP instance, NPRect *invalidRect) -{ - CallNPN_InvalidateRectProc(NPNFuncs.invalidaterect, instance, invalidRect); -} - -void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion) -{ - CallNPN_InvalidateRegionProc(NPNFuncs.invalidateregion, instance, invalidRegion); -} - -void NPN_ForceRedraw(NPP instance) -{ - CallNPN_ForceRedrawProc(NPNFuncs.forceredraw, instance); -} -*/ - -// Used by the Omaha 3 plugin. -NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier) { - return NPNFuncs.utf8fromidentifier(identifier); -} - -NPObject* NPN_RetainObject(NPObject* obj) { - return NPNFuncs.retainobject(obj); -} - -void NPN_ReleaseObject(NPObject* obj) { - NPNFuncs.releaseobject(obj); -} - -NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name) { - return NPNFuncs.getstringidentifier(name); -} - -NPObject* NPN_CreateObject(NPP npp, NPClass* aClass) { - return NPNFuncs.createobject(npp, aClass); -} - -void NPN_SetException(NPObject* obj, const NPUTF8* message) { - NPNFuncs.setexception(obj, message); -} - -void NPN_ReleaseVariantValue(NPVariant* variant) { - NPNFuncs.releasevariantvalue(variant); -} - -bool NPN_GetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName, - NPVariant* result) { - return NPNFuncs.getproperty(npp, obj, propertyName, result); -} - -// Some additional stuff that's used by the oneclick plugin -bool NPN_InvokeDefault(NPP npp, NPObject* obj, const NPVariant* args, - uint32_t argCount, NPVariant* result) { - return NPNFuncs.invokeDefault(npp, obj, args, argCount, result); -} diff --git a/omaha/plugins/base/npp_gate.cc b/omaha/plugins/base/npp_gate.cc deleted file mode 100644 index ee9da881f..000000000 --- a/omaha/plugins/base/npp_gate.cc +++ /dev/null @@ -1,358 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: NPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Netscape Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the NPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the NPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -//////////////////////////////////////////////////////////// -// -// Implementation of plugin entry points (NPP_*) -// -#include "omaha/plugins/base/pluginbase.h" - -// here the plugin creates a plugin instance object which -// will be associated with this newly created NPP instance and -// will do all the neccessary job -NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - NPError rv = NPERR_NO_ERROR; - - // create a new plugin instance object - // initialization will be done when the associated window is ready - nsPluginCreateData ds; - - ds.instance = instance; - ds.type = pluginType; - ds.mode = mode; - ds.argc = argc; - ds.argn = argn; - ds.argv = argv; - ds.saved = saved; - - nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds); - if(plugin == NULL) - return NPERR_OUT_OF_MEMORY_ERROR; - - // associate the plugin instance object with NPP instance - instance->pdata = (void *)plugin; - return rv; -} - -// here is the place to clean up and destroy the nsPluginInstance object -NPError NPP_Destroy (NPP instance, NPSavedData** save) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - NPError rv = NPERR_NO_ERROR; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin != NULL) { - plugin->shut(); - NS_DestroyPluginInstance(plugin); - } - return rv; -} - -// during this call we know when the plugin window is ready or -// is about to be destroyed so we can do some gui specific -// initialization and shutdown -NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - NPError rv = NPERR_NO_ERROR; - - if(pNPWindow == NULL) - return NPERR_GENERIC_ERROR; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - - if(plugin == NULL) - return NPERR_GENERIC_ERROR; - - // window just created - if(!plugin->isInitialized() && (pNPWindow->window != NULL)) { - if(!plugin->init(pNPWindow)) { - NS_DestroyPluginInstance(plugin); - return NPERR_MODULE_LOAD_FAILED_ERROR; - } - } - - // window goes away - if((pNPWindow->window == NULL) && plugin->isInitialized()) - return plugin->SetWindow(pNPWindow); - - // window resized? - if(plugin->isInitialized() && (pNPWindow->window != NULL)) - return plugin->SetWindow(pNPWindow); - - // this should not happen, nothing to do - if((pNPWindow->window == NULL) && !plugin->isInitialized()) - return plugin->SetWindow(pNPWindow); - - return rv; -} - -NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return NPERR_GENERIC_ERROR; - - NPError rv = plugin->NewStream(type, stream, seekable, stype); - return rv; -} - -int32 NPP_WriteReady (NPP instance, NPStream *stream) -{ - if(instance == NULL) - return 0x0fffffff; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return 0x0fffffff; - - int32 rv = plugin->WriteReady(stream); - return rv; -} - -int32 NPP_Write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer) -{ - if(instance == NULL) - return len; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return len; - - int32 rv = plugin->Write(stream, offset, len, buffer); - return rv; -} - -NPError NPP_DestroyStream (NPP instance, NPStream *stream, NPError reason) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return NPERR_GENERIC_ERROR; - - NPError rv = plugin->DestroyStream(stream, reason); - return rv; -} - -void NPP_StreamAsFile (NPP instance, NPStream* stream, const char* fname) -{ - if(instance == NULL) - return; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return; - - plugin->StreamAsFile(stream, fname); -} - -void NPP_Print (NPP instance, NPPrint* printInfo) -{ - if(instance == NULL) - return; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return; - - plugin->Print(printInfo); -} - -void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) -{ - if(instance == NULL) - return; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return; - - plugin->URLNotify(url, reason, notifyData); -} - -NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return NPERR_GENERIC_ERROR; - - NPError rv = plugin->GetValue(variable, value); - return rv; -} - -NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) -{ - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return NPERR_GENERIC_ERROR; - - NPError rv = plugin->SetValue(variable, value); - return rv; -} - -int16 NPP_HandleEvent(NPP instance, void* event) -{ - if(instance == NULL) - return 0; - - nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata; - if(plugin == NULL) - return 0; - - uint16 rv = plugin->HandleEvent(event); - return rv; -} - -#ifdef OJI -jref NPP_GetJavaClass (void) -{ - return NULL; -} -#endif - -/**************************************************/ -/* */ -/* Mac */ -/* */ -/**************************************************/ - -// Mac needs these wrappers, see npplat.h for more info - -#ifdef XP_MAC - -NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) -{ - NPError rv = NPP_New(pluginType, instance, mode, argc, argn, argv, saved); - return rv; -} - -NPError Private_Destroy(NPP instance, NPSavedData** save) -{ - NPError rv = NPP_Destroy(instance, save); - return rv; -} - -NPError Private_SetWindow(NPP instance, NPWindow* window) -{ - NPError rv = NPP_SetWindow(instance, window); - return rv; -} - -NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) -{ - NPError rv = NPP_NewStream(instance, type, stream, seekable, stype); - return rv; -} - -int32 Private_WriteReady(NPP instance, NPStream* stream) -{ - int32 rv = NPP_WriteReady(instance, stream); - return rv; -} - -int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer) -{ - int32 rv = NPP_Write(instance, stream, offset, len, buffer); - return rv; -} - -void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname) -{ - NPP_StreamAsFile(instance, stream, fname); -} - - -NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason) -{ - NPError rv = NPP_DestroyStream(instance, stream, reason); - return rv; -} - -int16 Private_HandleEvent(NPP instance, void* event) -{ - int16 rv = NPP_HandleEvent(instance, event); - return rv; -} - -void Private_Print(NPP instance, NPPrint* platformPrint) -{ - NPP_Print(instance, platformPrint); -} - -void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) -{ - NPP_URLNotify(instance, url, reason, notifyData); -} - -jref Private_GetJavaClass(void) -{ - return NULL; -} - -NPError Private_GetValue(NPP instance, NPPVariable variable, void *result) -{ - NPError rv = NPP_GetValue(instance, variable, result); - return rv; -} - -NPError Private_SetValue(NPP instance, NPNVariable variable, void *value) -{ - NPError rv = NPP_SetValue(instance, variable, value); - return rv; -} - -#endif //XP_MAC diff --git a/omaha/plugins/base/npplat.h b/omaha/plugins/base/npplat.h deleted file mode 100644 index e9700c75e..000000000 --- a/omaha/plugins/base/npplat.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: NPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Netscape Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the NPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the NPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef OMAHA_PLUGINS_COMMON_NPPLAT_H_ -#define OMAHA_PLUGINS_COMMON_NPPLAT_H_ - -/**************************************************/ -/* */ -/* Windows */ -/* */ -/**************************************************/ -#ifdef XP_WIN -#include "windows.h" -#endif //XP_WIN - -/**************************************************/ -/* */ -/* Unix */ -/* */ -/**************************************************/ -#ifdef XP_UNIX -#include -#endif //XP_UNIX - -/**************************************************/ -/* */ -/* Mac */ -/* */ -/**************************************************/ -#ifdef XP_MAC - -#include -#include -#include -#include -#include -#include - -#include "jri.h" - -// The Mixed Mode procInfos defined in npupp.h assume Think C- -// style calling conventions. These conventions are used by -// Metrowerks with the exception of pointer return types, which -// in Metrowerks 68K are returned in A0, instead of the standard -// D0. Thus, since NPN_MemAlloc and NPN_UserAgent return pointers, -// Mixed Mode will return the values to a 68K plugin in D0, but -// a 68K plugin compiled by Metrowerks will expect the result in -// A0. The following pragma forces Metrowerks to use D0 instead. -// -#ifdef __MWERKS__ -#ifndef powerc -#pragma pointers_in_D0 -#endif -#endif - -#ifdef __MWERKS__ -#ifndef powerc -#pragma pointers_in_A0 -#endif -#endif - -// The following fix for static initializers (which fixes a preious -// incompatibility with some parts of PowerPlant, was submitted by -// Jan Ulbrich. -#ifdef __MWERKS__ - #ifdef __cplusplus - extern "C" { - #endif - #ifndef powerc - extern void __InitCode__(void); - #else - extern void __sinit(void); - #define __InitCode__ __sinit - #endif - extern void __destroy_global_chain(void); - #ifdef __cplusplus - } - #endif // __cplusplus -#endif // __MWERKS__ - -// Wrapper functions for all calls from Netscape to the plugin. -// These functions let the plugin developer just create the APIs -// as documented and defined in npapi.h, without needing to -// install those functions in the function table or worry about -// setting up globals for 68K plugins. -NPError Private_Initialize(void); -void Private_Shutdown(void); -NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved); -NPError Private_Destroy(NPP instance, NPSavedData** save); -NPError Private_SetWindow(NPP instance, NPWindow* window); -NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype); -NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason); -int32 Private_WriteReady(NPP instance, NPStream* stream); -int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer); -void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname); -void Private_Print(NPP instance, NPPrint* platformPrint); -int16 Private_HandleEvent(NPP instance, void* event); -void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData); -jref Private_GetJavaClass(void); -NPError Private_GetValue(NPP instance, NPPVariable variable, void *result); -NPError Private_SetValue(NPP instance, NPNVariable variable, void *value); - -#endif //XP_MAC - -#include "third_party/npapi/bindings/nphostapi.h" - -#ifndef HIBYTE -#define HIBYTE(i) (i >> 8) -#endif - -#ifndef LOBYTE -#define LOBYTE(i) (i & 0xff) -#endif - -#endif // OMAHA_PLUGINS_COMMON_NPPLAT_H_ diff --git a/omaha/plugins/base/pluginbase.h b/omaha/plugins/base/pluginbase.h deleted file mode 100644 index 259bc7822..000000000 --- a/omaha/plugins/base/pluginbase.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: NPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Netscape Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the NPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the NPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef OMAHA_PLUGINS_COMMON_PLUGINBASE_H_ -#define OMAHA_PLUGINS_COMMON_PLUGINBASE_H_ - -#include "omaha/plugins/base/npplat.h" - -struct nsPluginCreateData -{ - NPP instance; - NPMIMEType type; - uint16 mode; - int16 argc; - char** argn; - char** argv; - NPSavedData* saved; -}; - -#pragma warning(push) -#pragma warning(disable:4100) // unreferenced formal parameter -class nsPluginInstanceBase -{ -public: - // these three methods must be implemented in the derived - // class platform specific way - virtual NPBool init(NPWindow* aWindow) = 0; - virtual void shut() = 0; - virtual NPBool isInitialized() = 0; - - // implement all or part of those methods in the derived - // class as needed - virtual NPError SetWindow(NPWindow* pNPWindow) { return NPERR_NO_ERROR; } - virtual NPError NewStream(NPMIMEType type, NPStream* stream, - NPBool seekable, uint16* stype) { return NPERR_NO_ERROR; } - virtual NPError DestroyStream(NPStream *stream, NPError reason) { return NPERR_NO_ERROR; } - virtual void StreamAsFile(NPStream* stream, const char* fname) { return; } - virtual int32 WriteReady(NPStream *stream) { return 0x0fffffff; } - virtual int32 Write(NPStream *stream, int32 offset, - int32 len, void *buffer) { return len; } - virtual void Print(NPPrint* printInfo) { return; } - virtual uint16 HandleEvent(void* event) { return 0; } - virtual void URLNotify(const char* url, NPReason reason, - void* notifyData) { return; } - virtual NPError GetValue(NPPVariable variable, void *value) { return NPERR_NO_ERROR; } - virtual NPError SetValue(NPNVariable variable, void *value) { return NPERR_NO_ERROR; } -}; -#pragma warning(pop) - -// functions that should be implemented for each specific plugin - -// creation and destruction of the object of the derived class -nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct); -void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin); - -// global plugin initialization and shutdown -NPError NS_PluginInitialize(); -void NS_PluginShutdown(); - -#ifdef XP_UNIX -// global to get plugins name & description -NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue); -#endif - -#endif // OMAHA_PLUGINS_COMMON_PLUGINBASE_H_ diff --git a/omaha/plugins/build.scons b/omaha/plugins/build.scons deleted file mode 100644 index 15a234520..000000000 --- a/omaha/plugins/build.scons +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -Import('env') - -env.BuildSConscript('base') -env.BuildSConscript('update') diff --git a/omaha/plugins/update/activex/oneclick_control.cc b/omaha/plugins/update/activex/oneclick_control.cc deleted file mode 100644 index 955cea1c7..000000000 --- a/omaha/plugins/update/activex/oneclick_control.cc +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2008-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Implementation of the OneClick Plugin. - -#include "omaha/plugins/update/activex/oneclick_control.h" - -#include -#include -#include -#include - -#include "omaha/base/error.h" -#include "omaha/base/file.h" -#include "omaha/base/string.h" -#include "omaha/base/system.h" -#include "omaha/base/vistautil.h" -#include "omaha/common/command_line.h" -#include "omaha/common/command_line_builder.h" -#include "omaha/common/const_cmd_line.h" -#include "omaha/common/goopdate_utils.h" -#include "omaha/common/update3_utils.h" -#include "omaha/common/webplugin_utils.h" -#include "omaha/goopdate/app_command.h" -#include "omaha/goopdate/app_manager.h" -#include "omaha/third_party/smartany/scoped_any.h" -#include "goopdate/omaha3_idl.h" - -namespace omaha { - -OneClickControl::OneClickControl() { - CORE_LOG(L2, (_T("[OneClickControl::OneClickControl]"))); -} - -OneClickControl::~OneClickControl() { - CORE_LOG(L2, (_T("[OneClickControl::~OneClickControl]"))); -} - -STDMETHODIMP OneClickControl::Install(BSTR cmd_line_args, - VARIANT* success_callback, - VARIANT* failure_callback) { - ASSERT1(cmd_line_args && cmd_line_args[0]); - ASSERT1(VariantIsValidCallback(success_callback)); - ASSERT1(VariantIsValidCallback(failure_callback)); - - if (!site_lock_.InApprovedDomain(this)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!cmd_line_args || - !cmd_line_args[0] || - !VariantIsValidCallback(success_callback) || - !VariantIsValidCallback(failure_callback) ) { - return E_INVALIDARG; - } - - const TCHAR kExtraArgsInstallPrefix[] = _T("/install \""); - const TCHAR kExtraArgsSuffix[] = _T("\""); - if (!String_StartsWith(cmd_line_args, kExtraArgsInstallPrefix, false) || - !String_EndsWith(cmd_line_args, kExtraArgsSuffix, false)) { - return E_INVALIDARG; - } - - CORE_LOG(L2, (_T("[OneClickControl::Install][cmd_line \"%s\"]"), - static_cast(CW2CT(cmd_line_args)))); - - // We trim cmd_line_args to just the extra args and thunk to Install2(). - CString extra_args(cmd_line_args); - extra_args = extra_args.Mid(arraysize(kExtraArgsInstallPrefix) - 1); - extra_args.Delete(extra_args.GetLength() - 1); - - HRESULT hr = Install2(extra_args.AllocSysString()); - if (SUCCEEDED(hr)) { - InvokeJavascriptCallback(success_callback, NULL); - } else { - CORE_LOG(LE, (_T("[DoOneClickInstallInternal failed][0x%08x]"), hr)); - InvokeJavascriptCallback(failure_callback, &hr); - } - - // Return success in all cases. The failure callback has already been called - // above, and we don't want to cause a failure path to be called again when - // the JavaScript catches the exception. - - return S_OK; -} - -STDMETHODIMP OneClickControl::Install2(BSTR extra_args) { - ASSERT1(extra_args && extra_args[0]); - - if (!site_lock_.InApprovedDomain(this)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!extra_args || !extra_args[0]) { - return E_INVALIDARG; - } - - CORE_LOG(L2, (_T("[OneClickControl::Install2][extra_args \"%s\"]"), - static_cast(CW2CT(extra_args)))); - - CString browser_url; - HRESULT hr = site_lock_.GetCurrentBrowserUrl(this, &browser_url); - if (FAILED(hr)) { - return hr; - } - - CString url_domain; - hr = SiteLock::GetUrlDomain(browser_url, &url_domain); - if (FAILED(hr)) { - return hr; - } - - // To protect against XSS attacks where an arbitrary extra_args could be - // passed into Install/Install2, BuildWebPluginCommandLine escapes all unsafe - // characters such as space, slash, double-quotes. ShellExecuteProcess is then - // passed a command line that is safe to interpret by the command line parser. - CString final_cmd_line_args; - hr = webplugin_utils::BuildWebPluginCommandLine(url_domain, - extra_args, - &final_cmd_line_args); - - CPath webpluginexe_path(app_util::GetCurrentModuleDirectory()); - VERIFY1(webpluginexe_path.Append(kOmahaWebPluginFileName)); - - scoped_process process_webpluginexe; - hr = System::ShellExecuteProcess(webpluginexe_path, - final_cmd_line_args, - NULL, - address(process_webpluginexe)); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[OneClickControl::Install2]") - _T("[ShellExecuteProcess failed][%s][%s][0x%x]"), - webpluginexe_path, final_cmd_line_args, hr)); - return hr; - } - - return S_OK; -} - -STDMETHODIMP OneClickControl::GetInstalledVersion(BSTR guid_string, - VARIANT_BOOL is_machine, - BSTR* version_string) { - if (!site_lock_.InApprovedDomain(this)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!guid_string || !version_string) { - return E_POINTER; - } - *version_string = NULL; - - CORE_LOG(L2, (_T("[OneClickControl::GetInstalledVersion][%s][%d]"), - guid_string, is_machine)); - - CString version; - HRESULT hr = DoGetInstalledVersion(guid_string, - is_machine == VARIANT_TRUE, - &version); - if (SUCCEEDED(hr)) { - *version_string = version.AllocSysString(); - } - - return S_OK; -} - -STDMETHODIMP OneClickControl::GetOneClickVersion(long* version) { // NOLINT - ASSERT1(version); - - if (!site_lock_.InApprovedDomain(this)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - CORE_LOG(L2, (_T("[OneClickControl::GetOneClickVersion]"))); - - if (!version) { - return E_POINTER; - } - - *version = atoi(ONECLICK_PLUGIN_VERSION_ANSI); // NOLINT - return S_OK; -} - -STDMETHODIMP OneClickControl::LaunchAppCommand(BSTR, VARIANT_BOOL, BSTR) { - return E_NOTIMPL; -} - -HRESULT OneClickControl::DoGetInstalledVersion(const TCHAR* guid_string, - bool is_machine, - CString* version_string) { - ASSERT1(guid_string); - ASSERT1(version_string); - - GUID app_guid = GUID_NULL; - HRESULT hr = StringToGuidSafe(guid_string, &app_guid); - if (FAILED(hr)) { - return hr; - } - - return AppManager::ReadAppVersionNoLock(is_machine, app_guid, version_string); -} - -bool OneClickControl::VariantIsValidCallback(const VARIANT* callback) { - return callback && - (callback->vt == VT_NULL || - callback->vt == VT_EMPTY || - (callback->vt == VT_DISPATCH && callback->pdispVal)); -} - -HRESULT OneClickControl::InvokeJavascriptCallback(VARIANT* callback, - const HRESULT* opt_param) { - if (!callback || callback->vt == VT_NULL || callback->vt == VT_EMPTY) { - return S_FALSE; - } - - if (callback->vt != VT_DISPATCH || !callback->pdispVal) { - return E_FAIL; - } - - const DISPID kDispId0 = 0; - DISPPARAMS dispparams = {0}; - - CComQIPtr dispatchex = callback->pdispVal; - if (dispatchex) { - DISPID disp_this = DISPID_THIS; - VARIANT var[2]; - var[0].vt = VT_DISPATCH; - var[0].pdispVal = dispatchex; - if (opt_param) { - var[1].vt = VT_I4; - var[1].intVal = *opt_param; - } - - dispparams.rgvarg = var; - dispparams.rgdispidNamedArgs = &disp_this; - dispparams.cNamedArgs = 1; - dispparams.cArgs = opt_param ? 2 : 1; - - return dispatchex->InvokeEx(kDispId0, LOCALE_USER_DEFAULT, - DISPATCH_METHOD, &dispparams, - NULL, NULL, NULL); - } else { - // Fallback on IDispatch if needed. (This route will be used for NPAPI - // functions wrapped via NPFunctionHost.) - - UINT arg_err = 0; - VARIANT var[1]; - - if (opt_param) { - var[0].vt = VT_I4; - var[0].intVal = *opt_param; - dispparams.rgvarg = var; - dispparams.cArgs = 1; - } - - return callback->pdispVal->Invoke(kDispId0, - IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - &dispparams, - NULL, - NULL, - &arg_err); - } -} - -} // namespace omaha - -// 4505: unreferenced local function has been removed -#pragma warning(disable : 4505) - diff --git a/omaha/plugins/update/activex/oneclick_control.h b/omaha/plugins/update/activex/oneclick_control.h deleted file mode 100644 index df7064507..000000000 --- a/omaha/plugins/update/activex/oneclick_control.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2008-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -// One-click support for Omaha returning users. - -#ifndef OMAHA_PLUGINS_UPDATE_ACTIVEX_ONECLICK_CONTROL_H_ -#define OMAHA_PLUGINS_UPDATE_ACTIVEX_ONECLICK_CONTROL_H_ - -// TODO(omaha): We may want to move sitelock.h to be the "standard" sitelock.h -// file from Microsoft (and move that file to omaha/external) and then have our -// modifications to sitelock be in a derived class within the plugins -// directory. - -#include -#include -#include -#include -#include -#include - -#include "omaha/base/ATLRegMapEx.h" -#include "omaha/base/const_addresses.h" -#include "omaha/base/constants.h" -#include "omaha/base/debug.h" -#include "omaha/base/logging.h" -#include "omaha/base/omaha_version.h" -#include "omaha/common/goopdate_utils.h" -#include "omaha/plugins/update/config.h" -#include "omaha/plugins/update/resource.h" -#include "omaha/plugins/update/site_lock.h" -#include "plugins/update/activex/update_control_idl.h" - -namespace omaha { - -class OneClickControl; - -typedef IObjectSafetyImpl - OneClickControlSafety; - -// Using 0xffff for the major/minor versions in the IDispatchImpl template will -// make ATL load the typelib directly from the DLL instead of looking up typelib -// registration in registry. The big benefit is that we do not need to register -// the typelib. Also, this is needed for Vista SP1 with UAC off, in which -// oleaut32 does not read typelib information from HKCU, because of a bug. -class ATL_NO_VTABLE OneClickControl - : public CComObjectRootEx, - public CComCoClass, - public IDispatchImpl, - public OneClickControlSafety, - public IObjectWithSiteImpl { - public: - OneClickControl(); - virtual ~OneClickControl(); - - DECLARE_REGISTRY_RESOURCEID_EX(IDR_ONECLICK_RGS) - - BEGIN_REGISTRY_MAP() - REGMAP_ENTRY(_T("CLSID"), GetObjectCLSID()) - REGMAP_ENTRY(_T("PROGID"), kOneclickControlProgId) - REGMAP_ENTRY(_T("HKROOT"), goopdate_utils::GetHKRoot()) - REGMAP_ENTRY(_T("SHELLNAME"), kOmahaWebPluginFileName) - REGMAP_ENTRY(_T("SHELLPATH"), GetShellPathForRegMap()) - - // The following entries are actually for the NPAPI plugin - REGMAP_ENTRY(_T("PLUGINDESCRIPTION"), kAppName) - REGMAP_ENTRY(_T("PLUGINDOMAIN"), kGoopdateServer) - REGMAP_ENTRY(_T("PLUGINVENDOR"), kFullCompanyName) - REGMAP_ENTRY(_T("PLUGINVERSION"), kOneclickPluginVersion) - REGMAP_ENTRY(_T("PLUGINPRODUCT"), kAppName) - REGMAP_ENTRY(_T("PLUGINMIMETYPE"), ONECLICK_MIME_TYPE) - END_REGISTRY_MAP() - - BEGIN_COM_MAP(OneClickControl) - COM_INTERFACE_ENTRY(IDispatch) - COM_INTERFACE_ENTRY(IObjectWithSite) - COM_INTERFACE_ENTRY(IObjectSafety) - END_COM_MAP() - - DECLARE_NOT_AGGREGATABLE(OneClickControl) - DECLARE_PROTECT_FINAL_CONSTRUCT(); - - // Installs the application that the passed-in manifest corresponds to. - STDMETHOD(Install)(BSTR cmd_line_args, - VARIANT* success_callback, - VARIANT* failure_callback); - - STDMETHOD(Install2)(BSTR extra_args); - - // Gets the version of the passed in application guid. If the application is - // not installed, returns an empty string. - STDMETHOD(GetInstalledVersion)(BSTR guid_string, - VARIANT_BOOL is_machine, - BSTR* version_string); - - // Gets the version of the plugin. The value is ONECLICK_PLUGIN_VERSION_ANSI. - // TODO(omaha3): If possible without causing incompatibilities, change version - // to a preferred type here and in OneClickWorker. - STDMETHOD(GetOneClickVersion)(long* version); // NOLINT - - // Launches a command defined by an installed application. Fails if the - // command is not successfully started, succeeds otherwise. Returns without - // waiting for the command to complete. - STDMETHOD(LaunchAppCommand)(BSTR app_guid, - VARIANT_BOOL is_machine, - BSTR cmd_id); - - private: - static bool is_machine() { - return goopdate_utils::IsRunningFromOfficialGoopdateDir(true); - } - - static CString GetShellPathForRegMap() { - return goopdate_utils::BuildInstallDirectory(is_machine(), - GetVersionString()); - } - - HRESULT DoGetInstalledVersion(const TCHAR* guid_string, - bool is_machine, - CString* version_string); - - static bool VariantIsValidCallback(const VARIANT* callback); - static HRESULT InvokeJavascriptCallback(VARIANT* callback, - const HRESULT* opt_param); - - SiteLock site_lock_; - - // If Admin, returns the path for Machine Goopdate. Else returns path for User - // Goopdate. - static CString GetGoopdateShellPathForRegMap(); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_ACTIVEX_ONECLICK_CONTROL_H_ - diff --git a/omaha/plugins/update/activex/update3web_control.cc b/omaha/plugins/update/activex/update3web_control.cc deleted file mode 100644 index 96abf4c69..000000000 --- a/omaha/plugins/update/activex/update3web_control.cc +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/activex/update3web_control.h" -#include -#include -#include "omaha/base/error.h" -#include "omaha/base/system.h" -#include "omaha/base/utils.h" -#include "omaha/base/vistautil.h" -#include "omaha/common/command_line.h" -#include "omaha/common/command_line_builder.h" -#include "omaha/common/const_cmd_line.h" -#include "omaha/common/goopdate_utils.h" -#include "omaha/common/update3_utils.h" -#include "omaha/common/webplugin_utils.h" -#include "omaha/goopdate/app_manager.h" -#include "goopdate/omaha3_idl.h" - -namespace omaha { - -Update3WebControl::Update3WebControl() { -} - -// There is a code generation bug in VC8. If a base class with template -// arguments and virtual members is not the first base class with virtual -// methods, the generated code for calling a virtual method on that base class -// will adjust the this pointer one extra time. This results in all sorts of -// strange things happening; typically, the program will crash at a later point. -// To avoid this, all calls to base class methods that have template arguments -// should have all the template arguments specified, since this seems to prevent -// the code generation bug from occuring. - -STDMETHODIMP Update3WebControl::createOmahaMachineServerAsync( - VARIANT_BOOL create_elevated, IDispatch** async_status) { - ASSERT1(async_status); - - CString url; - HRESULT hr = SiteLock::GetCurrentBrowserUrl(this, &url); - if (FAILED(hr)) { - CORE_LOG(LE, (L"[GetCurrentBrowserUrl failed][0x%08x]", hr)); - return hr; - } - - if (!site_lock_.InApprovedDomain(url)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!async_status) { - return E_POINTER; - } - - CComPtr cocreate_async; - hr = update3_utils::CoCreateWithProxyBlanket( - __uuidof(CoCreateAsyncClass), &cocreate_async); - if (FAILED(hr)) { - CORE_LOG(LE, (L"[CoCreate CoCreateAsyncClass failed][0x%08x]", hr)); - return hr; - } - - CComPtr status; - hr = cocreate_async->createOmahaMachineServerAsync(CComBSTR(url), - create_elevated, - &status); - if (FAILED(hr)) { - CORE_LOG(LE, (L"[CreateInstanceAsync failed][0x%08x]", hr)); - return hr; - } - - hr = status->QueryInterface(async_status); - CORE_LOG(L3, (L"[createOmahaMachineServerAsync][0x%p][0x%08x]", this, hr)); - return hr; -} - -STDMETHODIMP Update3WebControl::createOmahaUserServer(IDispatch** server) { - ASSERT1(server); - - CString url; - HRESULT hr = SiteLock::GetCurrentBrowserUrl(this, &url); - if (FAILED(hr)) { - CORE_LOG(LE, (L"[GetCurrentBrowserUrl failed][0x%08x]", hr)); - return hr; - } - - if (!site_lock_.InApprovedDomain(url)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!server) { - return E_POINTER; - } - - CComPtr security; - hr = update3_utils::CoCreateWithProxyBlanket( - __uuidof(GoogleUpdate3WebUserClass), &security); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[security.CoCreateWithProxyBlanket failed][0x%x]"), hr)); - return hr; - } - - hr = security->setOriginURL(CComBSTR(url)); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[setOriginURL failed][0x%08x]"), hr)); - return hr; - } - - hr = security->QueryInterface(server); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[QueryInterface failed][0x%08x]"), hr)); - return hr; - } - - CORE_LOG(L3, (L"[createOmahaUserServer][0x%p][0x%p]", this, *server)); - return hr; -} - -STDMETHODIMP Update3WebControl::getInstalledVersion(BSTR guid_string, - VARIANT_BOOL is_machine, - BSTR* version_string) { - if (!site_lock_.InApprovedDomain(this)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!guid_string || !version_string) { - return E_POINTER; - } - *version_string = NULL; - - CORE_LOG(L2, (_T("[Update3WebControl::getInstalledVersion][%s][%d]"), - guid_string, is_machine)); - - CString version; - HRESULT hr = GetVersionUsingRegistry(guid_string, - is_machine == VARIANT_TRUE, - &version); - if (SUCCEEDED(hr)) { - *version_string = version.AllocSysString(); - } - - return S_OK; -} - -#if 0 -// TODO(omaha3): Not using this method for now. CoCreation of -// GoogleUpdate3WebMachineClass can block, and should be using the async -// creation pattern aka createOmahaMachineServerAsync. -HRESULT Update3WebControl::GetVersionUsingCOMServer(const TCHAR* guid_string, - bool is_machine, - CString* version_string) { - CORE_LOG(L2, (_T("[GoopdateCtrl::GetVersionUsingCOMServer][%s][%d]"), - guid_string, is_machine)); - ASSERT1(guid_string); - ASSERT1(version_string); - - CComPtr update3web; - HRESULT hr = update3_utils::CoCreateWithProxyBlanket( - is_machine ? __uuidof(GoogleUpdate3WebMachineClass) : - __uuidof(GoogleUpdate3WebUserClass), - &update3web); - if (FAILED(hr)) { - CORE_LOG(LE, - (_T("[update3web.CoCreateWithProxyBlanket failed][0x%x]"), hr)); - return hr; - } - - CComPtr app_bundle_dispatch; - hr = update3web->createAppBundleWeb(&app_bundle_dispatch); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[update3web.createAppBundleWeb failed][0x%x]"), hr)); - return hr; - } - - CComPtr app_bundle_web; - hr = app_bundle_dispatch->QueryInterface(&app_bundle_web); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[QueryInterface for IAppBundleWeb failed][0x%x]"), hr)); - return hr; - } - - hr = app_bundle_web->initialize(); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[initialize fail][0x%x]"), hr)); - return hr; - } - - hr = app_bundle_web->createInstalledApp(CComBSTR(guid_string)); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[createInstalledApp fail][%s][0x%x]"), guid_string, hr)); - return hr; - } - - CComPtr app_dispatch; - hr = app_bundle_web->get_appWeb(0, &app_dispatch); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[get_appWeb failed][0x%x]"), hr)); - return hr; - } - - CComPtr app_web; - hr = app_dispatch->QueryInterface(&app_web); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[QueryInterface for IAppWeb failed][0x%x]"), hr)); - return hr; - } - - CComPtr app_version_dispatch; - hr = app_web->get_currentVersionWeb(&app_version_dispatch); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[get_currentVersionWeb failed][0x%x]"), hr)); - return hr; - } - - CComPtr app_version_web; - hr = app_version_dispatch->QueryInterface(&app_version_web); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[QueryInterface for IAppVersionWeb failed][0x%x]"), hr)); - return hr; - } - - CComBSTR version; - hr = app_version_web->get_version(&version); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[get_version failed][0x%x]"), hr)); - return hr; - } - - *version_string = version; - CORE_LOG(L2, (_T("[Update3WebControl::GetVersionUsingCOMServer][%s][%d][%s]"), - guid_string, is_machine, *version_string)); - return S_OK; -} -#endif - -HRESULT Update3WebControl::GetVersionUsingRegistry(const TCHAR* guid_string, - bool is_machine, - CString* version_string) { - CORE_LOG(L2, (_T("[GoopdateCtrl::GetVersionUsingRegistry][%s][%d]"), - guid_string, is_machine)); - ASSERT1(guid_string); - ASSERT1(version_string); - - GUID app_guid = GUID_NULL; - HRESULT hr = StringToGuidSafe(guid_string, &app_guid); - if (FAILED(hr)) { - return hr; - } - - return AppManager::ReadAppVersionNoLock(is_machine, app_guid, version_string); -} - -HRESULT Update3WebControl::crossInstall(BSTR extra_args) { - ASSERT1(extra_args); - - if (!site_lock_.InApprovedDomain(this)) { - return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED; - } - - if (!extra_args || !extra_args[0]) { - return E_INVALIDARG; - } - - CORE_LOG(L2, (_T("[Update3WebControl::crossInstall][%s]"), extra_args)); - - // Build the full command line as it should eventually be run. (This command - // line will be escaped, including the /install, and stowed in a /pi later.) - - CommandLineBuilder inner_builder(COMMANDLINE_MODE_INSTALL); - inner_builder.set_extra_args(CString(extra_args)); - CString inner_cmd_line_args = inner_builder.GetCommandLineArgs(); - - HRESULT hr = webplugin_utils::IsLanguageSupported(inner_cmd_line_args); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[IsLanguageSupported failed][0x%08x]"), hr)); - return hr; - } - - CString browser_url; - hr = site_lock_.GetCurrentBrowserUrl(this, &browser_url); - if (FAILED(hr)) { - return hr; - } - - CString url_domain; - hr = SiteLock::GetUrlDomain(browser_url, &url_domain); - if (FAILED(hr)) { - return hr; - } - - // Build the outer command line using /pi. - - CString url_domain_encoded; - CString cmd_line_encoded; - hr = StringEscape(url_domain, true, &url_domain_encoded); - if (FAILED(hr)) { - return hr; - } - - hr = StringEscape(inner_cmd_line_args, true, &cmd_line_encoded); - if (FAILED(hr)) { - return hr; - } - - CommandLineBuilder outer_builder(COMMANDLINE_MODE_WEBPLUGIN); - outer_builder.set_webplugin_url_domain(url_domain_encoded); - outer_builder.set_webplugin_args(cmd_line_encoded); - outer_builder.set_install_source(kCmdLineInstallSource_Update3Web); - CString final_cmd_line_args = outer_builder.GetCommandLineArgs(); - - CORE_LOG(L2, (_T("[Update3WebControl::crossInstall]") - _T("[Final command line params: %s]"), - final_cmd_line_args)); - - // Spawn a gu process. - - scoped_process process_goopdate; - - hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine(), - final_cmd_line_args, - address(process_goopdate)); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[Update3WebControl::crossInstall]") - _T("[Failed StartGoogleUpdateWithArgs][0x%x]"), hr)); - return hr; - } - - return S_OK; -} - -HRESULT Update3WebControl::launchAppCommand(BSTR, VARIANT_BOOL, BSTR) { - return E_NOTIMPL; -} - -Update3WebControl::~Update3WebControl() { -} - -} // namespace omaha diff --git a/omaha/plugins/update/activex/update3web_control.h b/omaha/plugins/update/activex/update3web_control.h deleted file mode 100644 index 0300d3375..000000000 --- a/omaha/plugins/update/activex/update3web_control.h +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_ACTIVEX_UPDATE3WEB_CONTROL_H_ -#define OMAHA_PLUGINS_UPDATE_ACTIVEX_UPDATE3WEB_CONTROL_H_ - -#include -#include -#include - -#include "base/basictypes.h" -#include "goopdate/omaha3_idl.h" -#include "omaha/base/atlregmapex.h" -#include "omaha/base/const_addresses.h" -#include "omaha/base/constants.h" -#include "omaha/base/omaha_version.h" -#include "common/goopdate_utils.h" -#include "omaha/plugins/update/config.h" -#include "omaha/plugins/update/resource.h" -#include "omaha/plugins/update/site_lock.h" -#include "plugins/update/activex/update_control_idl.h" - -namespace omaha { - -class Update3WebControl; - -typedef IObjectSafetyImpl - Update3WebControlSafety; - -class ATL_NO_VTABLE Update3WebControl - : public CComObjectRootEx, - public CComCoClass, - public IDispatchImpl, - public Update3WebControlSafety, - public IObjectWithSiteImpl { - public: - Update3WebControl(); - - DECLARE_NOT_AGGREGATABLE(Update3WebControl) - DECLARE_REGISTRY_RESOURCEID_EX(IDR_ONECLICK_RGS) - -#pragma warning(push) -// Construction of local static object is not thread-safe -#pragma warning(disable:4640) - BEGIN_REGISTRY_MAP() - REGMAP_ENTRY(L"CLSID", GetObjectCLSID()) - REGMAP_ENTRY(L"PROGID", kUpdate3WebControlProgId) - REGMAP_ENTRY(L"HKROOT", goopdate_utils::GetHKRoot()) - REGMAP_ENTRY(L"SHELLNAME", is_machine() ? kOmahaBrokerFileName : - kOmahaOnDemandFileName) - REGMAP_ENTRY(L"SHELLPATH", GetShellPathForRegMap()) - // The following entries are actually for the NPAPI plugin - REGMAP_ENTRY(L"PLUGINDESCRIPTION", kAppName) - REGMAP_ENTRY(L"PLUGINDOMAIN", kGoopdateServer) - REGMAP_ENTRY(L"PLUGINVENDOR", kFullCompanyName) - REGMAP_ENTRY(L"PLUGINVERSION", kUpdate3WebPluginVersion) - REGMAP_ENTRY(L"PLUGINPRODUCT", kAppName) - REGMAP_ENTRY(L"PLUGINMIMETYPE", UPDATE3WEB_MIME_TYPE) - END_REGISTRY_MAP() -#pragma warning(pop) - - BEGIN_COM_MAP(Update3WebControl) - COM_INTERFACE_ENTRY(IDispatch) - COM_INTERFACE_ENTRY(IObjectSafety) - COM_INTERFACE_ENTRY(IObjectWithSite) - END_COM_MAP() - - // IGoogleUpdate3WebControl methods. - STDMETHOD(createOmahaMachineServerAsync)(VARIANT_BOOL create_elevated, - IDispatch** async_status); - STDMETHOD(createOmahaUserServer)(IDispatch** server); - - // Gets the version of the passed in application guid. If the application is - // not installed, returns an empty string. - STDMETHOD(getInstalledVersion)(BSTR guid_string, - VARIANT_BOOL is_machine, - BSTR* version_string); - - // OneClick-equivalent API, used for cross-installs. - STDMETHOD(crossInstall)(BSTR extra_args); - - // Launches a command defined by an installed application. Fails if the - // command is not successfully started, succeeds otherwise. Returns without - // waiting for the command to complete. - STDMETHOD(launchAppCommand)(BSTR guid_string, - VARIANT_BOOL is_machine, - BSTR cmd_id); - - protected: - virtual ~Update3WebControl(); - - private: - static bool is_machine() { - return goopdate_utils::IsRunningFromOfficialGoopdateDir(true); - } - - static CString GetShellPathForRegMap() { - return goopdate_utils::BuildInstallDirectory(is_machine(), - GetVersionString()); - } - - HRESULT GetVersionUsingCOMServer(const TCHAR* guid_string, - bool is_machine, - CString* version_string); - HRESULT GetVersionUsingRegistry(const TCHAR* guid_string, - bool is_machine, - CString* version_string); - - SiteLock site_lock_; - - friend class Update3WebControlTest; - - DISALLOW_COPY_AND_ASSIGN(Update3WebControl); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_ACTIVEX_UPDATE3WEB_CONTROL_H_ diff --git a/omaha/plugins/update/activex/update3web_control_unittest.cc b/omaha/plugins/update/activex/update3web_control_unittest.cc deleted file mode 100644 index b81d4c9a5..000000000 --- a/omaha/plugins/update/activex/update3web_control_unittest.cc +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/activex/update3web_control.h" -#include -#include -#include -#include "base/basictypes.h" -#include "base/error.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -namespace { - -#define STUBMETHOD(identifier, ...) \ - STDMETHOD(identifier)(__VA_ARGS__) {\ - return E_NOTIMPL;\ - } - -#define STUBGETPROP(identifier, type) \ - STUBMETHOD(get_ ## identifier, type*) - -#define STUBPUTPROP(identifier, type) \ - STUBMETHOD(put_ ## identifier, type) - -#define STUBPROP(identifier, type) \ - STUBGETPROP(identifier, type)\ - STUBPUTPROP(identifier, type) - -class ATL_NO_VTABLE MockWebBrowser2 - : public CComObjectRootEx, - public IServiceProviderImpl, - public IWebBrowser2 { - public: - MockWebBrowser2() : url_("something-like-a-url") {} - virtual ~MockWebBrowser2() {} - - DECLARE_NOT_AGGREGATABLE(MockWebBrowser2); - - BEGIN_COM_MAP(MockWebBrowser2) - COM_INTERFACE_ENTRY(IServiceProvider) - COM_INTERFACE_ENTRY(IWebBrowser2) - END_COM_MAP() - - BEGIN_SERVICE_MAP(MockWebBrowser2) - SERVICE_ENTRY(SID_SWebBrowserApp) - END_SERVICE_MAP() - - // IDispatch - STUBMETHOD(GetIDsOfNames, REFIID, OLECHAR**, unsigned int, LCID, DISPID*); - STUBMETHOD(GetTypeInfo, unsigned int, LCID, ITypeInfo**); - STUBMETHOD(GetTypeInfoCount, UINT*); - STUBMETHOD(Invoke, DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, - EXCEPINFO*, unsigned int*); - - // IWebBrowser2 - STUBPROP(AddressBar, VARIANT_BOOL); - STUBGETPROP(Application, IDispatch*); - STUBGETPROP(Busy, VARIANT_BOOL); - STUBMETHOD(ClientToWindow, int*, int*); - STUBGETPROP(Container, IDispatch*); - STUBGETPROP(Document, IDispatch*); - STUBMETHOD(ExecWB, OLECMDID, OLECMDEXECOPT, VARIANT*, VARIANT*); - STUBGETPROP(FullName, BSTR); - STUBPROP(FullScreen, VARIANT_BOOL); - STUBMETHOD(GetProperty, BSTR, VARIANT*); - STUBMETHOD(GoBack, VOID); - STUBMETHOD(GoForward, VOID); - STUBMETHOD(GoHome, VOID); - STUBMETHOD(GoSearch, VOID); - STUBPROP(Height, LONG); - STUBGETPROP(HWND, LONG_PTR); - STUBPROP(Left, LONG); - STUBGETPROP(LocationName, BSTR); - STDMETHOD(get_LocationURL)(BSTR* url) { - *url = CComBSTR(url_).Detach(); - return S_OK; - } - STUBPROP(MenuBar, VARIANT_BOOL); - STUBGETPROP(Name, BSTR); - STUBMETHOD(Navigate, BSTR, VARIANT*, VARIANT*, VARIANT*, VARIANT*); - STUBMETHOD(Navigate2, VARIANT*, VARIANT*, VARIANT*, VARIANT*, VARIANT*); - STUBPROP(Offline, VARIANT_BOOL); - STUBGETPROP(Path, BSTR); - STUBGETPROP(Parent, IDispatch*); - STUBMETHOD(PutProperty, BSTR, VARIANT); - STUBMETHOD(QueryStatusWB, OLECMDID, OLECMDF*); - STUBMETHOD(Quit, VOID); - STUBGETPROP(ReadyState, READYSTATE); - STUBMETHOD(Refresh, VOID); - STUBMETHOD(Refresh2, VARIANT*); - STUBPROP(RegisterAsBrowser, VARIANT_BOOL); - STUBPROP(RegisterAsDropTarget, VARIANT_BOOL); - STUBPROP(Resizable, VARIANT_BOOL); - STUBMETHOD(ShowBrowserBar, VARIANT*, VARIANT*, VARIANT*); - STUBPROP(Silent, VARIANT_BOOL); - STUBPROP(StatusBar, VARIANT_BOOL); - STUBPROP(StatusText, BSTR); - STUBMETHOD(Stop, VOID); - STUBPROP(TheaterMode, VARIANT_BOOL); - STUBPROP(ToolBar, int); - STUBPROP(Top, LONG); - STUBGETPROP(TopLevelContainer, VARIANT_BOOL); - STUBPROP(Type, BSTR); - STUBPROP(Visible, VARIANT_BOOL); - STUBPROP(Width, LONG); - - void set_url(const char* url) { url_ = url; } - - private: - CString url_; - - DISALLOW_COPY_AND_ASSIGN(MockWebBrowser2); -}; - -class ATL_NO_VTABLE MockHTMLDocument2 - : public CComObjectRootEx, - public IServiceProvider, - public IOleClientSite, - public IOleContainer, - public IHTMLDocument2 { - public: - MockHTMLDocument2() {} - virtual ~MockHTMLDocument2() {} - - DECLARE_NOT_AGGREGATABLE(MockHTMLDocument2); - - BEGIN_COM_MAP(MockHTMLDocument2) - COM_INTERFACE_ENTRY(IServiceProvider) - COM_INTERFACE_ENTRY(IOleClientSite) - COM_INTERFACE_ENTRY(IOleContainer) - COM_INTERFACE_ENTRY(IHTMLDocument2) - END_COM_MAP() - - // IServiceProvider - STUBMETHOD(QueryService, REFGUID, REFIID, void**); - - // IOleContainer - STDMETHOD(GetContainer)(IOleContainer** container) { - return QueryInterface(IID_PPV_ARGS(container)); - } - STUBMETHOD(GetMoniker, DWORD, DWORD, IMoniker**); - STUBMETHOD(OnShowWindow, BOOL); - STUBMETHOD(RequestNewObjectLayout); - STUBMETHOD(SaveObject); - STUBMETHOD(ShowObject); - - // IOleContainer - STUBMETHOD(EnumObjects, DWORD, IEnumUnknown**); - STUBMETHOD(LockContainer, BOOL); - - // IDispatch - STUBMETHOD(GetIDsOfNames, REFIID, OLECHAR**, unsigned int, LCID, DISPID*); - STUBMETHOD(GetTypeInfo, unsigned int, LCID, ITypeInfo**); - STUBMETHOD(GetTypeInfoCount, UINT*); - STUBMETHOD(Invoke, DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, - EXCEPINFO*, unsigned int*); - - // IParseDisplayName - STUBMETHOD(ParseDisplayName, IBindCtx*, LPOLESTR, ULONG*, IMoniker**); - - // IHTMLDocument2 - STUBGETPROP(activeElement, IHTMLElement*); - STUBPROP(alinkColor, VARIANT); - STUBGETPROP(all, IHTMLElementCollection*); - STUBGETPROP(anchors, IHTMLElementCollection*); - STUBGETPROP(applets, IHTMLElementCollection*); - STUBPROP(bgColor, VARIANT); - STUBGETPROP(body, IHTMLElement*); - STUBPROP(charset, BSTR); - STUBMETHOD(clear, VOID); - STUBMETHOD(close, VOID); - STUBPROP(cookie, BSTR); - STUBMETHOD(createElement, BSTR, IHTMLElement**); - STUBMETHOD(createStyleSheet, BSTR, LONG, IHTMLStyleSheet**); - STUBPROP(defaultCharset, BSTR); - STUBPROP(designMode, BSTR); - STUBPROP(domain, BSTR); - STUBPROP(elementFromPoint, BSTR); - STUBMETHOD(elementFromPoint, LONG, LONG, IHTMLElement**); - STUBGETPROP(embeds, IHTMLElementCollection*); - STUBMETHOD(execCommand, BSTR, VARIANT_BOOL, VARIANT, VARIANT_BOOL*); - STUBMETHOD(execCommandShowHelp, BSTR, VARIANT_BOOL*); - STUBPROP(expando, VARIANT_BOOL); - STUBPROP(fgColor, VARIANT); - STUBGETPROP(fileCreatedDate, BSTR); - STUBGETPROP(fileModifiedDate, BSTR); - STUBGETPROP(fileSize, BSTR); - STUBGETPROP(fileUpdatedDate, BSTR); - STUBGETPROP(forms, IHTMLElementCollection*); - STUBGETPROP(frames, IHTMLFramesCollection2*); - STUBGETPROP(images, IHTMLElementCollection*); - STUBGETPROP(lastModified, BSTR); - STUBPROP(linkColor, VARIANT); - STUBGETPROP(links, IHTMLElementCollection*); - STUBGETPROP(location, IHTMLLocation*); - STUBGETPROP(mimeType, BSTR); - STUBGETPROP(nameProp, BSTR); - STUBPROP(onafterupdate, VARIANT); - STUBPROP(onbeforeupdate, VARIANT); - STUBPROP(onclick, VARIANT); - STUBPROP(ondblclick, VARIANT); - STUBPROP(ondragstart, VARIANT); - STUBPROP(onerrorupdate, VARIANT); - STUBPROP(onhelp, VARIANT); - STUBPROP(onkeydown, VARIANT); - STUBPROP(onkeypress, VARIANT); - STUBPROP(onkeyup, VARIANT); - STUBPROP(onmousedown, VARIANT); - STUBPROP(onmousemove, VARIANT); - STUBPROP(onmouseout, VARIANT); - STUBPROP(onmouseover, VARIANT); - STUBPROP(onmouseup, VARIANT); - STUBPROP(onreadystatechange, VARIANT); - STUBPROP(onrowenter, VARIANT); - STUBPROP(onrowexit, VARIANT); - STUBPROP(onselectstart, VARIANT); - STUBMETHOD(open, BSTR, VARIANT, VARIANT, VARIANT, IDispatch**); - STUBGETPROP(parentWindow, IHTMLWindow2*); - STUBGETPROP(plugins, IHTMLElementCollection*); - STUBPROP(protocol, BSTR); - STUBMETHOD(queryCommandEnabled, BSTR, VARIANT_BOOL*); - STUBMETHOD(queryCommandIndeterm, BSTR, VARIANT_BOOL*); - STUBMETHOD(queryCommandState, BSTR, VARIANT_BOOL*); - STUBMETHOD(queryCommandSupported, BSTR, VARIANT_BOOL*); - STUBMETHOD(queryCommandText, BSTR, BSTR*); - STUBMETHOD(queryCommandValue, BSTR, VARIANT*); - STUBGETPROP(readyState, BSTR); - STUBGETPROP(referrer, BSTR); - STUBGETPROP(Script, IDispatch*); - STUBGETPROP(security, BSTR); - STUBGETPROP(selection, IHTMLSelectionObject*); - STUBGETPROP(scripts, IHTMLElementCollection*); - STUBGETPROP(styleSheets, IHTMLStyleSheetsCollection*); - STUBPROP(title, BSTR); - STUBMETHOD(toString, BSTR*); - STDMETHOD(get_URL)(BSTR* url) { - *url = CComBSTR("something-else-like-a-url").Detach(); - return S_OK; - } - STUBPUTPROP(URL, BSTR); - STUBPROP(vlinkColor, VARIANT); - STUBMETHOD(write, SAFEARRAY*); - STUBMETHOD(writeln, SAFEARRAY*); - - private: - DISALLOW_COPY_AND_ASSIGN(MockHTMLDocument2); -}; - -#undef STUBMETHOD -#undef STUBGETPROP -#undef STUBPUTPROP -#undef STUBPROP - -template -HRESULT CComObjectCreatorHelper(CComObject** ptr) { - if (!ptr) { - return E_POINTER; - } - CComObject* raw_ptr = NULL; - RET_IF_FAILED(CComObject::CreateInstance(&raw_ptr)); - raw_ptr->AddRef(); - *ptr = raw_ptr; - return S_OK; -} - - -} // namespace - -class Update3WebControlTest : public testing::Test { - protected: - virtual void SetUp() { - ASSERT_SUCCEEDED(CComObjectCreatorHelper(&control_)); - } - - virtual void TearDown() { - } - - HRESULT GetCurrentBrowserUrl(CString* url) { - return control_->site_lock_.GetCurrentBrowserUrl(control_, url); - } - - CComPtr > control_; -}; - -TEST_F(Update3WebControlTest, SiteLock) { - CComPtr > browser; - ASSERT_SUCCEEDED(CComObjectCreatorHelper(&browser)); - CComPtr unknown; - ASSERT_SUCCEEDED(browser.QueryInterface(&unknown)); - ASSERT_SUCCEEDED(control_->SetSite(unknown)); - browser->set_url("http://www.google.com/pack/page.html"); - EXPECT_EQ(E_POINTER, - control_->getInstalledVersion(CComBSTR(), VARIANT_FALSE, NULL)); -} - -TEST_F(Update3WebControlTest, SiteLock_Negative) { - CComPtr browser; - ASSERT_SUCCEEDED(CComCoClass::CreateInstance(&browser)); - ASSERT_SUCCEEDED(control_->SetSite(browser)); - EXPECT_EQ(GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED, - control_->getInstalledVersion(CComBSTR(), VARIANT_FALSE, NULL)); -} - -TEST_F(Update3WebControlTest, GetCurrentBrowserUrl_FailIfNoSite) { - CString url; - EXPECT_FALSE(SUCCEEDED(GetCurrentBrowserUrl(&url))); -} - -TEST_F(Update3WebControlTest, GetCurrentBrowserUrl_WithIWebBrowser2) { - CComPtr browser; - ASSERT_SUCCEEDED(CComCoClass::CreateInstance(&browser)); - ASSERT_SUCCEEDED(control_->SetSite(browser)); - CString url; - EXPECT_EQ(S_OK, GetCurrentBrowserUrl(&url)); - EXPECT_STREQ(L"something-like-a-url", url); -} - -TEST_F(Update3WebControlTest, GetCurrentBrowserUrl_WithIHTMLDocument2) { - CComPtr document; - ASSERT_SUCCEEDED(CComCoClass::CreateInstance(&document)); - ASSERT_SUCCEEDED(control_->SetSite(document)); - CString url; - EXPECT_EQ(S_OK, GetCurrentBrowserUrl(&url)); - EXPECT_STREQ(L"something-else-like-a-url", url); -} - -} // namespace omaha diff --git a/omaha/plugins/update/build.scons b/omaha/plugins/update/build.scons deleted file mode 100644 index 05acb1639..000000000 --- a/omaha/plugins/update/build.scons +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - - -Import('env') - -# Need to do all these modifications to the non-default env because calling -# EnablePrecompile on the main environment, which customization_test_env clones. -# causes the opt build to fail. -# TODO(omaha): How much of this needs to be used by all libraries that get built -# and how much can be moved to BuildNpGoogleUpdateDll? -update_env = env.Clone() -update_env.Append( - CPPDEFINES = [ - # TODO(omaha): ActiveX plugins are usually apartment-threaded. There's - # no problem with compiling at a higher threading level than required, - # but there is a slight performance penalty for AddRef()/Release(). At - # some point, it may be desirable to switch back to apartment threaded, - # once the unit tests are fixed. - # We also take a 1KiB penalty for using _ATL_FREE_THREADED =) - '_ATL_FREE_THREADED', - ], - CPPFLAGS = [ - '/wd4263', # member function does not override any base class virtual - # member function - '/wd4264', # no override available for virtual member function; - # function is hidden - '/wd4265', # class has virtual functions, but destructor is not virtual - '/wd4310', # cast truncates constant value - '/wd4350', # behavior change: 'member1' called instead of 'member2' - '/wd4548', # expression before comma has no effect; expected expression - # with side-effect - '/wd4986', # exception spec does not match previous declaration - '/wd5038', # data member will be initialized after data member - ], - CPPPATH = [ - '$OBJ_ROOT', # Needed for generated files. - ], - LIBS = [ - '$LIB_DIR/base.lib', - '$LIB_DIR/breakpad.lib', - '$LIB_DIR/client.lib', - '$LIB_DIR/core.lib', - '$LIB_DIR/crx_file.lib', - '$LIB_DIR/google_update_recovery.lib', - '$LIB_DIR/goopdate_lib.lib', - '$LIB_DIR/libprotobuf.lib', - '$LIB_DIR/logging.lib', - '$LIB_DIR/net.lib', - '$LIB_DIR/omaha3_idl.lib', - '$LIB_DIR/security.lib', - '$LIB_DIR/service.lib', - '$LIB_DIR/setup.lib', - '$LIB_DIR/statsreport.lib', - '$LIB_DIR/ui.lib', - env['atls_libs'][env.Bit('debug')], - env['crt_libs'][env.Bit('debug')], - 'bits.lib', - 'comctl32.lib', - 'crypt32.lib', - 'imagehlp.lib', - 'iphlpapi.lib', - 'msi.lib', - 'msimg32.lib', - 'mstask.lib', - 'psapi.lib', - 'netapi32.lib', - 'shlwapi.lib', - 'taskschd.lib', - 'userenv.lib', - 'uxtheme.lib', - 'version.lib', - 'wintrust.lib', - 'wtsapi32.lib', - ], - LINKFLAGS = [ - '/DYNAMICBASE', # Enable ASLR. - '/NXCOMPAT', # Enable NX support. - ], -) -if update_env.Bit('has_device_management'): - update_env.Append( - LIBS = [ - '$LIB_DIR/dm_proto.lib', - ], - ) - -# COM stuff. -midl_env = env.Clone() -midl_env.Tool('midl') -# Generate optimized, stubless proxy/stub code. -midl_env['MIDLFLAGS'] += [ '/Oicf', ] - -# For some reason, calling TypeLibrary() twice on the same environment causes a -# re-link of the test executable when alternating between "hammer" and -# "hammer run_all_tests". -# To work around this, clone the original environment and call TypeLibrary once -# on each. Note that: -# 1) Cloning midl_env BEFORE TypeLibrary() is called does not solve this -# problem. The environment must be cloned from the original env. -# 2) Moving the following code down near npapi_testing_midl_env.TypeLibrary -# does not solve the problem either. -# 3) This problem does not occur if 'update_npapi_testing.lib' is passed as -# a source to other builders instead of as a LIB. -npapi_testing_midl_env = env.Clone() -npapi_testing_midl_env.Tool('midl') -# Generate optimized, stubless proxy/stub code. -npapi_testing_midl_env['MIDLFLAGS'] += [ '/Oicf', ] - -midl_env.TypeLibrary('activex/update_control_idl.idl') -update_control_idl_guids_lib = env.ComponentLibrary( - lib_name='update_control_idl_guids_lib', - source='$OBJ_ROOT/plugins/update/activex/update_control_idl_i.c', -) - -# Built as library to enable unit testing. -inputs = [ - 'activex/update3web_control.cc', - 'activex/oneclick_control.cc', - 'npapi/dispatch_host.cc', - 'npapi/npfunction_host.cc', - 'npapi/urlpropbag.cc', - 'npapi/np_update.cc', - 'npapi/variant_utils.cc', - 'config.cc', - 'site_lock.cc', - ] -update_lib = update_env.ComponentLibrary( - lib_name='update', - source=inputs, -) - - -def BuildNpGoogleUpdateDll(omaha_version_info): - plugin_env = update_env.Clone(COMPONENT_STATIC = False) - - prefix = omaha_version_info.filename_prefix - if prefix: - plugin_env['OBJPREFIX'] = plugin_env.subst(prefix + 'obj/$OBJPREFIX') - - version_string = omaha_version_info.GetVersionString() - plugin_env.Append( - LIBS = [ - '$LIB_DIR/common.lib', - '$LIB_DIR/plugin_base.lib', - 'wininet.lib', - update_control_idl_guids_lib, - update_lib, - ], - RCFLAGS = [ - '/DVERSION_MAJOR=%d' % omaha_version_info.version_major, - '/DVERSION_MINOR=%d' % omaha_version_info.version_minor, - '/DVERSION_BUILD=%d' % omaha_version_info.version_build, - '/DVERSION_PATCH=%d' % omaha_version_info.version_patch, - '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string, - ], - ) - - - resource = plugin_env.RES(target='%sresource.res' % prefix, - source='resource.rc') - plugin_env.Depends( - resource, - ['$MAIN_DIR/VERSION', - '$OBJ_ROOT/plugins/update/activex/update_control_idl.tlb', - 'oneclick.rgs']) - - target_name = '%s%s' % ( - prefix, - omaha_version_info.plugin_signed_file_info.unsigned_filename_base) - - inputs = [ - 'module.def', - 'module.cc', - resource, - ] - - unsigned_dll = plugin_env.ComponentLibrary( - lib_name=target_name, - source=inputs, - ) - - signed_dll = plugin_env.DualSignedBinary( - target='%s%s' % (prefix, - omaha_version_info.plugin_signed_file_info.filename), - source=unsigned_dll, - ) - - env.Replicate('$STAGING_DIR', signed_dll) - env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb']) - - -for omaha_version_info in env['omaha_versions_info']: - BuildNpGoogleUpdateDll(omaha_version_info) - - -# -# Tests -# - -# NPAPI unit test helper library -npapi_testing_midl_env.TypeLibrary('npapi/testing/dispatch_host_test_idl.idl') -update_env.ComponentLibrary( - lib_name = 'update_test_helpers', - source = [ - 'npapi/testing/dispatch_host_test_interface.cc', - 'npapi/testing/stubs.cc', - ] -) - -# NPAPI unit test resource -npapi_test_lib_resources = update_env.RES( - target='npapi/testing/dispatch_host_test.res', - source='npapi/testing/dispatch_host_test.rc'), -# TODO(omaha): we should really just make a proper .rc file scanner. -update_env.Depends( - npapi_test_lib_resources, - '$OBJ_ROOT/plugins/update/npapi/testing/dispatch_host_test_idl.tlb') diff --git a/omaha/plugins/update/config.cc b/omaha/plugins/update/config.cc deleted file mode 100644 index 6a4a2af8c..000000000 --- a/omaha/plugins/update/config.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/config.h" -#include -#include "omaha/base/constants.h" - -namespace omaha { - -const TCHAR kUpdate3WebPluginVersion[] = _T(UPDATE_PLUGIN_VERSION_ANSI); -const TCHAR kUpdate3WebControlProgId[] = COMPANY_NAME_IDENTIFIER _T(".") \ - _T("Update3WebControl.") \ - _T(UPDATE_PLUGIN_VERSION_ANSI); - -const TCHAR kOneclickPluginVersion[] = _T(ONECLICK_PLUGIN_VERSION_ANSI); -const TCHAR kOneclickControlProgId[] = COMPANY_NAME_IDENTIFIER _T(".") \ - _T("OneClickCtrl.") \ - _T(ONECLICK_PLUGIN_VERSION_ANSI); - -} // namespace omaha diff --git a/omaha/plugins/update/config.h b/omaha/plugins/update/config.h deleted file mode 100644 index b76198446..000000000 --- a/omaha/plugins/update/config.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_CONFIG_H_ -#define OMAHA_PLUGINS_UPDATE_CONFIG_H_ - -#include - -namespace omaha { - -// TODO(omaha3): The OneClick MIME type is currently defined one other place in -// the codebase, in base\const_config.h. I'm putting our own definition here; -// we should probably delete the copy in base, and potentially moving this file -// to base as plugin_constants.h. -// -// Note that COMPANY_DOMAIN_BASE_ANSI and *_PLUGIN_VERSION_ANSI are defined -// in omaha\main.scons. - -#define UPDATE3WEB_MIME_TYPE "application/x-vnd." COMPANY_DOMAIN_BASE_ANSI \ - ".update3webcontrol." UPDATE_PLUGIN_VERSION_ANSI - -#define ONECLICK_MIME_TYPE "application/x-vnd." COMPANY_DOMAIN_BASE_ANSI \ - ".oneclickctrl." ONECLICK_PLUGIN_VERSION_ANSI - -#define MERGED_MIME_TYPE UPDATE3WEB_MIME_TYPE "|" ONECLICK_MIME_TYPE - -extern const TCHAR kUpdate3WebPluginVersion[]; -extern const TCHAR kUpdate3WebControlProgId[]; - -extern const TCHAR kOneclickPluginVersion[]; -extern const TCHAR kOneclickControlProgId[]; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_CONFIG_H_ diff --git a/omaha/plugins/update/generate_plugin_idls.py b/omaha/plugins/update/generate_plugin_idls.py deleted file mode 100644 index 069a7aa18..000000000 --- a/omaha/plugins/update/generate_plugin_idls.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2007-2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -""" -Generates IDL file for the OneClick ActiveX control from the passed-in IDL -template. The input template is a complete IDL file in all but one respect; -It has one replaceable entry for the CLSID for GoopdateOneClickControl. -We generate a GUID using UUIDGEN.EXE, and write out an IDL with a new CLSID. - -""" - -import sys -import os -import getopt -import commands - - -def _GetStatusOutput(cmd): - """Return (status, output) of executing cmd in a shell.""" - if os.name == "nt": - pipe = os.popen(cmd + " 2>&1", 'r') - text = pipe.read() - sts = pipe.close() - if sts is None: sts = 0 - if text[-1:] == '\n': text = text[:-1] - return sts, text - else: - return commands.getstatusoutput(cmd) - - -def _GenerateIDLText(idl_template): - (status, guid) = _GetStatusOutput("uuidgen.exe") - if status != 0: - raise SystemExit("Failed to get GUID: %s" % guid) - - return idl_template % guid - - -def _GenerateIDLFile(idl_template_filename, idl_output_filename): - f_in = open(idl_template_filename, 'r') - idl_template = f_in.read() - f_in.close() - - idl_output = _GenerateIDLText(idl_template) - - f_out = open(idl_output_filename, 'w') - f_out.write(""" - // ** AUTOGENERATED FILE. DO NOT HAND-EDIT ** - """) - f_out.write(idl_output) - f_out.close() - - -def _Usage(): - """Prints out script usage information.""" - print """ -generate_oneclick_idl.py: Write out the given IDL file. - -Usage: - generate_oneclick_idl.py [--help - | --idl_template_file filename - --idl_output_file filename] - -Options: - --help Show this information. - --idl_output_file filename Path/name of output IDL filename. - --idl_template_file filename Path/name of input IDL template. -""" - - -def _Main(): - """Generates IDL file.""" - # use getopt to parse the option and argument list; this may raise, but - # don't catch it - _ARGUMENT_LIST = ["help", "idl_template_file=", "idl_output_file="] - (opts, args) = getopt.getopt(sys.argv[1:], "", _ARGUMENT_LIST) - if not opts or ("--help", "") in opts: - _Usage() - sys.exit() - - idl_template_filename = "" - idl_output_filename = "" - - for (o, v) in opts: - if o == "--idl_template_file": - idl_template_filename = v - if o == "--idl_output_file": - idl_output_filename = v - - # make sure we have work to do - if not idl_template_filename: - raise SystemExit("no idl_template_filename specified") - if not idl_output_filename: - raise SystemExit("no idl_output_filename specified") - - _GenerateIDLFile(idl_template_filename, idl_output_filename) - sys.exit() - - -if __name__ == "__main__": - _Main() - diff --git a/omaha/plugins/update/module.cc b/omaha/plugins/update/module.cc deleted file mode 100644 index f52e0a6fe..000000000 --- a/omaha/plugins/update/module.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include -#include "base/basictypes.h" -#include "omaha/base/omaha_version.h" -#include "goopdate/omaha3_idl.h" -#include "omaha/plugins/update/activex/update3web_control.h" -#include "omaha/plugins/update/activex/oneclick_control.h" -#include "plugins/update/activex/update_control_idl.h" - -namespace omaha { - -OBJECT_ENTRY_AUTO(__uuidof(GoogleUpdateOneClickControlCoClass), OneClickControl) -OBJECT_ENTRY_AUTO(__uuidof(GoogleUpdate3WebControlCoClass), Update3WebControl) - -namespace { - -class GoogleUpdateControlModule - : public CAtlDllModuleT { - public: - GoogleUpdateControlModule() {} - - DECLARE_LIBID(LIBID_GoogleUpdateControlLib); - - private: - DISALLOW_COPY_AND_ASSIGN(GoogleUpdateControlModule); // NOLINT -} _AtlModule; - -} // namespace - -} // namespace omaha - -BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) { - switch (reason) { - case DLL_PROCESS_ATTACH: - omaha::InitializeVersionFromModule(instance); - break; - default: - break; - } - - return omaha::_AtlModule.DllMain(reason, reserved); -} - -STDAPI DllCanUnloadNow() { - return omaha::_AtlModule.DllCanUnloadNow(); -} - -STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void** ppv) { - return omaha::_AtlModule.DllGetClassObject(clsid, iid, ppv); -} - -STDAPI DllRegisterServer() { - return omaha::_AtlModule.DllRegisterServer(false); -} - -STDAPI DllUnregisterServer() { - return omaha::_AtlModule.DllUnregisterServer(false); -} diff --git a/omaha/plugins/update/module.def b/omaha/plugins/update/module.def deleted file mode 100644 index e98111ec9..000000000 --- a/omaha/plugins/update/module.def +++ /dev/null @@ -1,23 +0,0 @@ -; Copyright 2009 Google Inc. -; -; Licensed under the Apache License, Version 2.0 (the "License"); -; you may not use this file except in compliance with the License. -; You may obtain a copy of the License at -; -; http://www.apache.org/licenses/LICENSE-2.0 -; -; Unless required by applicable law or agreed to in writing, software -; distributed under the License is distributed on an "AS IS" BASIS, -; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -; See the License for the specific language governing permissions and -; limitations under the License. -; ======================================================================== - -EXPORTS - NP_GetEntryPoints @1 - NP_Initialize @2 - NP_Shutdown @3 - DllCanUnloadNow PRIVATE - DllGetClassObject PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE diff --git a/omaha/plugins/update/npapi/dispatch_host.cc b/omaha/plugins/update/npapi/dispatch_host.cc deleted file mode 100644 index d188031ca..000000000 --- a/omaha/plugins/update/npapi/dispatch_host.cc +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// TODO(omaha): use NPN_SetException to return useful error information. - -#include "omaha/plugins/update/npapi/dispatch_host.h" - -#include -#include "base/logging.h" -#include "base/scope_guard.h" -#include "omaha/base/safe_format.h" -#include "omaha/base/string.h" -#include "omaha/plugins/update/npapi/variant_utils.h" - -namespace omaha { - -namespace { - -void SetExceptionIfFailed(NPObject* object, HRESULT result) { - if (FAILED(result)) { - CStringA message; - SafeCStringAFormat(&message, "0x%08x", result); - NPN_SetException(object, message); - } -} - -} // namespace - -DispatchHost* DispatchHost::CreateInstance(NPP npp, IDispatch* dispatch) { - ASSERT1(dispatch); - DispatchHost* host = static_cast( - NPN_CreateObject(npp, &kNPClass_)); - host->dispatch_ = dispatch; - CORE_LOG(L3, (L"[DispatchHost::DispatchHost][this=0x%p][dispatch=0x%p]", - host, dispatch)); - return host; -} - -DispatchHost::DispatchHost(NPP npp) : npp_(npp) { -} - -DispatchHost::~DispatchHost() { - CORE_LOG(L3, (L"[DispatchHost::~DispatchHost][this=0x%p][dispatch=0x%p]", - this, dispatch_)); -} - -DISPID DispatchHost::GetDispatchId(NPIdentifier name) { - NPUTF8* utf8_name = NPN_UTF8FromIdentifier(name); - CString wide_name = Utf8ToWideChar(utf8_name, lstrlenA(utf8_name)); - NPN_MemFree(utf8_name); - DISPID dispatch_id = DISPID_UNKNOWN; - HRESULT hr = dispatch_.GetIDOfName(wide_name, &dispatch_id); - if (FAILED(hr)) { - return DISPID_UNKNOWN; - } - return dispatch_id; -} - -// Whether or not a member should be treated as a property by NPAPI. A member is -// considered a property for NPAPI if either of the following are true: -// - The property is a getter with exactly one [out, retval] argument. -// - The property is a putter with exactly one [in] argument. -// The reason for this limitation is NPAPI does not support passing additional -// arguments when getting/setting properties. Properties that take additional -// arguments are handled as methods by NPAPI instead. -bool DispatchHost::IsProperty(DISPID dispatch_id) { - CComPtr type_info; - HRESULT hr = dispatch_->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &type_info); - if (FAILED(hr)) { - ASSERT(false, (L"[IsProperty][failed=0x%08x]", hr)); - return false; - } - TYPEATTR* type_attr; - hr = type_info->GetTypeAttr(&type_attr); - if (FAILED(hr)) { - ASSERT(false, (L"[IsProperty][failed=0x%08x]", hr)); - return false; - } - ON_SCOPE_EXIT_OBJ(*type_info.p, &ITypeInfo::ReleaseTypeAttr, type_attr); - - for (int i = 0; i < type_attr->cFuncs; ++i) { - FUNCDESC* func_desc = NULL; - hr = type_info->GetFuncDesc(i, &func_desc); - if (FAILED(hr)) { - ASSERT(false, (L"[IsProperty][failed=0x%08x]", hr)); - return false; - } - ON_SCOPE_EXIT_OBJ(*type_info.p, &ITypeInfo::ReleaseFuncDesc, func_desc); - if (dispatch_id == func_desc->memid) { - if (((func_desc->invkind & DISPATCH_PROPERTYGET) && - func_desc->cParams == 0) || - ((func_desc->invkind & DISPATCH_PROPERTYPUT) && - func_desc->cParams == 1 && - (func_desc->lprgelemdescParam[0].paramdesc.wParamFlags & - PARAMFLAG_FIN))) { - return true; - } - } - } - return false; -} - -// Simple helper to adapt NPAPI method/property invocations to IDispatch::Invoke -// by wrapping/unwrapping NPVariants into VARIANTs. -HRESULT DispatchHost::InvokeHelper(DISPID dispatch_id, WORD flags, - const NPVariant* args, uint32_t arg_count, - NPP npp, NPVariant* result) { - ASSERT1(args || arg_count == 0); - ASSERT1(result); - CORE_LOG(L3, (L"[InvokeHelper][this=0x%p][dispatch=0x%p][flags=0x%x]" - L"[arg_count=%d]", this, dispatch_, flags, arg_count)); - - // Just in case a rogue browser decides to use the return value on failure. - VOID_TO_NPVARIANT(*result); - std::unique_ptr dispatch_args(new CComVariant[arg_count]); - - // IDispatch::Invoke expects arguments in "reverse" order - for (uint32_t i = 0 ; i < arg_count; ++i) { - NPVariantToVariant(npp, args[i], &dispatch_args[arg_count - i - 1]); - } - DISPPARAMS dispatch_params = {}; - dispatch_params.rgvarg = dispatch_args.get(); - dispatch_params.cArgs = arg_count; - CComVariant dispatch_result; - HRESULT hr = dispatch_->Invoke(dispatch_id, IID_NULL, LOCALE_USER_DEFAULT, - flags, &dispatch_params, &dispatch_result, - NULL, NULL); - if (FAILED(hr)) { - CORE_LOG(L3, (L"[InvokeHelper][failed_hr=0x%p]", hr)); - return hr; - } - VariantToNPVariant(npp, dispatch_result, result); - return hr; -} - -NPObject* DispatchHost::Allocate(NPP npp, NPClass* class_functions) { - UNREFERENCED_PARAMETER(class_functions); - return new DispatchHost(npp); -} - -void DispatchHost::Deallocate(NPObject* object) { - delete static_cast(object); -} - -bool DispatchHost::HasMethod(NPObject* object, NPIdentifier name) { - DispatchHost* host = static_cast(object); - DISPID dispatch_id = host->GetDispatchId(name); - return dispatch_id != DISPID_UNKNOWN && !host->IsProperty(dispatch_id); -} - -bool DispatchHost::Invoke(NPObject* object, NPIdentifier name, - const NPVariant* args, uint32_t arg_count, - NPVariant* result) { - DispatchHost* host = static_cast(object); - CORE_LOG(L3, (L"[DispatchHost::Invoke][this=0x%p][dispatch=0x%p]", - host, host->dispatch_)); - HRESULT hr = host->InvokeHelper(host->GetDispatchId(name), - DISPATCH_METHOD | DISPATCH_PROPERTYGET, - args, - arg_count, - host->npp_, - result); - SetExceptionIfFailed(object, hr); - return SUCCEEDED(hr); -} - -bool DispatchHost::InvokeDefault(NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result) { - DispatchHost* host = static_cast(object); - CORE_LOG(L3, (L"[DispatchHost::InvokeDefault][this=0x%p][dispatch=0x%p]", - host, host->dispatch_)); - HRESULT hr = host->InvokeHelper(DISPID_VALUE, - DISPATCH_METHOD | DISPATCH_PROPERTYGET, - args, - arg_count, - host->npp_, - result); - SetExceptionIfFailed(object, hr); - return SUCCEEDED(hr); -} - -bool DispatchHost::HasProperty(NPObject* object, NPIdentifier name) { - DispatchHost* host = static_cast(object); - DISPID dispatch_id = host->GetDispatchId(name); - return dispatch_id != DISPID_UNKNOWN && host->IsProperty(dispatch_id); -} - -bool DispatchHost::GetProperty(NPObject* object, NPIdentifier name, - NPVariant* result) { - DispatchHost* host = static_cast(object); - CORE_LOG(L3, (L"[DispatchHost::GetProperty][this=0x%p][dispatch=0x%p]", - host, host->dispatch_)); - HRESULT hr = host->InvokeHelper(host->GetDispatchId(name), - DISPATCH_PROPERTYGET, - NULL, - 0, - host->npp_, - result); - SetExceptionIfFailed(object, hr); - return SUCCEEDED(hr); -} - -bool DispatchHost::SetProperty(NPObject* object, NPIdentifier name, - const NPVariant* value) { - DispatchHost* host = static_cast(object); - CORE_LOG(L3, (L"[DispatchHost::SetProperty][this=0x%p][dispatch=0x%p]", - host, host->dispatch_)); - DISPID dispatch_id = host->GetDispatchId(name); - CComVariant dispatch_arg; - NPVariantToVariant(host->npp_, *value, &dispatch_arg); - HRESULT hr = host->dispatch_.PutProperty(dispatch_id, &dispatch_arg); - SetExceptionIfFailed(object, hr); - return SUCCEEDED(hr); -} - -bool DispatchHost::RemoveProperty(NPObject* object, NPIdentifier name) { - UNREFERENCED_PARAMETER(object); - UNREFERENCED_PARAMETER(name); - return false; -} - -bool DispatchHost::Enumerate(NPObject* object, NPIdentifier** names, - uint32_t* count) { - UNREFERENCED_PARAMETER(object); - UNREFERENCED_PARAMETER(names); - UNREFERENCED_PARAMETER(count); - return false; -} - -bool DispatchHost::Construct(NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result) { - UNREFERENCED_PARAMETER(object); - UNREFERENCED_PARAMETER(args); - UNREFERENCED_PARAMETER(arg_count); - UNREFERENCED_PARAMETER(result); - return false; -} - -NPClass DispatchHost::kNPClass_ = { - NP_CLASS_STRUCT_VERSION, - Allocate, - Deallocate, - NULL, - HasMethod, - Invoke, - InvokeDefault, - HasProperty, - GetProperty, - SetProperty, - RemoveProperty, - Enumerate, - Construct, -}; - -} // namespace omaha diff --git a/omaha/plugins/update/npapi/dispatch_host.h b/omaha/plugins/update/npapi/dispatch_host.h deleted file mode 100644 index 7bdcb02e5..000000000 --- a/omaha/plugins/update/npapi/dispatch_host.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// DispatchHost hosts an IDispatch object inside a NPObject to allow scripting -// of COM objects from a NPAPI environment. Types are automatically marshalled -// between NPVariant and VARIANT using the functions in variant_utils.h. -// Limitations: -// - IDispatch methods/properties may only take arguments of type VT_VOID, -// VT_NULL, VT_BOOL, VT_I4, VT_R8, and VT_BSTR -// - Multiple out parameters are not supported. -// - IDispatch methods/properties may only return a value of type VT_EMPTY, -// VT_VOID, VT_NULL, VT_BOOL, VT_I4, VT_UI4, VT_R8, VT_BSTR, and VT_DISPATCH -// - A method and property a property that takes additional arguments may not -// have the same identifier--the method will not be callable through -// DispatchHost. - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_DISPATCH_HOST_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_DISPATCH_HOST_H_ - -#include -#include - -#include "base/basictypes.h" -#include "third_party/npapi/bindings/nphostapi.h" - -namespace omaha { - -class DispatchHostTest; - -class DispatchHost : public NPObject { - public: - static DispatchHost* CreateInstance(NPP npp, IDispatch* dispatch); - - private: - explicit DispatchHost(NPP npp); - ~DispatchHost(); - - DISPID GetDispatchId(NPIdentifier name); - bool IsProperty(DISPID dispatch_id); - HRESULT InvokeHelper(DISPID dispatch_id, WORD flags, const NPVariant* args, - uint32_t arg_count, NPP npp, NPVariant* result); - - static NPObject* Allocate(NPP npp, NPClass *class_functions); - static void Deallocate(NPObject* object); - static bool HasMethod(NPObject* object, NPIdentifier name); - static bool Invoke(NPObject* object, NPIdentifier name, const NPVariant* args, - uint32_t arg_count, NPVariant* result); - static bool InvokeDefault(NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result); - static bool HasProperty(NPObject* object, NPIdentifier name); - static bool GetProperty(NPObject* object, NPIdentifier name, - NPVariant* result); - static bool SetProperty(NPObject* object, NPIdentifier name, - const NPVariant* value); - static bool RemoveProperty(NPObject* object, NPIdentifier name); - static bool Enumerate(NPObject* object, NPIdentifier** names, - uint32_t* count); - static bool Construct(NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result); - - NPP npp_; - // The hosted dispatch object. - CComPtr dispatch_; - - // The NPObject vtable. - static NPClass kNPClass_; - - friend class DispatchHostTest; - - DISALLOW_COPY_AND_ASSIGN(DispatchHost); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_DISPATCH_HOST_H_ diff --git a/omaha/plugins/update/npapi/dispatch_host_unittest.cc b/omaha/plugins/update/npapi/dispatch_host_unittest.cc deleted file mode 100644 index 1fe08e10a..000000000 --- a/omaha/plugins/update/npapi/dispatch_host_unittest.cc +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/dispatch_host.h" -#include -#include -#include -#include -#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h" -#include "omaha/plugins/update/npapi/testing/stubs.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -class DispatchHostTest : public testing::Test { - protected: - virtual void SetUp() { - CComPtr dispatch; - ASSERT_SUCCEEDED( - CComCoClass::CreateInstance(&dispatch)); - dispatch_host_ = NPN_CreateObject(NULL, &DispatchHost::kNPClass_); - static_cast(dispatch_host_)->dispatch_ = dispatch.Detach(); - VOID_TO_NPVARIANT(result_); - } - - virtual void TearDown() { - NPN_ReleaseObject(dispatch_host_); - for (std::vector::iterator it = args_.begin(); - it != args_.end(); ++it) { - NPN_ReleaseVariantValue(&*it); - } - NPN_ReleaseVariantValue(&result_); - } - - void UseTestInterface2() { - NPN_ReleaseObject(dispatch_host_); - CComPtr dispatch; - ASSERT_SUCCEEDED( - CComCoClass::CreateInstance(&dispatch)); - dispatch_host_ = NPN_CreateObject(NULL, &DispatchHost::kNPClass_); - static_cast(dispatch_host_)->dispatch_ = dispatch.Detach(); - } - - void PushArg(bool value) { - args_.push_back(NPVariant()); - BOOLEAN_TO_NPVARIANT(value, args_.back()); - } - - void PushArg(int32 value) { - args_.push_back(NPVariant()); - INT32_TO_NPVARIANT(value, args_.back()); - } - - void PushArg(double value) { - args_.push_back(NPVariant()); - DOUBLE_TO_NPVARIANT(value, args_.back()); - } - - void PushArg(const char* value) { - args_.push_back(NPVariant()); - - #pragma warning(push) - // conversion from 'size_t' to 'uint32_t', possible loss of data. - #pragma warning(disable : 4267) - - // TODO(omaha): _strdup is an implementation detail of the stubs. - STRINGZ_TO_NPVARIANT(_strdup(value), args_.back()); - - #pragma warning(pop) - } - - NPObject* dispatch_host_; - NPIdentifierFactory id_factory_; - - std::vector args_; - NPVariant result_; -}; - -TEST_F(DispatchHostTest, HasMethod) { - EXPECT_TRUE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("Random"))); - EXPECT_TRUE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("AddAsMethod"))); - - // Property getters with input arguments should be treated as methods. - EXPECT_TRUE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("AddAsProperty"))); - - // Properties and non-existent members are not methods. - EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("Property"))); - EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("ReadOnlyProperty"))); - EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("WriteOnlyProperty"))); - EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_, - id_factory_.Create("DoesNotExist"))); -} - -TEST_F(DispatchHostTest, InvokeNoArgs) { - EXPECT_TRUE(NPN_Invoke(NULL, dispatch_host_, id_factory_.Create("Random"), - NULL, 0, &result_)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); - EXPECT_EQ(42, result_.value.intValue); -} - -TEST_F(DispatchHostTest, InvokeWithArgs) { - PushArg(7); - PushArg(27); - EXPECT_TRUE(NPN_Invoke(NULL, dispatch_host_, - id_factory_.Create("AddAsMethod"), &args_.front(), - static_cast(args_.size()), &result_)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); - EXPECT_EQ(34, result_.value.intValue); -} - -TEST_F(DispatchHostTest, InvokePropertyWithArgs) { - // Property getters that have input args should be handle by Invoke - PushArg(8); - PushArg(15); - EXPECT_TRUE(NPN_Invoke(NULL, dispatch_host_, - id_factory_.Create("AddAsProperty"), &args_.front(), - static_cast(args_.size()), &result_)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); - EXPECT_EQ(23, result_.value.intValue); -} - -TEST_F(DispatchHostTest, InvokeNonexistentMethod) { - // Non-existent method, should fail. - INT32_TO_NPVARIANT(0x19821982, result_); - EXPECT_FALSE(NPN_Invoke(NULL, dispatch_host_, - id_factory_.Create("NonExistent"), NULL, 0, - &result_)); - EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); -} - -TEST_F(DispatchHostTest, InvokeWithIncompatibleArgs) { - PushArg("Hello World!"); - PushArg(0x19851985); - INT32_TO_NPVARIANT(0x19881988, result_); - EXPECT_FALSE(NPN_Invoke(NULL, dispatch_host_, - id_factory_.Create("AddAsMethod"), &args_.front(), - static_cast(args_.size()), &result_)); - EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); -} - -TEST_F(DispatchHostTest, InvokeWithIncorrectNumberOfArgs) { - PushArg("Don't panic."); - INT32_TO_NPVARIANT(0x77777777, result_); - EXPECT_FALSE(NPN_Invoke(NULL, dispatch_host_, id_factory_.Create("Random"), - &args_.front(), static_cast(args_.size()), - &result_)); - EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); -} - -TEST_F(DispatchHostTest, InvokeDefault) { - EXPECT_TRUE(NPN_InvokeDefault(NULL, dispatch_host_, NULL, 0, &result_)); - EXPECT_TRUE(NPVARIANT_IS_OBJECT(result_)); -} - -TEST_F(DispatchHostTest, InvokeDefaultPropertyWithArgs) { - UseTestInterface2(); - PushArg(1048576); - EXPECT_TRUE(NPN_InvokeDefault(NULL, dispatch_host_, &args_.front(), - static_cast(args_.size()), &result_)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); - EXPECT_EQ(1048576 * 2, result_.value.intValue); -} - -// TODO(omaha): implement negative test - -TEST_F(DispatchHostTest, HasProperty) { - EXPECT_TRUE(NPN_HasProperty(NULL, dispatch_host_, - id_factory_.Create("Property"))); - EXPECT_TRUE(NPN_HasProperty(NULL, dispatch_host_, - id_factory_.Create("ReadOnlyProperty"))); - EXPECT_TRUE(NPN_HasProperty(NULL, dispatch_host_, - id_factory_.Create("WriteOnlyProperty"))); - - // Property getters with input arguments should not be treated as properties. - EXPECT_FALSE(NPN_HasProperty(NULL, dispatch_host_, - id_factory_.Create("AddAsProperty"))); - - // Methods and non-existent members are not properties. - EXPECT_FALSE(NPN_HasProperty(NULL, dispatch_host_, - id_factory_.Create("Random"))); - EXPECT_FALSE(NPN_HasProperty(NULL, dispatch_host_, - id_factory_.Create("DoesNotExist"))); -} - -TEST_F(DispatchHostTest, GetProperty) { - EXPECT_TRUE(NPN_GetProperty(NULL, dispatch_host_, - id_factory_.Create("Property"), &result_)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); - EXPECT_EQ(0xdeadbeef, result_.value.intValue); -} - -TEST_F(DispatchHostTest, GetPropertyReadOnly) { - EXPECT_TRUE(NPN_GetProperty(NULL, dispatch_host_, - id_factory_.Create("ReadOnlyProperty"), - &result_)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); - EXPECT_EQ(19700101, result_.value.intValue); -} - -TEST_F(DispatchHostTest, SetProperty) { - PushArg(20002000); - EXPECT_TRUE(NPN_SetProperty(NULL, dispatch_host_, - id_factory_.Create("Property"), &args_.front())); -} - -TEST_F(DispatchHostTest, SetPropertyWriteOnly) { - PushArg(20612061); - EXPECT_TRUE(NPN_SetProperty(NULL, dispatch_host_, - id_factory_.Create("WriteOnlyProperty"), - &args_.front())); -} - -TEST_F(DispatchHostTest, Unsupported) { - EXPECT_FALSE(NPN_RemoveProperty(NULL, dispatch_host_, NULL)); - EXPECT_FALSE(NPN_Enumerate(NULL, dispatch_host_, NULL, NULL)); - EXPECT_FALSE(NPN_Construct(NULL, dispatch_host_, NULL, 0, NULL)); -} - -} // namespace omaha diff --git a/omaha/plugins/update/npapi/np_update.cc b/omaha/plugins/update/npapi/np_update.cc deleted file mode 100644 index 948c0fa99..000000000 --- a/omaha/plugins/update/npapi/np_update.cc +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/np_update.h" - -#include -#include - -#include "omaha/base/debug.h" -#include "omaha/base/scope_guard.h" -#include "omaha/base/string.h" -#include "omaha/plugins/update/config.h" -#include "omaha/plugins/update/npapi/dispatch_host.h" -#include "omaha/plugins/update/npapi/urlpropbag.h" -#include "plugins/update/activex/update_control_idl.h" - -NPError NS_PluginInitialize() { - return NPERR_NO_ERROR; -} - -void NS_PluginShutdown() { -} - -nsPluginInstanceBase* NS_NewPluginInstance(nsPluginCreateData* data) { - return new omaha::NPUpdate(data->instance, data->type); -} - -void NS_DestroyPluginInstance(nsPluginInstanceBase* plugin) { - delete plugin; -} - -namespace omaha { - -NPUpdate::NPUpdate(NPP instance, const char* mime_type) - : instance_(instance), - is_initialized_(false), - mime_type_(mime_type), - scriptable_object_(NULL) { - ASSERT1(instance); - // TODO(omaha): initialize COM -} - -NPUpdate::~NPUpdate() { - if (scriptable_object_) { - NPN_ReleaseObject(scriptable_object_); - } -} - -NPBool NPUpdate::init(NPWindow* np_window) { - UNREFERENCED_PARAMETER(np_window); - is_initialized_ = true; - return TRUE; -} - -void NPUpdate::shut() { - is_initialized_ = false; -} - -NPBool NPUpdate::isInitialized() { - // TODO(omaha): figure the right boolean type to return here... - return is_initialized_ ? TRUE : FALSE; -} - -NPError NPUpdate::GetValue(NPPVariable variable, void* value) { - if (!instance_) { - return NPERR_INVALID_INSTANCE_ERROR; - } - - if (NPPVpluginScriptableNPObject != variable || !value) { - return NPERR_INVALID_PARAM; - } - - CString url; - if (!GetCurrentBrowserUrl(&url) || !site_lock_.InApprovedDomain(url)) { - return NPERR_INVALID_URL; - } - - if (!scriptable_object_) { - CComPtr p; - - CLSID clsid; - if (!MapMimeTypeToClsid(&clsid)) { - return NPERR_INVALID_PLUGIN_ERROR; - } - if (FAILED(p.CoCreateInstance(clsid))) { - return NPERR_OUT_OF_MEMORY_ERROR; - } - - // Store the current URL in a property bag and set it as the site of - // the object. - CComPtr pb; - if (FAILED(UrlPropertyBag::Create(url, &pb))) { - return NPERR_GENERIC_ERROR; - } - CComPtr sited_obj; - if (FAILED(p.QueryInterface(&sited_obj))) { - return NPERR_GENERIC_ERROR; - } - if (FAILED(sited_obj->SetSite(pb))) { - return NPERR_GENERIC_ERROR; - } - - scriptable_object_ = DispatchHost::CreateInstance(instance_, p); - } - - if (scriptable_object_) { - NPN_RetainObject(scriptable_object_); - } else { - return NPERR_OUT_OF_MEMORY_ERROR; - } - - *(reinterpret_cast(value)) = scriptable_object_; - return NPERR_NO_ERROR; -} - -bool NPUpdate::MapMimeTypeToClsid(CLSID* clsid) { - ASSERT1(clsid); - // TODO(omaha): We could probably abstract this out to a map that can - // have entries added to it at runtime, making this module fully generic. - // We could also consider extracting the MIME_TYPE resource from the current - // DLL and populating it from that. - if (0 == mime_type_.CompareNoCase(CString(UPDATE3WEB_MIME_TYPE))) { - *clsid = __uuidof(GoogleUpdate3WebControlCoClass); - return true; - } - if (0 == mime_type_.CompareNoCase(CString(ONECLICK_MIME_TYPE))) { - *clsid = __uuidof(GoogleUpdateOneClickControlCoClass); - return true; - } - return false; -} - -bool NPUpdate::GetCurrentBrowserUrl(CString* url) { - ASSERT1(url); - - NPObject* window = NULL; - NPError error = NPN_GetValue(instance_, NPNVWindowNPObject, &window); - if (NPERR_NO_ERROR != error || !window) { - ASSERT(false, (L"NPN_GetValue returned error %d", error)); - return false; - } - ON_SCOPE_EXIT(NPN_ReleaseObject, window); - - NPIdentifier location_id = NPN_GetStringIdentifier("location"); - NPVariant location; - NULL_TO_NPVARIANT(location); - if (!NPN_GetProperty(instance_, window, location_id, &location)) { - ASSERT1(false); - return false; - } - ON_SCOPE_EXIT(NPN_ReleaseVariantValue, &location); - if (!NPVARIANT_IS_OBJECT(location)) { - ASSERT(false, (L"Variant type: %d", location.type)); - return false; - } - - NPIdentifier href_id = NPN_GetStringIdentifier("href"); - NPVariant href; - NULL_TO_NPVARIANT(href); - if (!NPN_GetProperty(instance_, NPVARIANT_TO_OBJECT(location), href_id, - &href)) { - ASSERT1(false); - return false; - } - ON_SCOPE_EXIT(NPN_ReleaseVariantValue, &href); - if (!NPVARIANT_IS_STRING(href)) { - ASSERT(false, (L"Variant type: %d", href.type)); - return false; - } - - *url = Utf8ToWideChar(NPVARIANT_TO_STRING(href).UTF8Characters, - NPVARIANT_TO_STRING(href).UTF8Length); - return true; -} - -} // namespace omaha diff --git a/omaha/plugins/update/npapi/np_update.h b/omaha/plugins/update/npapi/np_update.h deleted file mode 100644 index c8668e7c0..000000000 --- a/omaha/plugins/update/npapi/np_update.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_NP_UPDATE_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_NP_UPDATE_H_ - -#include -#include "base/basictypes.h" -#include "omaha/plugins/base/pluginbase.h" -#include "omaha/plugins/update/site_lock.h" -#include "third_party/npapi/bindings/nphostapi.h" - -namespace omaha { - -class DispatchHost; - -class NPUpdate : public nsPluginInstanceBase { - public: - explicit NPUpdate(NPP instance, const char* mime_type); - virtual ~NPUpdate(); - - // nsPluginInstanceBase overrides. - virtual NPBool init(NPWindow* np_window); - virtual void shut(); - virtual NPBool isInitialized(); - virtual NPError GetValue(NPPVariable variable, void* value); - - private: - bool GetCurrentBrowserUrl(CString* url); - bool MapMimeTypeToClsid(CLSID* clsid); - - NPP instance_; - bool is_initialized_; - CString mime_type_; - SiteLock site_lock_; - DispatchHost* scriptable_object_; - - friend class NPUpdateTest; - - DISALLOW_COPY_AND_ASSIGN(NPUpdate); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_NP_UPDATE_H_ diff --git a/omaha/plugins/update/npapi/npfunction_host.cc b/omaha/plugins/update/npapi/npfunction_host.cc deleted file mode 100644 index fd2c79219..000000000 --- a/omaha/plugins/update/npapi/npfunction_host.cc +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/npfunction_host.h" - -#include -#include "omaha/base/debug.h" -#include "omaha/base/utils.h" -#include "omaha/plugins/update/npapi/variant_utils.h" - -namespace omaha { - -typedef CComObject CComNpFuncHost; -typedef scoped_any scoped_host; - -HRESULT NpFunctionHost::Create(NPP npp, NPObject* npobj, IDispatch** host) { - ASSERT1(npobj); - ASSERT1(host); - - if (!npobj || !host) { - return E_INVALIDARG; - } - - // Create the host and hand off the NPObject to it. - scoped_host comobj; - HRESULT hr = CComNpFuncHost::CreateInstance(address(comobj)); - if (FAILED(hr)) { - return hr; - } - get(comobj)->AddRef(); - - comobj->npp_ = npp; - comobj->obj_ = npobj; - NPN_RetainObject(npobj); - - return comobj->QueryInterface(host); -} - -STDMETHODIMP NpFunctionHost::GetTypeInfoCount(UINT* pctinfo) { - if (pctinfo == NULL) { - return E_INVALIDARG; - } - *pctinfo = 0; - return S_OK; -} - -STDMETHODIMP NpFunctionHost::GetTypeInfo(UINT iTInfo, - LCID lcid, - ITypeInfo** ppTInfo) { - UNREFERENCED_PARAMETER(iTInfo); - UNREFERENCED_PARAMETER(lcid); - UNREFERENCED_PARAMETER(ppTInfo); - - return E_NOTIMPL; -} - -STDMETHODIMP NpFunctionHost::GetIDsOfNames(REFIID riid, - LPOLESTR* rgszNames, - UINT cNames, - LCID lcid, - DISPID* rgDispId) { - UNREFERENCED_PARAMETER(riid); - UNREFERENCED_PARAMETER(rgszNames); - UNREFERENCED_PARAMETER(cNames); - UNREFERENCED_PARAMETER(lcid); - UNREFERENCED_PARAMETER(rgDispId); - - return E_NOTIMPL; -} - -STDMETHODIMP NpFunctionHost::Invoke(DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS* pDispParams, - VARIANT* pVarResult, - EXCEPINFO* pExcepInfo, - UINT* puArgErr) { - UNREFERENCED_PARAMETER(dispIdMember); - UNREFERENCED_PARAMETER(riid); - UNREFERENCED_PARAMETER(lcid); - UNREFERENCED_PARAMETER(pExcepInfo); - UNREFERENCED_PARAMETER(puArgErr); - - if (wFlags != DISPATCH_METHOD) { - return DISP_E_MEMBERNOTFOUND; - } - - uint32_t num_args = 0; - std::unique_ptr arguments; - if (pDispParams) { - // Javascript doesn't officially support named args, so the current - // implementation ignores any named args that are supplied. However, - // you can cast a function object to a string and it will hold the - // argument names as used in the function definition. Thus, if we - // need to support named arguments in the future, we may be able to - // get the argument names indirectly using NPN_Evaluate() and emulate. - if (pDispParams->cNamedArgs != 0) { - return DISP_E_NONAMEDARGS; - } - if (pDispParams->cArgs != 0) { - num_args = pDispParams->cArgs; - arguments.reset(new NPVariant[num_args]); - for (uint32_t i = 0; i < num_args; ++i) { - // Arguments are stored in rgvarg in reverse order. - VariantToNPVariant(npp_, - pDispParams->rgvarg[num_args - 1 - i], - &arguments[i]); - } - } - } - - NPVariant retval; - VOID_TO_NPVARIANT(retval); - - bool result = NPN_InvokeDefault(npp_, - obj_, - arguments.get(), - num_args, - &retval); - if (result && pVarResult) { - NPVariantToVariant(npp_, retval, pVarResult); - } - NPN_ReleaseVariantValue(&retval); - - return result ? S_OK : E_FAIL; -} - -void NpFunctionHost::FinalRelease() { - ASSERT1(obj_); - if (obj_) { - NPN_ReleaseObject(obj_); - obj_ = NULL; - } -} - -NpFunctionHost::NpFunctionHost() : npp_(NULL), obj_(NULL) {} - -} // namespace omaha - diff --git a/omaha/plugins/update/npapi/npfunction_host.h b/omaha/plugins/update/npapi/npfunction_host.h deleted file mode 100644 index 11587d4c2..000000000 --- a/omaha/plugins/update/npapi/npfunction_host.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// NpFunctionHost hosts an NPObject inside an IDispatch interface to allow -// invoking NPAPI functions from a COM environment. Types are automatically -// marshalled between NPVariant and VARIANT using the functions in -// variant_utils.h. (For the reverse -- providing an NPObject interface to -// a COM object implementing IDispatch -- see DispatchHost.) -// -// Note that this currently only supports functions; this does not currently -// support objects. NPN_Enumerate() only provides method/property names, -// not return types or argument counts/types, which makes it impossible to -// properly implement IDispatch::GetTypeInfo(). (However, we can implement -// GetIDsOfNames() if we need it in the future.) - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_NPFUNCTION_HOST_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_NPFUNCTION_HOST_H_ - -#include -#include - -#include "base/basictypes.h" -#include "third_party/npapi/bindings/nphostapi.h" - -namespace omaha { - -class NpFunctionHostTest; - -class ATL_NO_VTABLE NpFunctionHost - : public CComObjectRootEx, - public IDispatch { - public: - static HRESULT Create(NPP npp, NPObject* npobj, IDispatch** host); - - BEGIN_COM_MAP(NpFunctionHost) - COM_INTERFACE_ENTRY(IDispatch) - END_COM_MAP() - - // IDispatch methods. - STDMETHOD(GetTypeInfoCount)(UINT* pctinfo); - STDMETHOD(GetTypeInfo)(UINT iTInfo, - LCID lcid, - ITypeInfo** ppTInfo); - STDMETHOD(GetIDsOfNames)(REFIID riid, - LPOLESTR* rgszNames, - UINT cNames, - LCID lcid, - DISPID* rgDispId); - STDMETHOD(Invoke)(DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS* pDispParams, - VARIANT* pVarResult, - EXCEPINFO* pExcepInfo, - UINT* puArgErr); - - // CComObjectRootEx overrides. - void FinalRelease(); - - protected: - NpFunctionHost(); - virtual ~NpFunctionHost() {} - - private: - NPP npp_; - NPObject* obj_; - - DISALLOW_COPY_AND_ASSIGN(NpFunctionHost); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_NPFUNCTION_HOST_H_ diff --git a/omaha/plugins/update/npapi/npfunction_host_unittest.cc b/omaha/plugins/update/npapi/npfunction_host_unittest.cc deleted file mode 100644 index ca9d8c5af..000000000 --- a/omaha/plugins/update/npapi/npfunction_host_unittest.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/npfunction_host.h" -#include -#include -#include -#include -#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h" -#include "omaha/plugins/update/npapi/testing/stubs.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -class NpFunctionHostTest; - -class MockFunctionNPObject : public NPObject { - public: - static MockFunctionNPObject* CreateInstance(NpFunctionHostTest* creator) { - NPObject* obj = NPN_CreateObject(NULL, &MockFunctionNPObject::kNPClass_); - MockFunctionNPObject* realobj = static_cast(obj); - realobj->creator_ = creator; - return realobj; - } - - static NPObject* Allocate(NPP npp, NPClass* class_functions) { - UNREFERENCED_PARAMETER(class_functions); - return new MockFunctionNPObject(npp); - } - - static void Deallocate(NPObject* object) { - delete static_cast(object); - } - - static bool InvokeDefault(NPObject* object, - const NPVariant* args, - uint32_t arg_count, - NPVariant* result) { - MockFunctionNPObject* realobj = static_cast(object); - return realobj->InvokeDefaultLocal(args, arg_count, result); - } - - bool InvokeDefaultLocal(const NPVariant* args, - uint32_t arg_count, - NPVariant* result); - - protected: - explicit MockFunctionNPObject(NPP npp) : npp_(npp), creator_(NULL) {} - - static NPUTF8* NPN_ReallocateStringZ(const char* string) { - uint32 buflen = static_cast(strlen(string) + 1); - NPUTF8* npnstr = reinterpret_cast(NPN_MemAlloc(buflen)); - memmove(npnstr, string, buflen); - return npnstr; - } - - private: - NPP npp_; - NpFunctionHostTest* creator_; - - // The NPObject vtable. - static NPClass kNPClass_; -}; - -class NpFunctionHostTest : public testing::Test { - protected: - friend class MockFunctionNPObject; - - virtual void SetUp() { - function_ = MockFunctionNPObject::CreateInstance(this); - EXPECT_SUCCEEDED(NpFunctionHost::Create(NULL, function_, &host_)); - } - - virtual void TearDown() { - } - - NPObject* function_; - CComPtr host_; - - std::vector mock_args_; - NPVariant mock_result_; -}; - -NPClass MockFunctionNPObject::kNPClass_ = { - NP_CLASS_STRUCT_VERSION, - Allocate, - Deallocate, - NULL, - NULL, - NULL, - InvokeDefault, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, -}; - -bool MockFunctionNPObject::InvokeDefaultLocal(const NPVariant* args, - uint32_t arg_count, - NPVariant* result) { - const char* kMultiStringReturn = "multi"; - - // The mock NPObject exhibits the following external behavior: - // * If no arguments, return nothing - // * If one argument, return a boolean (true) - // * If two arguments, return a string ("multi") - // * Otherwise, treat it as an invoke failure. - // It also copies the arguments as supplied, and the intended NPVariant - // return value, to the test closure that created it. - - creator_->mock_args_.resize(arg_count); - for (uint32_t i = 0; i < arg_count; ++i) { - creator_->mock_args_[i] = args[i]; - } - - switch (arg_count) { - case 0: - VOID_TO_NPVARIANT(*result); - break; - case 1: - BOOLEAN_TO_NPVARIANT(true, *result); - break; - case 2: - { - #pragma warning(push) - // conversion from 'size_t' to 'uint32_t', possible loss of data. - #pragma warning(disable : 4267) - - NPUTF8* utf8string = NPN_ReallocateStringZ(kMultiStringReturn); - STRINGZ_TO_NPVARIANT(utf8string, *result); - - #pragma warning(pop) - } - break; - default: - return false; - } - - creator_->mock_result_ = *result; - return true; -} - -TEST_F(NpFunctionHostTest, GetTypeInfoCount) { - UINT typeinfos_available = 1; - EXPECT_SUCCEEDED(host_->GetTypeInfoCount(&typeinfos_available)); - EXPECT_EQ(0, typeinfos_available); -} - -TEST_F(NpFunctionHostTest, GetTypeInfo_NotImplemented) { - ITypeInfo* typeinfo = NULL; - - EXPECT_EQ(E_NOTIMPL, host_->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &typeinfo)); -} - -TEST_F(NpFunctionHostTest, GetIDsOfNames_NotImplemented) { - LPOLESTR member_name = const_cast(L"NonexistentMember"); - DISPID member_dispid = 0; - EXPECT_EQ(E_NOTIMPL, host_->GetIDsOfNames(IID_NULL, &member_name, 1, - LOCALE_SYSTEM_DEFAULT, - &member_dispid)); -} - -TEST_F(NpFunctionHostTest, Invoke_NonMethod_NotSupported) { - EXPECT_EQ(DISP_E_MEMBERNOTFOUND, host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_PROPERTYGET, - NULL, - NULL, - NULL, - NULL)); -} - -TEST_F(NpFunctionHostTest, Invoke_NamedArgs_NotSupported) { - DISPID param_name = 12; - DISPPARAMS params = {}; - params.cNamedArgs = 1; - params.rgdispidNamedArgs = ¶m_name; - EXPECT_EQ(DISP_E_NONAMEDARGS, host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - ¶ms, - NULL, - NULL, - NULL)); -} - -TEST_F(NpFunctionHostTest, Invoke_NoArgs_NullDispParams) { - VARIANT retval = {}; - EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - NULL, - &retval, - NULL, - NULL)); - - EXPECT_EQ(0, static_cast(mock_args_.size())); - - EXPECT_TRUE(NPVARIANT_IS_VOID(mock_result_)); - EXPECT_EQ(VT_EMPTY, retval.vt); - VariantClear(&retval); -} - -TEST_F(NpFunctionHostTest, Invoke_NoArgs_ValidDispParams) { - VARIANT retval = {}; - DISPPARAMS params = {}; - EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - ¶ms, - &retval, - NULL, - NULL)); - - EXPECT_EQ(0, static_cast(mock_args_.size())); - - EXPECT_TRUE(NPVARIANT_IS_VOID(mock_result_)); - EXPECT_EQ(VT_EMPTY, retval.vt); - VariantClear(&retval); -} - -TEST_F(NpFunctionHostTest, Invoke_NoArgs_OneParam) { - const int kTestIntVal = 0xDEADBEEF; - - VARIANT retval = {}; - VARIANT firstparam = {}; - firstparam.vt = VT_I4; - firstparam.intVal = kTestIntVal; - - DISPPARAMS dispparams = {}; - dispparams.cArgs = 1; - dispparams.rgvarg = &firstparam; - - EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - &dispparams, - &retval, - NULL, - NULL)); - - EXPECT_EQ(1, mock_args_.size()); - EXPECT_TRUE(NPVARIANT_IS_INT32(mock_args_[0])); - EXPECT_EQ(kTestIntVal, NPVARIANT_TO_INT32(mock_args_[0])); - - EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(mock_result_)); - EXPECT_EQ(true, NPVARIANT_TO_BOOLEAN(mock_result_)); - EXPECT_EQ(VT_BOOL, retval.vt); - EXPECT_EQ(VARIANT_TRUE, retval.boolVal); - VariantClear(&retval); -} - -TEST_F(NpFunctionHostTest, Invoke_NoArgs_TwoParams) { - const double kTestFloatVal = 3.1415927; - - VARIANT retval = {}; - VARIANT params[2] = {}; - params[0].vt = VT_BOOL; // Invoke expects args in reverse order - params[0].intVal = VARIANT_TRUE; - params[1].vt = VT_R8; - params[1].dblVal = kTestFloatVal; - - DISPPARAMS dispparams = {}; - dispparams.cArgs = 2; - dispparams.rgvarg = params; - EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - &dispparams, - &retval, - NULL, - NULL)); - - EXPECT_EQ(2, mock_args_.size()); - EXPECT_TRUE(NPVARIANT_IS_DOUBLE(mock_args_[0])); - EXPECT_EQ(kTestFloatVal, NPVARIANT_TO_DOUBLE(mock_args_[0])); - EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(mock_args_[1])); - EXPECT_EQ(true, NPVARIANT_TO_BOOLEAN(mock_args_[1])); - - EXPECT_TRUE(NPVARIANT_IS_STRING(mock_result_)); - // Don't check mock_result's contents; it will have been released by Invoke() - EXPECT_EQ(VT_BSTR, retval.vt); - EXPECT_STREQ(CString("multi"), CString(retval.bstrVal)); - VariantClear(&retval); -} - -TEST_F(NpFunctionHostTest, Invoke_NoArgs_ThreeParams) { - VARIANT retval = {}; - VARIANT params[3] = {}; - for (int i = 0; i < 3; ++i) { - params[i].vt = VT_BOOL; - params[i].intVal = VARIANT_TRUE; - } - - DISPPARAMS dispparams = {}; - dispparams.cArgs = 3; - dispparams.rgvarg = params; - EXPECT_EQ(E_FAIL, host_->Invoke(0, IID_NULL, - LOCALE_SYSTEM_DEFAULT, - DISPATCH_METHOD, - &dispparams, - &retval, - NULL, - NULL)); - EXPECT_EQ(3, mock_args_.size()); -} - - -} // namespace omaha diff --git a/omaha/plugins/update/npapi/testing/dispatch_host_test.rc b/omaha/plugins/update/npapi/testing/dispatch_host_test.rc deleted file mode 100644 index 0b44ad0ec..000000000 --- a/omaha/plugins/update/npapi/testing/dispatch_host_test.rc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include -#include "omaha/plugins/update/npapi/testing/resource.h" - -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -IDR_DISPATCH_HOST_TEST_TLB TYPELIB "plugins/update/npapi/testing/dispatch_host_test_idl.tlb" diff --git a/omaha/plugins/update/npapi/testing/dispatch_host_test_idl.idl b/omaha/plugins/update/npapi/testing/dispatch_host_test_idl.idl deleted file mode 100644 index dcb1007d6..000000000 --- a/omaha/plugins/update/npapi/testing/dispatch_host_test_idl.idl +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -import "oaidl.idl"; -import "ocidl.idl"; - -[ - object, - uuid(d5c74868-1734-44e1-bab2-187a0ad2bd37), - dual, - pointer_default(unique) -] -interface IDispatchHostTestInterface : IDispatch { - [id(1)] HRESULT Random([out, retval] INT* x); - - [propget] HRESULT Property([out, retval] INT* x); - [propput] HRESULT Property([in] INT x); - - [propget] HRESULT ReadOnlyProperty([out, retval] INT* x); - [propput] HRESULT WriteOnlyProperty([in] INT x); - - [id(2)] HRESULT AddAsMethod([in] INT a, [in] INT b, [out, retval] INT* c); - [id(3), propget] HRESULT AddAsProperty([in] INT a, [in] INT b, - [out, retval] INT* c); - - // TODO(omaha): do we need to test multi-argument property putters? - - [id(DISPID_VALUE)] HRESULT DidYouMeanRecursion([out, retval] IDispatch** me); -}; - -[ - object, - uuid(0abeb84d-6a9c-4c5c-9394-6123c933baec), - dual, - pointer_default(unique) -] -interface IDispatchHostTestInterface2 : IDispatch { - [id(DISPID_VALUE), propget] HRESULT Get([in] INT index, [out, retval] INT* x); -}; - -[ - uuid(65530e7f-4922-49e2-a5ee-c3e172da974a), -] -library DispatchHostTestingLib { - importlib("stdole2.tlb"); - - interface IDispatchHostTestInterface; - interface IDispatchHostTestInterface2; -}; diff --git a/omaha/plugins/update/npapi/testing/dispatch_host_test_interface.cc b/omaha/plugins/update/npapi/testing/dispatch_host_test_interface.cc deleted file mode 100644 index 07608093f..000000000 --- a/omaha/plugins/update/npapi/testing/dispatch_host_test_interface.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h" - -namespace omaha { - -STDMETHODIMP DispatchHostTestInterface::Random(INT* x) { - *x = 42; - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::get_Property(INT* x) { - *x = 0xdeadbeef; - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::put_Property(INT x) { - UNREFERENCED_PARAMETER(x); - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::get_ReadOnlyProperty(INT* x) { - *x = 19700101; - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::put_WriteOnlyProperty(INT x) { - UNREFERENCED_PARAMETER(x); - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::AddAsMethod(INT a, INT b, INT* c) { - *c = a + b; - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::get_AddAsProperty( - INT a, INT b, INT* c) { - *c = a + b; - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface::DidYouMeanRecursion(IDispatch** me) { - *me = this; - return S_OK; -} - -STDMETHODIMP DispatchHostTestInterface2::get_Get(INT index, INT* x) { - *x = index << 1; - return S_OK; -} - -} // namespace omaha diff --git a/omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h b/omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h deleted file mode 100644 index 589b22347..000000000 --- a/omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_DISPATCH_HOST_TEST_INTERFACE_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_DISPATCH_HOST_TEST_INTERFACE_H_ - -#include -#include -#include - -#include "base/basictypes.h" -#include "omaha/base/app_util.h" -#include "omaha/plugins/update/npapi/testing/resource.h" -#include "plugins/update/npapi/testing/dispatch_host_test_idl.h" - -namespace omaha { - -// This class allows for using IDispatchImpl with a specific TypeLib, in a -// module that has multiple TYPELIB resources. Ordinarily, IDispatchImpl will -// only load the first TYPELIB resource in the module, which does not work for -// omaha_unittest.exe. Hence the need for this class. This class loads the TLB -// with the specified resource id, and uses that for subsequent IDispatch -// requests. -// TODO(omaha3): Perhaps move this class into a generic utility header. - -template -class ATL_NO_VTABLE IDispatchImplResId : public IDispatchImpl { - public: - IDispatchImplResId() { - CComPtr type_lib; - CString tlb_path; - - // Format the path as "ModulePath\\ResourceId". Specifying a ResourceId - // allows overriding the default behavior of LoadTypeLib to load the first - // TYPELIB resource from the module. - tlb_path.Format(_T("%s\\%d"), app_util::GetCurrentModuleName(), tlb_res_id); - - HRESULT hr = LoadTypeLib(tlb_path, &type_lib); - if (FAILED(hr)) { - return; - } - - CComPtr type_info; - hr = type_lib->GetTypeInfoOfGuid(*piid, &type_info); - if (FAILED(hr)) { - return; - } - - CComPtr type_info2; - if (SUCCEEDED(type_info->QueryInterface(&type_info2))) { - type_info = type_info2; - } - - // Override the ITypeInfo in the CComTypeInfoHolder, which will be used in - // subsequent calls to the IDispatch methods. - this->_tih.m_pInfo = type_info.Detach(); - } - - virtual ~IDispatchImplResId() {} -}; - -class ATL_NO_VTABLE DispatchHostTestInterface - : public CComObjectRootEx, - public IDispatchImplResId { - public: - DispatchHostTestInterface() {} - virtual ~DispatchHostTestInterface() {} - - DECLARE_NOT_AGGREGATABLE(DispatchHostTestInterface); - - BEGIN_COM_MAP(DispatchHostTestInterface) - COM_INTERFACE_ENTRY(IDispatch) - END_COM_MAP() - - // IDispatchHostTestInterface methods. - STDMETHOD(Random)(INT* x); - - STDMETHOD(get_Property)(INT* x); - STDMETHOD(put_Property)(INT x); - - STDMETHOD(get_ReadOnlyProperty)(INT* x); - STDMETHOD(put_WriteOnlyProperty)(INT x); - - STDMETHOD(AddAsMethod)(INT a, INT b, INT* c); - STDMETHOD(get_AddAsProperty)(INT a, INT b, INT* c); - - STDMETHOD(DidYouMeanRecursion)(IDispatch** me); - - private: - DISALLOW_COPY_AND_ASSIGN(DispatchHostTestInterface); -}; - -class ATL_NO_VTABLE DispatchHostTestInterface2 - : public CComObjectRootEx, - public IDispatchImplResId { - public: - DispatchHostTestInterface2() {} - virtual ~DispatchHostTestInterface2() {} - - DECLARE_NOT_AGGREGATABLE(DispatchHostTestInterface2); - - BEGIN_COM_MAP(DispatchHostTestInterface) - COM_INTERFACE_ENTRY(IDispatch) - END_COM_MAP() - - // IDispatchHostTestInterface2 methods. - STDMETHOD(get_Get)(INT index, INT* x); - - private: - DISALLOW_COPY_AND_ASSIGN(DispatchHostTestInterface2); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_DISPATCH_HOST_TEST_INTERFACE_H_ diff --git a/omaha/plugins/update/npapi/testing/resource.h b/omaha/plugins/update/npapi/testing/resource.h deleted file mode 100644 index 6bfedb3f8..000000000 --- a/omaha/plugins/update/npapi/testing/resource.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_RESOURCE_H__ -#define OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_RESOURCE_H__ - -#define IDR_DISPATCH_HOST_TEST_TLB 3 - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_RESOURCE_H__ diff --git a/omaha/plugins/update/npapi/testing/stubs.cc b/omaha/plugins/update/npapi/testing/stubs.cc deleted file mode 100644 index 30921cc5c..000000000 --- a/omaha/plugins/update/npapi/testing/stubs.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/testing/stubs.h" -#include -#include -#include "base/debug.h" - -namespace omaha { - -NPIdentifierFactory::NPIdentifierFactory() { -} - -NPIdentifierFactory::~NPIdentifierFactory() { - for (std::vector::const_iterator it = identifiers_.begin(); - it != identifiers_.end(); ++it) { - free(*it); - } -} - -NPIdentifier NPIdentifierFactory::Create(const char* name) { - NPIdentifier identifier = _strdup(name); - identifiers_.push_back(identifier); - return identifier; -} - -} // namespace omaha - -extern "C" { -void* NPN_MemAlloc(uint32 size) { - return malloc(size); -} - -void NPN_MemFree(void* ptr) { - free(ptr); -} - -NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier) { - return _strdup(static_cast(identifier)); -} - -NPObject* NPN_CreateObject(NPP npp, NPClass* class_vtable) { - UNREFERENCED_PARAMETER(npp); - ASSERT1(class_vtable); - NPObject* object = class_vtable->allocate(npp, class_vtable); - object->_class = class_vtable; - object->referenceCount = 1; - return object; -} - -NPObject* NPN_RetainObject(NPObject* object) { - ASSERT1(object); - ++object->referenceCount; - return object; -} - -void NPN_ReleaseObject(NPObject* object) { - ASSERT1(object); - ASSERT1(object->referenceCount > 0); - if (--object->referenceCount == 0) { - object->_class->deallocate(object); - } -} - -void NPN_ReleaseVariantValue(NPVariant* variant) { - if (NPVARIANT_IS_STRING(*variant)) { - NPN_MemFree(const_cast(variant->value.stringValue.UTF8Characters)); - } else if (NPVARIANT_IS_OBJECT(*variant)) { - NPN_ReleaseObject(variant->value.objectValue); - } - VOID_TO_NPVARIANT(*variant); - return; -} - -bool NPN_HasMethod(NPP npp, NPObject* object, NPIdentifier name) { - UNREFERENCED_PARAMETER(npp); - return object->_class->hasMethod(object, name); -} - -bool NPN_Invoke(NPP npp, NPObject* object, NPIdentifier name, - const NPVariant* args, uint32_t arg_count, NPVariant* result) { - UNREFERENCED_PARAMETER(npp); - return object->_class->invoke(object, name, args, arg_count, result); -} - -bool NPN_InvokeDefault(NPP npp, NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result) { - UNREFERENCED_PARAMETER(npp); - return object->_class->invokeDefault(object, args, arg_count, result); -} - -bool NPN_HasProperty(NPP npp, NPObject* object, NPIdentifier name) { - UNREFERENCED_PARAMETER(npp); - return object->_class->hasProperty(object, name); -} - -bool NPN_GetProperty(NPP npp, NPObject* object, NPIdentifier name, - NPVariant* result) { - UNREFERENCED_PARAMETER(npp); - return object->_class->getProperty(object, name, result); -} - -bool NPN_SetProperty(NPP npp, NPObject* object, NPIdentifier name, - const NPVariant* value) { - UNREFERENCED_PARAMETER(npp); - return object->_class->setProperty(object, name, value); -} - -bool NPN_RemoveProperty(NPP npp, NPObject* object, NPIdentifier name) { - UNREFERENCED_PARAMETER(npp); - return object->_class->removeProperty(object, name); -} - -bool NPN_Enumerate(NPP npp, NPObject* object, NPIdentifier** names, - uint32_t* count) { - UNREFERENCED_PARAMETER(npp); - return object->_class->enumerate(object, names, count); -} - -bool NPN_Construct(NPP npp, NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result) { - UNREFERENCED_PARAMETER(npp); - return object->_class->construct(object, args, arg_count, result); -} - -void NPN_SetException(NPObject* object, const NPUTF8* message) { - UNREFERENCED_PARAMETER(object); - UNREFERENCED_PARAMETER(message); -} -} // extern "C" diff --git a/omaha/plugins/update/npapi/testing/stubs.h b/omaha/plugins/update/npapi/testing/stubs.h deleted file mode 100644 index 76ba6b316..000000000 --- a/omaha/plugins/update/npapi/testing/stubs.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Normally, implementations of these functions are provided by the NPAPI -// runtime. These stub implementations are intended only for use in unit tests. - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_STUBS_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_STUBS_H_ - -#include -#include "third_party/npapi/bindings/nphostapi.h" - -namespace omaha { - -// Not part of NPAPI proper, but useful nonetheless. Note that the stub -// implementation of NPIdentifier does not conform to NPAPI's idea of what an -// NPIdentifier ought to be: specifically, uniqueness is not guaranteed. -class NPIdentifierFactory { - public: - NPIdentifierFactory(); - ~NPIdentifierFactory(); - NPIdentifier Create(const char* name); - - private: - std::vector identifiers_; - - DISALLOW_COPY_AND_ASSIGN(NPIdentifierFactory); -}; - -} // namespace omaha - -extern "C" { -void* NPN_MemAlloc(uint32 size); -void NPN_MemFree(void* ptr); -NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier); -NPObject* NPN_CreateObject(NPP npp, NPClass* class_vtable); -NPObject* NPN_RetainObject(NPObject* object); -void NPN_ReleaseObject(NPObject* object); -void NPN_ReleaseVariantValue(NPVariant* variant); - -bool NPN_HasMethod(NPP npp, NPObject* object, NPIdentifier name); -bool NPN_Invoke(NPP npp, NPObject* object, NPIdentifier name, - const NPVariant* args, uint32_t arg_count, NPVariant* result); -bool NPN_InvokeDefault(NPP npp, NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result); -bool NPN_HasProperty(NPP npp, NPObject* object, NPIdentifier name); -bool NPN_GetProperty(NPP npp, NPObject* object, NPIdentifier name, - NPVariant* result); -bool NPN_SetProperty(NPP npp, NPObject* object, NPIdentifier name, - const NPVariant* value); -bool NPN_RemoveProperty(NPP npp, NPObject* object, NPIdentifier name); -bool NPN_Enumerate(NPP npp, NPObject* object, NPIdentifier** names, - uint32_t* count); -bool NPN_Construct(NPP npp, NPObject* object, const NPVariant* args, - uint32_t arg_count, NPVariant* result); -void NPN_SetException(NPObject* object, const NPUTF8* message); -} // extern "C" - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_STUBS_H_ - diff --git a/omaha/plugins/update/npapi/urlpropbag.cc b/omaha/plugins/update/npapi/urlpropbag.cc deleted file mode 100644 index 940a9b494..000000000 --- a/omaha/plugins/update/npapi/urlpropbag.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/urlpropbag.h" - -#include "omaha/base/debug.h" -#include "omaha/base/utils.h" -#include "omaha/plugins/update/npapi/variant_utils.h" - -namespace omaha { - -typedef CComObject CComUrlBag; -typedef scoped_any scoped_bag; - -HRESULT UrlPropertyBag::Create(const TCHAR* url, IPropertyBag** pb) { - ASSERT1(url); - ASSERT1(pb); - - if (!url || !pb) { - return E_INVALIDARG; - } - - // Create the host and hand off the string to it. - scoped_bag comobj; - HRESULT hr = CComUrlBag::CreateInstance(address(comobj)); - if (FAILED(hr)) { - return hr; - } - get(comobj)->AddRef(); - - comobj->url_ = url; - - return comobj->QueryInterface(pb); -} - -STDMETHODIMP UrlPropertyBag::Read(LPCOLESTR pszPropName, VARIANT* pVar, - IErrorLog* pErrorLog) { - UNREFERENCED_PARAMETER(pErrorLog); - - ASSERT1(pszPropName); - ASSERT1(pVar); - if (!pszPropName || !pVar) { - return E_POINTER; - } - - if (0 == _tcscmp(pszPropName, kUrlPropertyBag_Url)) { - V_VT(pVar) = VT_BSTR; - V_BSTR(pVar) = url_.AllocSysString(); - return S_OK; - } - - return E_INVALIDARG; -} - -STDMETHODIMP UrlPropertyBag::Write(LPCOLESTR pszPropName, VARIANT* pVar) { - UNREFERENCED_PARAMETER(pszPropName); - UNREFERENCED_PARAMETER(pVar); - return E_FAIL; -} - -UrlPropertyBag::UrlPropertyBag() {} - -} // namespace omaha - diff --git a/omaha/plugins/update/npapi/urlpropbag.h b/omaha/plugins/update/npapi/urlpropbag.h deleted file mode 100644 index b670bc26d..000000000 --- a/omaha/plugins/update/npapi/urlpropbag.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_URLPROPBAG_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_URLPROPBAG_H_ - -#include -#include -#include - -#include "base/basictypes.h" - -namespace omaha { - -const TCHAR* const kUrlPropertyBag_Url = _T("omaha-urlpropertybag-url"); - -class ATL_NO_VTABLE UrlPropertyBag - : public CComObjectRootEx, - public IPropertyBag { - public: - static HRESULT Create(const TCHAR* url, IPropertyBag** pb); - - BEGIN_COM_MAP(UrlPropertyBag) - COM_INTERFACE_ENTRY(IPropertyBag) - END_COM_MAP() - - // IPropertyBag methods. - STDMETHOD(Read)(LPCOLESTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog); - STDMETHOD(Write)(LPCOLESTR pszPropName, VARIANT* pVar); - - protected: - UrlPropertyBag(); - virtual ~UrlPropertyBag() {} - - private: - CString url_; - - DISALLOW_COPY_AND_ASSIGN(UrlPropertyBag); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_URLPROPBAG_H_ diff --git a/omaha/plugins/update/npapi/variant_utils.cc b/omaha/plugins/update/npapi/variant_utils.cc deleted file mode 100644 index 4d60e0282..000000000 --- a/omaha/plugins/update/npapi/variant_utils.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// TODO(omaha): verify that the new operator throws on failure to prevent NULL -// pointer exploits. - -#include "omaha/plugins/update/npapi/variant_utils.h" - -#include - -#include -#include "base/debug.h" -#include "base/string.h" -#include "omaha/plugins/update/npapi/dispatch_host.h" -#include "omaha/plugins/update/npapi/npfunction_host.h" - -namespace omaha { - -void NPVariantToVariant(NPP npp, - const NPVariant& source, - VARIANT* destination) { - ASSERT1(destination); - V_VT(destination) = VT_EMPTY; - - switch (source.type) { - case NPVariantType_Void: - V_VT(destination) = VT_EMPTY; - break; - case NPVariantType_Null: - V_VT(destination) = VT_NULL; - break; - case NPVariantType_Bool: - V_VT(destination) = VT_BOOL; - V_BOOL(destination) = source.value.boolValue ? VARIANT_TRUE - : VARIANT_FALSE; - break; - case NPVariantType_Int32: - V_VT(destination) = VT_I4; - V_I4(destination) = source.value.intValue; - break; - case NPVariantType_Double: - V_VT(destination) = VT_R8; - V_R8(destination) = source.value.doubleValue; - break; - case NPVariantType_String: - V_VT(destination) = VT_BSTR; - if (source.value.stringValue.UTF8Length) { - CString string = Utf8ToWideChar(source.value.stringValue.UTF8Characters, - source.value.stringValue.UTF8Length); - V_BSTR(destination) = string.AllocSysString(); - } else { - V_BSTR(destination) = CString().AllocSysString(); - } - break; - case NPVariantType_Object: - V_VT(destination) = VT_DISPATCH; - if (source.value.objectValue) { - NpFunctionHost::Create(npp, source.value.objectValue, - &V_DISPATCH(destination)); - } else { - V_DISPATCH(destination) = NULL; - } - break; - default: - ASSERT1(false); - break; - } -} - -void VariantToNPVariant(NPP npp, - const VARIANT& source, - NPVariant* destination) { - ASSERT1(destination); - VOID_TO_NPVARIANT(*destination); - - switch (V_VT(&source)) { - case VT_EMPTY: - VOID_TO_NPVARIANT(*destination); - break; - case VT_NULL: - NULL_TO_NPVARIANT(*destination); - break; - case VT_BOOL: - BOOLEAN_TO_NPVARIANT(V_BOOL(&source), *destination); - break; - case VT_I4: - INT32_TO_NPVARIANT(V_I4(&source), *destination); - break; - case VT_UI4: - INT32_TO_NPVARIANT(V_UI4(&source), *destination); - break; - case VT_R8: - DOUBLE_TO_NPVARIANT(V_R8(&source), *destination); - break; - case VT_BSTR: - if (V_BSTR(&source)) { - int source_length = ::SysStringLen(V_BSTR(&source)) + 1; - int buffer_length = ::WideCharToMultiByte(CP_UTF8, 0, V_BSTR(&source), - source_length, NULL, 0, NULL, - NULL); - if (buffer_length == 0) { - break; - } - char* buffer = static_cast(NPN_MemAlloc(buffer_length)); - VERIFY1(::WideCharToMultiByte(CP_UTF8, 0, V_BSTR(&source), - source_length, buffer, buffer_length, - NULL, NULL) > 0); - STRINGN_TO_NPVARIANT(buffer, - static_cast(buffer_length - 1), - *destination); - } else { - char* buffer = static_cast(NPN_MemAlloc(1)); - buffer[0] = '\0'; - STRINGN_TO_NPVARIANT(buffer, 0, *destination); - } - break; - case VT_DISPATCH: - if (V_DISPATCH(&source)) { - OBJECT_TO_NPVARIANT( - DispatchHost::CreateInstance(npp, V_DISPATCH(&source)), - *destination); - } - break; - default: - ASSERT(false, (L"Unhandled variant type %d", V_VT(&source))); - break; - } -} - -} // namespace omaha diff --git a/omaha/plugins/update/npapi/variant_utils.h b/omaha/plugins/update/npapi/variant_utils.h deleted file mode 100644 index d28392629..000000000 --- a/omaha/plugins/update/npapi/variant_utils.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Converts between NPVariant and VARIANT types. -// The following two-way conversions are supported: -// NPVariantType_Void <-> VT_EMPTY -// NPVariantType_Null <-> VT_NULL -// NPVariantType_Bool <-> VT_BOOL -// NPVariantType_Int32 <-> VT_I4 -// NPVariantType_Double <-> VT_R8 -// NPVariantType_String <-> VT_BSTR -// -// Furthermore, the following one-way conversions are supported: -// VT_UI4 -> NPVariantType_Int32 -// VT_DISPATCH -> NPVariantType_Object (DispatchHost) -// NPVariantType_Object -> VT_DISPATCH (NpFunctionHost) - -#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_VARIANT_UTILS_H_ -#define OMAHA_PLUGINS_UPDATE_NPAPI_VARIANT_UTILS_H_ - -#include -#include "third_party/npapi/bindings/nphostapi.h" - -namespace omaha { - -void NPVariantToVariant(NPP npp, - const NPVariant& source, - VARIANT* destination); - -void VariantToNPVariant(NPP npp, - const VARIANT& source, - NPVariant* destination); - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_NPAPI_VARIANT_UTILS_H_ diff --git a/omaha/plugins/update/npapi/variant_utils_unittest.cc b/omaha/plugins/update/npapi/variant_utils_unittest.cc deleted file mode 100644 index b26737a47..000000000 --- a/omaha/plugins/update/npapi/variant_utils_unittest.cc +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/npapi/variant_utils.h" -#include -#include -#include -#include -#include -#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h" -#include "omaha/plugins/update/npapi/testing/stubs.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -class VariantUtilsTest : public testing::Test { - protected: - virtual void SetUp() { - VOID_TO_NPVARIANT(np_variant_); - } - - virtual void TearDown() { - NPN_ReleaseVariantValue(&np_variant_); - } - - void TestNPV2V() { - NPVariantToVariant(NULL, np_variant_, &variant_); - ExpectTypesEquivalent(); - } - - void TestV2NPV() { - NPN_ReleaseVariantValue(&np_variant_); - VariantToNPVariant(NULL, variant_, &np_variant_); - ExpectTypesEquivalent(); - } - - void ExpectTypesEquivalent() { - switch (V_VT(&variant_)) { - case VT_EMPTY: - EXPECT_TRUE(NPVARIANT_IS_VOID(np_variant_)); - return; - break; - case VT_NULL: - EXPECT_TRUE(NPVARIANT_IS_NULL(np_variant_)); - return; - break; - case VT_BOOL: - EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(np_variant_)); - return; - break; - case VT_I4: - case VT_UI4: - EXPECT_TRUE(NPVARIANT_IS_INT32(np_variant_)); - return; - break; - case VT_R8: - EXPECT_TRUE(NPVARIANT_IS_DOUBLE(np_variant_)); - return; - break; - case VT_BSTR: - EXPECT_TRUE(NPVARIANT_IS_STRING(np_variant_)); - return; - break; - case VT_DISPATCH: - EXPECT_TRUE(NPVARIANT_IS_OBJECT(np_variant_)); - return; - break; - default: - break; - } - ASSERT(false, (L"Expected equivalent types but got the following instead:\n" - L" np_variant_.type -> %d\n V_VT(&variant_) -> %d\n", - np_variant_.type, V_VT(&variant_))); - } - - NPVariant np_variant_; - CComVariant variant_; -}; - -TEST_F(VariantUtilsTest, NPVariantToVariant_Void) { - VOID_TO_NPVARIANT(np_variant_); - TestNPV2V(); -} - -TEST_F(VariantUtilsTest, NPVariantToVariant_Null) { - NULL_TO_NPVARIANT(np_variant_); - TestNPV2V(); -} - -TEST_F(VariantUtilsTest, NPVariantToVariant_Bool) { - BOOLEAN_TO_NPVARIANT(true, np_variant_); - TestNPV2V(); - EXPECT_EQ(VARIANT_TRUE, V_BOOL(&variant_)); - - BOOLEAN_TO_NPVARIANT(false, np_variant_); - TestNPV2V(); - EXPECT_EQ(VARIANT_FALSE, V_BOOL(&variant_)); -} - -TEST_F(VariantUtilsTest, NPVariantToVariant_Int32) { - INT32_TO_NPVARIANT(std::numeric_limits::min(), np_variant_); - TestNPV2V(); - EXPECT_EQ(std::numeric_limits::min(), V_I4(&variant_)); - - INT32_TO_NPVARIANT(0, np_variant_); - TestNPV2V(); - EXPECT_EQ(0, V_I4(&variant_)); - - INT32_TO_NPVARIANT(std::numeric_limits::max(), np_variant_); - TestNPV2V(); - EXPECT_EQ(std::numeric_limits::max(), V_I4(&variant_)); -} - -TEST_F(VariantUtilsTest, NPVariantToVariant_Double) { - DOUBLE_TO_NPVARIANT(-1, np_variant_); - TestNPV2V(); - EXPECT_DOUBLE_EQ(-1, V_R8(&variant_)); - - DOUBLE_TO_NPVARIANT(0, np_variant_); - TestNPV2V(); - EXPECT_DOUBLE_EQ(0, V_R8(&variant_)); - - DOUBLE_TO_NPVARIANT(static_cast(std::numeric_limits::max()), - np_variant_); - TestNPV2V(); - EXPECT_DOUBLE_EQ(static_cast(std::numeric_limits::max()), - V_R8(&variant_)); -} - -TEST_F(VariantUtilsTest, NPVariantToVariant_String) { - #pragma warning(push) - // conversion from 'size_t' to 'uint32_t', possible loss of data. - #pragma warning(disable : 4267) - - // TODO(omaha): _strdup depends on an implementation detail of the stubs. - STRINGZ_TO_NPVARIANT(_strdup(""), np_variant_); - TestNPV2V(); - EXPECT_STREQ(L"", V_BSTR(&variant_)); - - // Force the length to be zero. - STRINGZ_TO_NPVARIANT(_strdup("junk"), np_variant_); - np_variant_.value.stringValue.UTF8Length = 0; - TestNPV2V(); - EXPECT_STREQ(L"", V_BSTR(&variant_)); - - STRINGZ_TO_NPVARIANT(_strdup("ROBERT'); DROP TABLE Students; --"), - np_variant_); - TestNPV2V(); - EXPECT_STREQ(L"ROBERT'); DROP TABLE Students; --", V_BSTR(&variant_)); - - // Check that NPVariantToVariant properly converts UTF-8 to UTF-16. - STRINGZ_TO_NPVARIANT(_strdup("one: \xe4\xb8\x80"), np_variant_); - TestNPV2V(); - EXPECT_STREQ(L"one: \x4e00", V_BSTR(&variant_)); - - #pragma warning(pop) -} -/* -TEST_F(VariantUtilsTest, NPVariantToVariant_Unsupported) { - // NPVariantType_Object -> VT_DISPATCH conversion is not supported. - ExpectAsserts expect_asserts; - OBJECT_TO_NPVARIANT(NULL, np_variant_); - variant_ = 24; - NPVariantToVariant(NULL, np_variant_, &variant_); - EXPECT_EQ(VT_EMPTY, V_VT(&variant_)); - // Manual cleanup, since OBJECT_TO_NPVARIANT macro was used with a NULL - // NPObject, which is normally illegal. - VOID_TO_NPVARIANT(np_variant_); -} -*/ -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_EMPTY) { - variant_.ChangeType(VT_EMPTY); - TestV2NPV(); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_NULL) { - variant_.ChangeType(VT_NULL); - TestV2NPV(); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_BOOL) { - variant_ = true; - TestV2NPV(); - EXPECT_TRUE(np_variant_.value.boolValue); - - variant_ = false; - TestV2NPV(); - EXPECT_FALSE(np_variant_.value.boolValue); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_I4) { - variant_ = std::numeric_limits::max(); - TestV2NPV(); - EXPECT_EQ(std::numeric_limits::max(), np_variant_.value.intValue); - - variant_ = 0; - TestV2NPV(); - EXPECT_EQ(0, np_variant_.value.intValue); - - variant_ = std::numeric_limits::min(); - TestV2NPV(); - EXPECT_EQ(std::numeric_limits::min(), np_variant_.value.intValue); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_UI4) { - variant_ = 0U; - TestV2NPV(); - EXPECT_EQ(0, np_variant_.value.intValue); - - variant_ = static_cast(std::numeric_limits::max()); - TestV2NPV(); - EXPECT_EQ(std::numeric_limits::max(), np_variant_.value.intValue); - - // MSIE can natively support VT_UI4. Unfortunately, Firefox cannot. - // Check that kuint32max wraps around to -1. - variant_ = std::numeric_limits::max(); - TestV2NPV(); - EXPECT_EQ(-1, np_variant_.value.intValue); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_R8) { - variant_ = 0.0; - TestV2NPV(); - EXPECT_DOUBLE_EQ(0.0, np_variant_.value.doubleValue); - - variant_ = -1.0; - TestV2NPV(); - EXPECT_DOUBLE_EQ(-1.0, np_variant_.value.doubleValue); - - variant_ = static_cast(std::numeric_limits::max()); - TestV2NPV(); - EXPECT_DOUBLE_EQ(static_cast(std::numeric_limits::max()), - np_variant_.value.doubleValue); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_BSTR) { - variant_ = ""; - TestV2NPV(); - EXPECT_STREQ("", np_variant_.value.stringValue.UTF8Characters); - - variant_ = L"sudo make me a sandwich"; - TestV2NPV(); - EXPECT_STREQ("sudo make me a sandwich", - np_variant_.value.stringValue.UTF8Characters); - - // A NULL BSTR should be treated as an empty string. - V_VT(&variant_) = VT_BSTR; - V_BSTR(&variant_) = NULL; - TestV2NPV(); - EXPECT_STREQ("", np_variant_.value.stringValue.UTF8Characters); - - // Check that VariantToNPVariant properly converts UTF-16 to UTF-8. - variant_ = L"one: \x4e00"; - TestV2NPV(); - EXPECT_STREQ("one: \xe4\xb8\x80", - np_variant_.value.stringValue.UTF8Characters); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_VT_DISPATCH) { - CComPtr dispatch; - ASSERT_SUCCEEDED( - CComCoClass::CreateInstance(&dispatch)); - variant_ = dispatch; - TestV2NPV(); - - // Check that the wrapped object's methods can be called. - NPIdentifierFactory identifier; - NPVariant result; - VOID_TO_NPVARIANT(result); - EXPECT_TRUE(NPN_Invoke(NULL, np_variant_.value.objectValue, - identifier.Create("Random"), NULL, 0, &result)); - EXPECT_TRUE(NPVARIANT_IS_INT32(result)); - EXPECT_EQ(42, result.value.intValue); -} - -TEST_F(VariantUtilsTest, VariantToNPVariant_Unsupported) { - // Legal variant types inferred from oaidl.idl and wtypes.h. Note that some - // types that aren't marked as appearing in VARIANTs still have a VARIANT - // field for that type, so they are included anyway... - - // Note that VT_UNKNOWN must not be the last element in the array; otherwise - // CComVariant will attempt to call Release() on a NULL pointer. - const VARTYPE kUnsupportedSimpleTypes[] = { - VT_I2, VT_R4, VT_CY, VT_DATE, VT_ERROR, VT_VARIANT, VT_UNKNOWN, VT_DECIMAL, - VT_RECORD, VT_I1, VT_UI1, VT_UI2, VT_I8, VT_UI8, VT_INT, VT_UINT, VT_BYREF - }; - for (int i = 0; i < arraysize(kUnsupportedSimpleTypes); ++i) { - ExpectAsserts expect_asserts; - V_VT(&variant_) = kUnsupportedSimpleTypes[i]; - INT32_TO_NPVARIANT(42, np_variant_); - TestV2NPV(); - EXPECT_TRUE(NPVARIANT_IS_VOID(np_variant_)); - } - - // Compound modifiers. - const VARTYPE kCompoundModifiers[] = { - VT_ARRAY, VT_BYREF, VT_ARRAY | VT_BYREF - }; - // Compound types. - const VARTYPE kCompoundableTypes[] = { - VT_I2, VT_I4, VT_R4, VT_R8, VT_CY, VT_DATE, VT_BSTR, VT_DISPATCH, VT_ERROR, - VT_BOOL, VT_VARIANT, VT_UNKNOWN, VT_DECIMAL, VT_RECORD, VT_I1, VT_UI1, - VT_UI2, VT_UI4, VT_I8, VT_UI8, VT_INT, VT_UINT - }; - - for (int i = 0; i < arraysize(kCompoundModifiers); ++i) { - for (int j = 0; j < arraysize(kCompoundableTypes); ++j) { - ExpectAsserts expect_asserts; - V_VT(&variant_) = kCompoundModifiers[i] | kCompoundableTypes[j]; - INT32_TO_NPVARIANT(42, np_variant_); - TestV2NPV(); - EXPECT_TRUE(NPVARIANT_IS_VOID(np_variant_)); - } - } -} - -} // namespace omaha diff --git a/omaha/plugins/update/omaha_customization_update_apis_unittest.cc b/omaha/plugins/update/omaha_customization_update_apis_unittest.cc deleted file mode 100644 index a06acd7f7..000000000 --- a/omaha/plugins/update/omaha_customization_update_apis_unittest.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Tests the constants that vary depending on the customization of Omaha. -// The test checks for the Google Update variations, but can be modified for -// your purposes. - -#include -#include -#include -#include -#include "omaha/base/utils.h" -#include "plugins/update/activex/update_control_idl.h" -#include "omaha/testing/omaha_customization_test.h" - -// Most of the tests are intentionally not using the omaha namespace. Most of -// the values being tested are not in this namespace, and being in the global -// namespace is required by TEST_GU_INT_F to catch conflicts with Google types -// when building non-Google versions. - -class OmahaCustomizationUpdateComInterfaceTest - : public OmahaCustomizationTypeLibComInterfaceTest { - protected: - OmahaCustomizationUpdateComInterfaceTest() - : OmahaCustomizationTypeLibComInterfaceTest(UPDATE_PLUGIN_FILENAME) { - } -}; - -TEST_F(OmahaCustomizationUpdateComInterfaceTest, TypeLib) { - EXPECT_GU_ID_EQ(_T("{b627c883-e979-4873-80b3-ddd0b658b56a}"), - LIBID_GoogleUpdateControlLib); - - EXPECT_SUCCEEDED(GetTypeLibDocumentation()); - EXPECT_STREQ(_T("GoogleUpdateControlLib"), item_name_); - EXPECT_GU_STREQ(_T("Google Update Browser Plugins 3.0 Type Library"), - item_doc_string_); - EXPECT_EQ(0, help_context_); - EXPECT_TRUE(!help_file_); -} - -TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest, - IGoogleUpdateOneClick) { - // TODO(omaha): Test uuid constants after extracting from IDLs. - EXPECT_GU_ID_EQ(_T("{6F65D62B-2F32-4483-9028-176C30B2389D}"), - __uuidof(IGoogleUpdateOneClick)); - - EXPECT_SUCCEEDED(GetDocumentation(_T("IGoogleUpdateOneClick"))); - EXPECT_STREQ(_T("Google Update OneClick Control"), item_doc_string_); - EXPECT_EQ(0, help_context_); - EXPECT_TRUE(!help_file_); -} - -TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest, - IGoogleUpdate3WebControl) { - // TODO(omaha): Test uuid constants after extracting from IDLs. - EXPECT_GU_ID_EQ(_T("{57E37502-65A5-484a-A035-C1608B2626EA}"), - __uuidof(IGoogleUpdate3WebControl)); - - EXPECT_SUCCEEDED(GetDocumentation(_T("IGoogleUpdate3WebControl"))); - EXPECT_STREQ(_T("GoogleUpdate3Web Control"), item_doc_string_); - EXPECT_EQ(0, help_context_); - EXPECT_TRUE(!help_file_); -} - -TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest, - GoogleUpdateOneClickControlCoClass) { - EXPECT_GU_ID_EQ(_T("{c442ac41-9200-4770-8cc0-7cdb4f245c55}"), - __uuidof(GoogleUpdateOneClickControlCoClass)); - - EXPECT_SUCCEEDED(GetDocumentation(_T("GoogleUpdateOneClickControlCoClass"))); - EXPECT_STREQ(_T("Google Update OneClick Control Class"), item_doc_string_); - EXPECT_EQ(0, help_context_); - EXPECT_TRUE(!help_file_); -} - -TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest, - GoogleUpdate3WebControlCoClass) { - EXPECT_GU_ID_EQ(_T("{c3101a8b-0ee1-4612-bfe9-41ffc1a3c19d}"), - __uuidof(GoogleUpdate3WebControlCoClass)); - - EXPECT_SUCCEEDED(GetDocumentation(_T("GoogleUpdate3WebControlCoClass"))); - EXPECT_STREQ(_T("GoogleUpdate3Web Control Class"), item_doc_string_); - EXPECT_EQ(0, help_context_); - EXPECT_TRUE(!help_file_); -} - -// Verifies there are no new interfaces in the TypeLib. -TEST_F(OmahaCustomizationUpdateComInterfaceTest, VerifyNoNewInterfaces) { - EXPECT_EQ(4, type_lib_->GetTypeInfoCount()) - << _T("A new interface may have been added. If so, roll ") - << _T("the plugin version and add test(s) for new interface(s)."); -} diff --git a/omaha/plugins/update/omaha_customization_update_unittest.cc b/omaha/plugins/update/omaha_customization_update_unittest.cc deleted file mode 100644 index fe7fd0146..000000000 --- a/omaha/plugins/update/omaha_customization_update_unittest.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Tests the constants that vary depending on the customization of Omaha. -// The test checks for the Google Update variations, but can be modified for -// your purposes. - -#include "omaha/plugins/update/config.h" -#include "omaha/testing/omaha_customization_test.h" - -namespace omaha { - -TEST(OmahaCustomizationUpdateTest, Constants_BuildFiles) { -// The plugin version may or may not match in non-Google Update builds. -#ifdef GOOGLE_UPDATE_BUILD - EXPECT_STREQ(_T("3"), kUpdate3WebPluginVersion); - EXPECT_STREQ(_T("9"), kOneclickPluginVersion); -#else - std::wcout << _T("Did not test kPluginVersions.") << std::endl; -#endif - - EXPECT_GU_STREQ( - _T("Google.Update3WebControl.") _T(UPDATE_PLUGIN_VERSION_ANSI), - kUpdate3WebControlProgId); - EXPECT_GU_STREQ( - _T("Google.OneClickCtrl.") _T(ONECLICK_PLUGIN_VERSION_ANSI), - kOneclickControlProgId); -} - -} // namespace omaha diff --git a/omaha/plugins/update/oneclick.rgs b/omaha/plugins/update/oneclick.rgs deleted file mode 100644 index 550117f58..000000000 --- a/omaha/plugins/update/oneclick.rgs +++ /dev/null @@ -1,101 +0,0 @@ -'%HKROOT%' -{ - NoRemove SOFTWARE - { - NoRemove MozillaPlugins - { - ForceRemove '@%PLUGINDOMAIN%/%PLUGINPRODUCT%;version=%PLUGINVERSION%' - { - val Path = s '%MODULE%' - val Description = s '%PLUGINDESCRIPTION%' - val ProductName = s '%PLUGINPRODUCT%' - val Vendor = s '%PLUGINVENDOR%' - val Version = s '%PLUGINVERSION%' - MimeTypes - { - '%PLUGINMIMETYPE%' - } - } - } - NoRemove Microsoft - { - NoRemove Windows - { - NoRemove CurrentVersion - { - NoRemove Ext - { - NoRemove PreApproved - { - ForceRemove '%CLSID%' - } - NoRemove Stats - { - ForceRemove '%CLSID%' - { - iexplore - { - AllowedDomains - { - '*' - } - } - } - } - } - } - } - NoRemove 'Internet Explorer' - { - NoRemove 'Low Rights' - { - NoRemove 'ElevationPolicy' - { - ForceRemove '%CLSID%' - { - val AppName = s '%SHELLNAME%' - val AppPath = s '%SHELLPATH%' - val Policy = d '3' - } - } - } - } - } - NoRemove Classes - { - ForceRemove '%PROGID%' = s '%PLUGINPRODUCT% Plugin' - { - CLSID = s '%CLSID%' - } - NoRemove CLSID - { - ForceRemove '%CLSID%' = s '%PLUGINPRODUCT% Plugin' - { - ProgID = s '%PROGID%' - InprocServer32 = s '%MODULE%' - { - val ThreadingModel = s 'Apartment' - } - 'Implemented Categories' - { - '{59FB2056-D625-48D0-A944-1A85B5AB2640}' = s 'CATID_AppContainerCompatible' - } - } - } - NoRemove MIME - { - NoRemove Database - { - NoRemove 'Content Type' - { - ForceRemove '%PLUGINMIMETYPE%' - { - val CLSID = s '%CLSID%' - } - } - } - } - } - } -} - diff --git a/omaha/plugins/update/resource.h b/omaha/plugins/update/resource.h deleted file mode 100644 index 583ea9df5..000000000 --- a/omaha/plugins/update/resource.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_RESOURCE_H_ -#define OMAHA_PLUGINS_UPDATE_RESOURCE_H_ - -#define IDR_ONECLICK_RGS 102 - -#endif // OMAHA_PLUGINS_UPDATE_RESOURCE_H_ diff --git a/omaha/plugins/update/resource.rc b/omaha/plugins/update/resource.rc deleted file mode 100644 index 6c4f72f13..000000000 --- a/omaha/plugins/update/resource.rc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -// These include headers with absolute paths, which Resource Editor cannot load. -#ifndef APSTUDIO_INVOKED -#include "omaha/base/const_config.h" -#include "omaha/plugins/update/config.h" -#include "omaha/plugins/update/resource.h" -#endif // APSTUDIO_INVOKED - -#define PLUGIN_FILENAME UPDATE_PLUGIN_FILENAME -#include "../plugin_version.rc" - -// Path is in the obj directory, which Resource Editor does not look in. -#ifndef APSTUDIO_INVOKED -1 TYPELIB "plugins/update/activex/update_control_idl.tlb" -#endif // APSTUDIO_INVOKED - -IDR_ONECLICK_RGS REGISTRY "oneclick.rgs" - diff --git a/omaha/plugins/update/site_lock.cc b/omaha/plugins/update/site_lock.cc deleted file mode 100644 index 2ec0187ec..000000000 --- a/omaha/plugins/update/site_lock.cc +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/site_lock.h" - -#include -#include -#include -#include - -#include "omaha/base/atl_regexp.h" -#include "omaha/base/constants.h" -#include "omaha/base/error.h" -#include "omaha/base/safe_format.h" -#include "omaha/base/reg_key.h" -#include "omaha/plugins/update/npapi/urlpropbag.h" - -namespace omaha { - -SiteLock::SiteLock() { - for (int i = 0; i < arraysize(kSiteLockPatternStrings); ++i) { - VERIFY1(AddPattern(kSiteLockPatternStrings[i])); - } - - // TODO(omaha): should this be wrapped in a #ifdef DEBUG? - CString dev_pattern_string; - if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV, - kRegValueOneClickHostPattern, - &dev_pattern_string)) && - !dev_pattern_string.IsEmpty()) { - VERIFY1(AddPattern(dev_pattern_string)); - } -} - -SiteLock::~SiteLock() { - for (size_t i = 0; i < patterns_.size(); ++i) { - delete patterns_[i]; - } -} - -bool SiteLock::InApprovedDomain(IObjectWithSite* plugin) { - CString url; - if (FAILED(GetCurrentBrowserUrl(plugin, &url))) { - return false; - } - return InApprovedDomain(url); -} - -bool SiteLock::InApprovedDomain(const WCHAR* url) { - // TODO(omaha): investigate using CUrl to remove dependency on wininet - URL_COMPONENTS components = {sizeof(components)}; - components.dwHostNameLength = 1; - if (!::InternetCrackUrl(url, 0, 0, &components)) { - return false; - } - // On some platforms, InternetCrackUrl() is unreliable and will return - // success but leave lpszHostName NULL. Make sure it's valid. (b/5532393) - if (!components.lpszHostName || components.dwHostNameLength == 0) { - return false; - } - CString hostname(components.lpszHostName, components.dwHostNameLength); - for (std::vector::const_iterator it = patterns_.begin(); - it != patterns_.end(); - ++it) { - AtlMatchContext context; - if ((*it)->Match(hostname, &context)) { - return true; - } - } - - return false; -} - -HRESULT SiteLock::GetCurrentBrowserUrl(IObjectWithSite* plugin, CString* url) { - if (SUCCEEDED(ExtractUrlFromBrowser(plugin, url))) { - return S_OK; - } - if (SUCCEEDED(ExtractUrlFromPropBag(plugin, url))) { - return S_OK; - } - return E_FAIL; -} - -// TODO(omaha): Move this to common\webplugin_utils. -HRESULT SiteLock::GetUrlDomain(const CString& url, CString* url_domain) { - ASSERT1(url_domain); - url_domain->Empty(); - - URL_COMPONENTS urlComponents = {0}; - urlComponents.dwStructSize = sizeof(urlComponents); - urlComponents.dwSchemeLength = 1; - urlComponents.dwHostNameLength = 1; - if (!::InternetCrackUrl(url, 0, 0, &urlComponents)) { - HRESULT hr = HRESULTFromLastError(); - CORE_LOG(L2, (_T("[InternetCrackUrl failed][0x%08x]"), hr)); - return hr; - } - - CString scheme(urlComponents.lpszScheme, urlComponents.dwSchemeLength); - CString host_name(urlComponents.lpszHostName, urlComponents.dwHostNameLength); - ASSERT1(!scheme.IsEmpty()); - ASSERT1(!host_name.IsEmpty()); - - SafeCStringFormat(url_domain, _T("%s://%s/"), scheme, host_name); - return S_OK; -} - -bool SiteLock::AddPattern(const WCHAR* pattern) { - ASSERT1(pattern); - - // An empty pattern will match everything... - if (!*pattern) { - ASSERT1(false); - return false; - } - - auto re = std::make_unique(); - REParseError error = re->Parse(pattern); - if (REPARSE_ERROR_OK != error) { - ASSERT(false, (L"Failed to parse site lock pattern: %s", - pattern)); - return false; - } - patterns_.push_back(re.release()); - return true; -} - -// If the plugin is being hosted inside an NPAPI environment, NPUpdate will set -// a UrlPropertyBag object as our object site. Fetch the URL used to create -// our object from it. -HRESULT SiteLock::ExtractUrlFromPropBag(IObjectWithSite* plugin, CString* url) { - ASSERT1(plugin); - ASSERT1(url); - - CComPtr property_bag; - HRESULT hr = plugin->GetSite(IID_PPV_ARGS(&property_bag)); - if (FAILED(hr)) { - return hr; - } - - CComVariant var; - hr = property_bag->Read(kUrlPropertyBag_Url, &var, NULL); - if (FAILED(hr)) { - return hr; - } - if (var.vt != VT_BSTR || !var.bstrVal) { - return E_UNEXPECTED; - } - *url = var.bstrVal; - return S_OK; -} - -// If the plugin is hosted in an ActiveX environment, IE will set itself as the -// object site. Fetch the current URL from it. -HRESULT SiteLock::ExtractUrlFromBrowser(IObjectWithSite* plugin, CString* url) { - ASSERT1(plugin); - ASSERT1(url); - - CComPtr service_provider; - HRESULT hr = plugin->GetSite(IID_PPV_ARGS(&service_provider)); - if (FAILED(hr)) { - return hr; - } - - CComPtr web_browser; - hr = service_provider->QueryService(SID_SWebBrowserApp, - IID_PPV_ARGS(&web_browser)); - - CComBSTR bstr_url; - if (SUCCEEDED(hr)) { - hr = web_browser->get_LocationURL(&bstr_url); - } else { - // Do things the hard way... - CComPtr client_site; - hr = plugin->GetSite(IID_PPV_ARGS(&client_site)); - if (FAILED(hr)) { - return hr; - } - - CComPtr container; - hr = client_site->GetContainer(&container); - if (FAILED(hr)) { - return hr; - } - - CComPtr html_document; - hr = container.QueryInterface(&html_document); - if (FAILED(hr)) { - return hr; - } - - hr = html_document->get_URL(&bstr_url); - } - - if (SUCCEEDED(hr)) { - *url = bstr_url; - } - - return hr; -} - -} // namespace omaha diff --git a/omaha/plugins/update/site_lock.h b/omaha/plugins/update/site_lock.h deleted file mode 100644 index efaa84775..000000000 --- a/omaha/plugins/update/site_lock.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_PLUGINS_UPDATE_SITE_LOCK_H_ -#define OMAHA_PLUGINS_UPDATE_SITE_LOCK_H_ - -#include -#include -#include - -#include "base/basictypes.h" -#include "omaha/base/atl_regexp.h" - -namespace omaha { - -class SiteLock { - public: - SiteLock(); - ~SiteLock(); - - bool InApprovedDomain(IObjectWithSite* url_provider); - bool InApprovedDomain(const WCHAR* url); - - static HRESULT GetCurrentBrowserUrl(IObjectWithSite* plugin, CString* url); - static HRESULT GetUrlDomain(const CString& url, CString* url_domain); - - private: - bool AddPattern(const WCHAR* pattern); - - static HRESULT ExtractUrlFromBrowser(IObjectWithSite* plugin, CString* url); - static HRESULT ExtractUrlFromPropBag(IObjectWithSite* plugin, CString* url); - - std::vector patterns_; - - DISALLOW_COPY_AND_ASSIGN(SiteLock); -}; - -} // namespace omaha - -#endif // OMAHA_PLUGINS_UPDATE_SITE_LOCK_H_ diff --git a/omaha/plugins/update/site_lock_unittest.cc b/omaha/plugins/update/site_lock_unittest.cc deleted file mode 100644 index f9c797ac7..000000000 --- a/omaha/plugins/update/site_lock_unittest.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/plugins/update/site_lock.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -class SiteLockTest : public testing::Test { - protected: - SiteLock site_lock_; -}; - -TEST_F(SiteLockTest, InApprovedDomain_GoogleDotCom) { - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.com/")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.com/pack/")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.co.uk")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.co.uk/pack/")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://pack.google.com/")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://pack.google.com/pack/")); - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://fake.google.com/")); - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://fake.google.com/pack/")); - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://google.com/")); - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://google.com/pack/")); -} - -TEST_F(SiteLockTest, InApprovedDomain_ChromeDotCom) { - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.chrome.com/")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.chrome.com/dl/")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://chrome.com")); - EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://chrome.com/dl/")); - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://fake.chrome.com/")); -} - -TEST_F(SiteLockTest, InApprovedDomain_EvilHackerDotCom) { - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://www.evilhacker.com/")); - EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://www.evilhacker.com/dl/")); -} - -} // namespace omaha diff --git a/omaha/recovery/build.scons b/omaha/recovery/build.scons index aa33be6b6..a7d00e892 100644 --- a/omaha/recovery/build.scons +++ b/omaha/recovery/build.scons @@ -68,6 +68,7 @@ test_env.Append( '$LIB_DIR/common.lib', '$LIB_DIR/crx_file.lib', '$LIB_DIR/google_update_recovery.lib', + '$LIB_DIR/goopdate_lib.lib', '$LIB_DIR/libprotobuf.lib', '$LIB_DIR/security.lib', test_env['atls_libs'][test_env.Bit('debug')], @@ -100,5 +101,3 @@ test_env.ComponentTestProgram(target_name, test_inputs, COMPONENT_TEST_DISABLED=True) - -env.BuildSConscript('repair_exe') diff --git a/omaha/recovery/client/google_update_recovery.cc b/omaha/recovery/client/google_update_recovery.cc index 3e59463f2..c9de12c05 100644 --- a/omaha/recovery/client/google_update_recovery.cc +++ b/omaha/recovery/client/google_update_recovery.cc @@ -52,10 +52,9 @@ const TCHAR* const kQueryStringFormat = // Information about where to obtain Omaha info. // This must never change in Omaha. const TCHAR* const kRegValueProductVersion = _T("pv"); -const TCHAR* const kRelativeGoopdateRegPath = _T("Software\\Google\\Update\\"); const TCHAR* const kRelativeClientsGoopdateRegPath = - _T("Software\\Google\\Update\\Clients\\") - _T("{430FD4D0-B729-4F61-AA34-91526481799D}"); + _T("Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\") + GOOPDATE_APP_ID; // The UpdateDev registry value to override the Code Red url. const TCHAR* const kRegValueNameCodeRedUrl = _T("CodeRedUrl"); @@ -226,7 +225,7 @@ HRESULT ResetRecoveryDir(CPath* recovery_dir) { *recovery_dir += kRecoveryDirectory; if (recovery_dir->IsDirectory()) { - VERIFY1(SUCCEEDED(DeleteDirectoryContents(*recovery_dir))); + VERIFY_SUCCEEDED(DeleteDirectoryContents(*recovery_dir)); return S_OK; } @@ -436,7 +435,7 @@ HRESULT GetDownloadTargetPath(CPath* download_target_path, } *download_target_path = *parent_dir; - *download_target_path += _T("GoogleUpdateSetup.crx3"); + *download_target_path += MAIN_EXE_BASE_NAME _T("Setup.crx3"); return S_OK; } @@ -456,7 +455,7 @@ HRESULT DownloadRepairFile(const CString& download_target_path, CString url; HRESULT hr = GetRegStringValue(true, - _T("SOFTWARE\\Google\\UpdateDev"), + _T("SOFTWARE\\") PATH_COMPANY_NAME _T("\\UpdateDev"), kRegValueNameCodeRedUrl, &url); if (FAILED(hr)) { @@ -492,35 +491,6 @@ HRESULT RunRepairFile(const CString& file_path, bool is_machine_app) { return StartProcess(NULL, command_line.GetBuffer()); } -enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED}; -static volatile LONG g_domain_state = UNKNOWN; - -bool IsEnrolledToDomain() { - DWORD is_enrolled(false); - if (SUCCEEDED(GetRegDwordValue(true, - REG_UPDATE_DEV, - kRegValueIsEnrolledToDomain, - &is_enrolled))) { - return !!is_enrolled; - } - - if (g_domain_state == UNKNOWN) { - LPWSTR domain; - NETSETUP_JOIN_STATUS join_status; - if (::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) { - return false; - } - - ::NetApiBufferFree(domain); - ::InterlockedCompareExchange(&g_domain_state, - join_status == ::NetSetupDomainName ? - ENROLLED : NOT_ENROLLED, - UNKNOWN); - } - - return g_domain_state == ENROLLED; -} - } // namespace // Verifies the integrity of the file, the file certificate, and the timestamp @@ -618,7 +588,7 @@ HRESULT ValidateAndUnpackCRX(const CPath& from_crx_path, } CPath exe = unpack_under_path; - exe += _T("GoogleUpdateSetup.exe"); + exe += MAIN_EXE_BASE_NAME _T("Setup.exe"); if (!exe.FileExists()) { return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } @@ -643,19 +613,6 @@ HRESULT FixGoogleUpdate(const TCHAR* app_guid, return E_INVALIDARG; } - DWORD update_check_period_override_minutes(UINT_MAX); - - if (omaha::IsEnrolledToDomain()) { - HRESULT hr = omaha::GetRegDwordValue( - true, - GOOPDATE_POLICIES_RELATIVE, - omaha::kRegValueAutoUpdateCheckPeriodOverrideMinutes, - &update_check_period_override_minutes); - if (SUCCEEDED(hr) && (0 == update_check_period_override_minutes)) { - return HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY); - } - } - CPath download_target_path; CPath parent_dir; HRESULT hr = omaha::GetDownloadTargetPath(&download_target_path, diff --git a/omaha/recovery/client/google_update_recovery_unittest.cc b/omaha/recovery/client/google_update_recovery_unittest.cc index a349864a6..dffbc5cc7 100644 --- a/omaha/recovery/client/google_update_recovery_unittest.cc +++ b/omaha/recovery/client/google_update_recovery_unittest.cc @@ -60,24 +60,18 @@ namespace omaha { namespace { -const TCHAR kDummyAppGuid[] = _T("{8E472B0D-3E8B-43b1-B89A-E8506AAF1F16}"); -const TCHAR kDummyAppVersion[] = _T("3.4.5.6"); -const TCHAR kDummyAppLang[] = _T("en-us"); +const TCHAR kTestAppGuid[] = _T("{8E472B0D-3E8B-43b1-B89A-E8506AAF1F16}"); +const TCHAR kTestAppVersion[] = _T("3.4.5.6"); +const TCHAR kTestAppLang[] = _T("en-us"); -const TCHAR kTempDirectory[] = _T("C:\\WINDOWS\\Temp"); - -const TCHAR kFullMachineOmahaMainKeyPath[] = - _T("HKLM\\Software\\Google\\Update\\"); -const TCHAR kFullUserOmahaMainKeyPath[] = - _T("HKCU\\Software\\Google\\Update\\"); const TCHAR kFullMachineOmahaClientKeyPath[] = - _T("HKLM\\Software\\Google\\Update\\Clients\\") + _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\") _T("{430FD4D0-B729-4f61-AA34-91526481799D}"); const TCHAR kFullUserOmahaClientKeyPath[] = - _T("HKCU\\Software\\Google\\Update\\Clients\\") + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\Clients\\") _T("{430FD4D0-B729-4f61-AA34-91526481799D}"); -const HRESULT kDummyNoFileError = 0x80041234; +const HRESULT kTestNoFileError = 0x80041234; const TCHAR kArgumentSavingExecutableRelativePath[] = _T("unittest_support\\SaveArguments.exe"); @@ -93,11 +87,11 @@ const TCHAR* const kInvalidFileUrl = _T("http://www.google.com/robots.txt"); // These methods were copied from omaha/testing/omaha_unittest.cpp. const TCHAR kRegistryHiveOverrideRoot[] = - _T("HKCU\\Software\\Google\\Update\\UnitTest\\"); + _T("HKCU\\Software\\") PATH_COMPANY_NAME _T("\\Update\\UnitTest\\"); -const TCHAR kExpectedUrlForDummyAppAndNoOmahaValues[] = _T("https://clients2.google.com/service/check2?crx3=true&appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=0.0.0.0&userid=&osversion="); // NOLINT -const int kExpectedUrlForDummyAppAndNoOmahaValuesLength = - arraysize(kExpectedUrlForDummyAppAndNoOmahaValues) - 1; +const TCHAR kExpectedUrlForTestAppAndNoOmahaValues[] = _T("https://clients2.") COMPANY_DOMAIN _T("/service/check2?crx3=true&appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=0.0.0.0&userid=&osversion="); // NOLINT +const int kExpectedUrlForTestAppAndNoOmahaValuesLength = + arraysize(kExpectedUrlForTestAppAndNoOmahaValues) - 1; // Overrides the HKLM and HKCU registry hives so that accesses go to the // specified registry key instead. @@ -255,7 +249,7 @@ class GoogleUpdateRecoveryTest : public testing::Test { return S_OK; } - // Returns kDummyNoFileError, simulating no file to download. + // Returns kTestNoFileError, simulating no file to download. static HRESULT DownloadFileNoFile(const TCHAR* url, const TCHAR* file_path, void* context) { @@ -266,7 +260,7 @@ class GoogleUpdateRecoveryTest : public testing::Test { GoogleUpdateRecoveryTest::set_saved_file_path(file_path); GoogleUpdateRecoveryTest::set_saved_context(context); - return kDummyNoFileError; + return kTestNoFileError; } // Overrides the address to cause a file to be downloaded via HTTP. @@ -307,7 +301,6 @@ class GoogleUpdateRecoveryTest : public testing::Test { network_config->Clear(); network_config->Add(new UpdateDevProxyDetector); - network_config->Add(new FirefoxProxyDetector); network_config->Add(new IEWPADProxyDetector); network_config->Add(new IEPACProxyDetector); network_config->Add(new IENamedProxyDetector); @@ -326,7 +319,7 @@ class GoogleUpdateRecoveryTest : public testing::Test { if (HTTP_STATUS_OK == status_code) { return S_OK; } else if (HTTP_STATUS_NO_CONTENT == status_code) { - return kDummyNoFileError; + return kTestNoFileError; } else { // Apps would not have this assumption. ASSERT(false, (_T("Status code %i received. Expected 200 or 204."), @@ -399,9 +392,9 @@ class GoogleUpdateRecoveryRegistryProtectedTest TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_UseRealHttpClient) { EXPECT_EQ(CRYPT_E_NO_MATCH, - FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileInvalidFile, NULL)); @@ -409,9 +402,9 @@ TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_UseRealHttpClient) { TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_Machine) { CString context_string(_T("some context")); - EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadArgumentSavingCrx3, &context_string)); @@ -425,9 +418,9 @@ TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_Machine) { TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_User) { CString context_string(_T("more context")); - EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, false, DownloadArgumentSavingCrx3, &context_string)); @@ -440,9 +433,9 @@ TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_User) { } TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_Machine) { - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); @@ -452,9 +445,9 @@ TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_Machine) { } TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_User) { - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, false, DownloadFileNoFile, NULL)); @@ -469,16 +462,16 @@ INSTANTIATE_TEST_CASE_P(IsDomain, TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, FixGoogleUpdate_AllValues_MachineApp) { - const TCHAR kExpectedUrlFormat[] = _T("https://clients2.google.com/service/check2?crx3=true&appid=%%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=5.6.78.1&userid=%s&osversion="); // NOLINT + const TCHAR kExpectedUrlFormat[] = _T("https://clients2.") COMPANY_DOMAIN _T("/service/check2?crx3=true&appid=%%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=5.6.78.1&userid=%s&osversion="); // NOLINT EnableUsageStats(true); EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaClientKeyPath, _T("pv"), _T("5.6.78.1"))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); @@ -491,16 +484,16 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, FixGoogleUpdate_AllValues_UserApp) { - const TCHAR kExpectedUrlFormat[] = _T("https://clients2.google.com/service/check2?crx3=true&appid=%%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%%7D&appversion=3.4.5.6&applang=en-us&machine=0&version=5.6.78.1&userid=%s&osversion="); // NOLINT + const TCHAR kExpectedUrlFormat[] = _T("https://clients2.") COMPANY_DOMAIN _T("/service/check2?crx3=true&appid=%%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%%7D&appversion=3.4.5.6&applang=en-us&machine=0&version=5.6.78.1&userid=%s&osversion="); // NOLINT EnableUsageStats(true); EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullUserOmahaClientKeyPath, _T("pv"), _T("5.6.78.1"))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, false, DownloadFileNoFile, NULL)); @@ -513,22 +506,22 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, FixGoogleUpdate_NoOmahaRegKeys) { - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, FixGoogleUpdate_EmptyAppInfo) { - const TCHAR kExpectedUrl[] = _T("https://clients2.google.com/service/check2?crx3=true&appid=&appversion=&applang=&machine=1&version=0.0.0.0&userid=&osversion="); // NOLINT + const TCHAR kExpectedUrl[] = _T("https://clients2.") COMPANY_DOMAIN _T("/service/check2?crx3=true&appid=&appversion=&applang=&machine=1&version=0.0.0.0&userid=&osversion="); // NOLINT - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(_T(""), + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(_T(""), _T(""), _T(""), true, @@ -566,27 +559,6 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, NULL)); } -// Setting kRegValueAutoUpdateCheckPeriodOverrideMinutes to zero disables -// Code Red checks just as it does regular update checks. -TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, - FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDword) { - EXPECT_HRESULT_SUCCEEDED( - RegKey::SetValue(kRegKeyGoopdateGroupPolicy, - kRegValueAutoUpdateCheckPeriodOverrideMinutes, - static_cast(0))); - - EXPECT_EQ(IsDomain() ? - HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY) : - kDummyNoFileError, - FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, - true, - DownloadFileNoFile, - NULL)); - EXPECT_EQ(IsDomain(), saved_url_.IsEmpty()); -} - TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDwordInHkcu) { EXPECT_HRESULT_SUCCEEDED( @@ -594,14 +566,14 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, kRegValueAutoUpdateCheckPeriodOverrideMinutes, static_cast(0))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } @@ -612,14 +584,14 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, kRegValueAutoUpdateCheckPeriodOverrideMinutes, static_cast(1400))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } @@ -630,14 +602,14 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, kRegValueAutoUpdateCheckPeriodOverrideMinutes, static_cast(0))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } @@ -648,14 +620,14 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, kRegValueAutoUpdateCheckPeriodOverrideMinutes, static_cast(1400))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } @@ -666,14 +638,14 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, kRegValueAutoUpdateCheckPeriodOverrideMinutes, _T("0"))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } @@ -686,14 +658,14 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, &zero, sizeof(zero))); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } @@ -701,21 +673,21 @@ TEST_P(GoogleUpdateRecoveryRegistryProtectedTest, FixGoogleUpdate_GroupPolicyKeyExistsButNoAutoUpdateCheckPeriodMinutes) { EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(kRegKeyGoopdateGroupPolicy)); - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileNoFile, NULL)); - EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues, - saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength)); + EXPECT_STREQ(kExpectedUrlForTestAppAndNoOmahaValues, + saved_url_.Left(kExpectedUrlForTestAppAndNoOmahaValuesLength)); CheckSavedUrlOSFragment(); } TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileCollision) { - EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, false, DownloadArgumentSavingCrx3, NULL)); @@ -728,9 +700,9 @@ TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileCollision) { FileLock lock; EXPECT_HRESULT_SUCCEEDED(lock.Lock(first_saved_file_path)); - EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, false, DownloadArgumentSavingCrx3, NULL)); @@ -762,7 +734,7 @@ TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedValid) { TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_NotSigned) { const CString executable_full_path(MakeTestFilepath( - _T("GoogleUpdate_unsigned.exe"))); + MAIN_EXE_BASE_NAME _T("_unsigned.exe"))); EXPECT_TRUE(File::Exists(executable_full_path)); EXPECT_EQ(TRUST_E_NOSIGNATURE, VerifyFileSignature(executable_full_path)); } @@ -770,7 +742,7 @@ TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_NotSigned) { // The file is signed with an old cerificate not present in the pin list. TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_NotTrusted) { const CString executable_full_path(MakeTestFilepath( - _T("unittest_support\\GoogleUpdate_old_signature.exe"))); + _T("unittest_support\\") MAIN_EXE_BASE_NAME _T("_old_signature.exe"))); EXPECT_TRUE(File::Exists(executable_full_path)); EXPECT_EQ(GOOPDATE_E_SIGNATURE_NOT_TRUSTED_PIN, VerifyFileSignature(executable_full_path)); @@ -785,7 +757,7 @@ TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_UntrustedChain) { TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_HashFails) { const CString executable_full_path(MakeTestFilepath( - _T("unittest_support\\GoogleUpdate_corrupted.exe"))); + _T("unittest_support\\") MAIN_EXE_BASE_NAME _T("_corrupted.exe"))); EXPECT_TRUE(File::Exists(executable_full_path)); EXPECT_EQ(TRUST_E_BAD_DIGEST, VerifyFileSignature(executable_full_path)); } @@ -830,7 +802,7 @@ TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_InvalidMarkups) { EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND), VerifyRepairFileMarkup(MakeTestFilepath(kNoResourcesExecutable))); - const TCHAR kResourcesButNoMarkupExecutable[] = _T("GoogleUpdate.exe"); + const TCHAR kResourcesButNoMarkupExecutable[] = MAIN_EXE_BASE_NAME _T(".exe"); EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND), VerifyRepairFileMarkup(MakeTestFilepath( kResourcesButNoMarkupExecutable))); @@ -871,9 +843,9 @@ TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_BadFilenames) { // Production Server Response Tests Tests // TEST_F(GoogleUpdateRecoveryTest, ProductionServerResponseTest) { - EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid, - kDummyAppVersion, - kDummyAppLang, + EXPECT_EQ(kTestNoFileError, FixGoogleUpdate(kTestAppGuid, + kTestAppVersion, + kTestAppLang, true, DownloadFileFromServer, NULL)) << diff --git a/omaha/recovery/recovery_test.cc b/omaha/recovery/recovery_test.cc index 7c55f1b8e..bd001bc87 100644 --- a/omaha/recovery/recovery_test.cc +++ b/omaha/recovery/recovery_test.cc @@ -25,7 +25,6 @@ #include "omaha/recovery/client/google_update_recovery.h" using omaha::UpdateDevProxyDetector; -using omaha::FirefoxProxyDetector; using omaha::IEWPADProxyDetector; using omaha::IEPACProxyDetector; using omaha::IENamedProxyDetector; @@ -65,7 +64,6 @@ HRESULT DownloadFile(const TCHAR* url, const TCHAR* file_path, void*) { } network_config->Clear(); network_config->Add(new UpdateDevProxyDetector); - network_config->Add(new FirefoxProxyDetector); network_config->Add(new IEWPADProxyDetector); network_config->Add(new IEPACProxyDetector); network_config->Add(new IENamedProxyDetector); @@ -88,9 +86,9 @@ HRESULT DownloadFile(const TCHAR* url, const TCHAR* file_path, void*) { } // namespace int main() { - const TCHAR* const kDummyGuid = _T("{20F8FA32-8E16-4046-834B-88661E021AFC}"); + const TCHAR* const kTestGuid = _T("{20F8FA32-8E16-4046-834B-88661E021AFC}"); - HRESULT hr = FixGoogleUpdate(kDummyGuid, + HRESULT hr = FixGoogleUpdate(kTestGuid, _T("1.0.555.0"), _T("en-us"), true, diff --git a/omaha/recovery/repair_exe/build.scons b/omaha/recovery/repair_exe/build.scons deleted file mode 100644 index 75f065869..000000000 --- a/omaha/recovery/repair_exe/build.scons +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - - -Import('env') - - -local_env = env.Clone() - -inputs = [ - 'mspexecutableelevator.cc', - 'repair_goopdate.cc', - ] - -repair_goopdate_lib = local_env.ComponentLibrary('repair_goopdate', inputs) -subdirs = [ - 'custom_action', - 'msp', - ] - -for subdir in subdirs: - env.BuildSConscript(subdir) diff --git a/omaha/recovery/repair_exe/custom_action/build.scons b/omaha/recovery/repair_exe/custom_action/build.scons deleted file mode 100644 index 60738e88e..000000000 --- a/omaha/recovery/repair_exe/custom_action/build.scons +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2009-2010 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - - -Import('env') - - -# -# Build executecustomaction.dll -# - -lib_target_name = 'executecustomaction_lib' - -lib_env = env.Clone() - -lib_inputs = [ - 'execute_repair_file.cc', - ] - -lib_output = lib_env.ComponentLibrary( - lib_name=lib_target_name, - source=lib_inputs, -) - -dll_target_name = 'executecustomaction' - -dll_env = env.Clone( - # Build a dll, not a lib. - COMPONENT_STATIC = False, -) - -dll_env.Append( - LIBS = [ - dll_env['atls_libs'][dll_env.Bit('debug')], - dll_env['crt_libs'][dll_env.Bit('debug')], - '$LIB_DIR/base.lib', - '$LIB_DIR/common.lib', - '$LIB_DIR/crx_file.lib', - '$LIB_DIR/google_update_recovery.lib', - '$LIB_DIR/libprotobuf.lib', - '$LIB_DIR/repair_goopdate.lib', - '$LIB_DIR/security.lib', - 'crypt32.lib', - 'msi.lib', - '$LIB_DIR/net.lib', - 'wintrust.lib', - - # These are required by base.lib - 'netapi32.lib', - 'psapi.lib', - 'shlwapi.lib', - 'userenv.lib', - 'version.lib', - 'wtsapi32.lib', - lib_target_name + '.lib', - ], - - CPPDEFINES = [ - '_USRDLL', - ], -) - -dll_inputs = [ - 'executecustomaction.cc', - 'executecustomaction.def', - ] - -dll_env.ComponentLibrary( - lib_name=dll_target_name, - source=dll_inputs, -) - - -# -# Build testelevateusingmsp.exe -# -test_env = env.Clone() - -test_env['LIBS'] += [ - test_env['atls_libs'][test_env.Bit('debug')], - test_env['crt_libs'][test_env.Bit('debug')], - '$LIB_DIR/base.lib', - '$LIB_DIR/repair_goopdate.lib', - 'crypt32.lib', - 'msi.lib', - 'wintrust.lib', - # These are required by base.lib - 'netapi32.lib', - 'psapi.lib', - 'shlwapi.lib', - 'userenv.lib', - 'version.lib', - 'wtsapi32.lib', - ] - -# This is a console application. -test_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS,5.01']) -test_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE,5.01'] - -target_name = 'testelevateusingmsp' - -test_inputs = [ - 'testelevateusingmsp.cc', - ] - -# This test requires arguments, so do not create a run alias. -test_env.ComponentTestProgram( - prog_name=target_name, - source=test_inputs, - COMPONENT_TEST_RUNNABLE=False -) diff --git a/omaha/recovery/repair_exe/custom_action/execute_repair_file.cc b/omaha/recovery/repair_exe/custom_action/execute_repair_file.cc deleted file mode 100644 index f6d1af11b..000000000 --- a/omaha/recovery/repair_exe/custom_action/execute_repair_file.cc +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Verifies and executes the repair file for the MSP custom action. - -#include "omaha/recovery/repair_exe/custom_action/execute_repair_file.h" -#include -#include "omaha/base/debug.h" -#include "omaha/base/error.h" -#include "omaha/base/file.h" -#include "omaha/base/logging.h" -#include "omaha/base/path.h" -#include "omaha/base/string.h" -#include "omaha/base/system.h" -#include "omaha/base/utils.h" - -namespace omaha { - -namespace { - -// TODO(omaha): Add a parameter to specify the process working directory -HRESULT ExecuteFile(const CString& filename, const CString& args) { - HRESULT hr = System::ShellExecuteProcess(filename, args, NULL, NULL); - - if (FAILED(hr)) { - UTIL_LOG(LE, (_T("[ExecuteFile - failed to exec][%s][%s][0x%08x]"), - filename, args, hr)); - return hr; - } - - return S_OK; -} - -HRESULT GetDir(int csidl, const CString& path_tail, CString* dir) { - ASSERT1(dir); - - CString path; - HRESULT hr = GetFolderPath(csidl, &path); - if (FAILED(hr)) { - return hr; - } - if (!::PathAppend(CStrBuf(path, MAX_PATH), path_tail)) { - return E_FAIL; - } - dir->SetString(path); - - // Try to create the directory. Continue if the directory can't be created. - hr = CreateDir(path, NULL); - if (FAILED(hr)) { - UTIL_LOG(LW, (_T("[GetDir failed to create dir][%s][0x%08x]"), path, hr)); - } - return S_OK; -} - -// Creates machine wide goopdate install dir: "Program Files/Google/Update". -CString GetMachineGoopdateInstallDir() { - CString path; - VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES, - CString(OMAHA_REL_GOOPDATE_INSTALL_DIR), - &path))); - return path; -} - -// Copies the file to "%ProgramFiles%\Google\Update\". -// Assumes %ProgramFiles% is secure, which it should be unless permissions have -// been changed. -// Note: Cannot use the temp dir because MSI uses the logon user's environment -// and the user's temp dir is unlikely to be secure. -HRESULT CopyToSecureLocation(const CString& source, CString* new_location) { - if (source.IsEmpty() || !new_location) { - return E_INVALIDARG; - } - - CString filename = GetFileFromPath(source); - *new_location = GetMachineGoopdateInstallDir(); - if (!::PathAppend(CStrBuf(*new_location, MAX_PATH), filename)) { - return HRESULTFromLastError(); - } - - return File::Copy(source, *new_location, true); -} - -} // namespace - -HRESULT VerifyIsValidRepairFile(const CString& filename); - -// Assumes it is called elevated or with admin permissions. -// Copies the file to a secure location, before verifying and executing it. -// When the file is an Omaha metainstaller, it is important that the temp -// directory when elevated is also secure in order to maintain the security -// of all files being executed while elevated. -HRESULT VerifyFileAndExecute(const CString& filename, const CString& args) { - UTIL_LOG(L1, (_T("[VerifyFileAndExecute][%s][%s]"), filename, args)); - - CString secure_filename; - HRESULT hr = CopyToSecureLocation(filename, &secure_filename); - if (FAILED(hr)) { - UTIL_LOG(LE, (_T("[CopyToSecureLocation failed][error 0x%08x]"), hr)); - return hr; - } - - hr = omaha::VerifyIsValidRepairFile(secure_filename); - if (FAILED(hr)) { - UTIL_LOG(LE, (_T("[VerifyIsValidRepairFile failed][error 0x%08x]"), hr)); - return hr; - } - - hr = ExecuteFile(secure_filename, args); - if (FAILED(hr)) { - UTIL_LOG(LE, (_T("[ExecuteFile failed][error 0x%08x]"), hr)); - return hr; - } - - // Use the API directly rather than File::DeleteAfterReboot() because - // File::DeleteAfterReboot() moves the file immediately, causing the - // GetFileVersionInfoSize call in the metainstaller to fail. - // Because this is always run as SYSTEM, the delayed delete will succeed. - VERIFY1(::MoveFileEx(secure_filename, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)); - - return S_OK; -} - -} // namespace omaha diff --git a/omaha/recovery/repair_exe/custom_action/execute_repair_file.h b/omaha/recovery/repair_exe/custom_action/execute_repair_file.h deleted file mode 100644 index 123b05d85..000000000 --- a/omaha/recovery/repair_exe/custom_action/execute_repair_file.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Verifies and executes the repair file for the MSP custom action. - -#ifndef OMAHA_RECOVERY_REPAIR_EXE_CUSTOM_ACTION_EXECUTE_REPAIR_FILE_H__ -#define OMAHA_RECOVERY_REPAIR_EXE_CUSTOM_ACTION_EXECUTE_REPAIR_FILE_H__ - -#include -#include - -namespace omaha { - -// Verifies the repair file and executes it. -HRESULT VerifyFileAndExecute(const CString& filename, const CString& args); - -} // namespace omaha - -#endif // OMAHA_RECOVERY_REPAIR_EXE_CUSTOM_ACTION_EXECUTE_REPAIR_FILE_H__ diff --git a/omaha/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc b/omaha/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc deleted file mode 100644 index adb29201f..000000000 --- a/omaha/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -// -// Unit tests for the file execution module of the MSP custom action. - -#include "omaha/base/app_util.h" -#include "omaha/base/file.h" -#include "omaha/base/path.h" -#include "omaha/base/vistautil.h" -#include "omaha/recovery/repair_exe/custom_action/execute_repair_file.h" -#include "omaha/testing/unit_test.h" - -#undef MSP_TEST_EXPECTED_DIR -#define MSP_TEST_EXPECTED_DIR \ - _T("%PROGRAMFILES%\\") OMAHA_REL_GOOPDATE_INSTALL_DIR - -namespace omaha { - -namespace { - -// The valid repair file saves the arguments passed to it to a file. -void RunAndVerifySavedArgs(const CString& args) { - CString expected_copy_path = - MSP_TEST_EXPECTED_DIR _T("\\SaveArguments.exe"); - EXPECT_SUCCEEDED(ExpandStringWithSpecialFolders(&expected_copy_path)); - CString saved_arguments_file_path = - MSP_TEST_EXPECTED_DIR _T("\\saved_arguments.txt"); - EXPECT_SUCCEEDED(ExpandStringWithSpecialFolders(&saved_arguments_file_path)); - - CString repair_file(app_util::GetCurrentModuleDirectory()); - EXPECT_TRUE(::PathAppend(CStrBuf(repair_file, MAX_PATH), - _T("unittest_support\\SaveArguments.exe"))); - - ::DeleteFile(saved_arguments_file_path); - - if (vista_util::IsUserAdmin()) { - EXPECT_FALSE(File::Exists(saved_arguments_file_path)); - - EXPECT_SUCCEEDED(omaha::VerifyFileAndExecute(repair_file, args)); - - bool is_found = false; - for (int tries = 0; tries < 100 && !is_found; ++tries) { - ::Sleep(50); - is_found = File::Exists(saved_arguments_file_path); - } - ASSERT_TRUE(is_found); - - scoped_hfile file; - for (int tries = 0; tries < 100 && !valid(file); ++tries) { - ::Sleep(50); - reset(file, ::CreateFile(saved_arguments_file_path, - GENERIC_READ, - 0, // do not share - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, - NULL)); // no template - } - ASSERT_TRUE(valid(file)); - - const int kBufferLen = 50; - TCHAR buffer[kBufferLen + 1] = {0}; - DWORD bytes_read = 0; - - // Do not assume the buffer read by ReadFile remains zero-terminated. - EXPECT_TRUE(::ReadFile(get(file), - buffer, - kBufferLen * sizeof(TCHAR), - &bytes_read, - NULL)); - EXPECT_EQ(0, bytes_read % sizeof(TCHAR)); - buffer[bytes_read / sizeof(TCHAR)] = _T('\0'); - EXPECT_STREQ(args, buffer); - - reset(file); - - ::DeleteFile(expected_copy_path); - EXPECT_TRUE(::DeleteFile(saved_arguments_file_path)); - } else { - const bool expected_file_exists = File::Exists(saved_arguments_file_path); - EXPECT_EQ(E_ACCESSDENIED, omaha::VerifyFileAndExecute(repair_file, args)); - - // We can't force the file to be deleted, so make sure it wasn't created - // or deleted by the above method. - EXPECT_EQ(expected_file_exists, File::Exists(saved_arguments_file_path)); - } -} - -} // namespace - -TEST(ExecuteRepairFileTest, VerifyFileAndExecute_EmptyFilename) { - EXPECT_EQ(E_INVALIDARG, VerifyFileAndExecute(_T(""), _T(""))); -} - -TEST(ExecuteRepairFileTest, VerifyFileAndExecute_FileDoesNotExist) { - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), - VerifyFileAndExecute(_T("no_such_file.exe"), _T(""))); -} - -TEST(ExecuteRepairFileTest, VerifyFileAndExecute_FilenameIsDirectory) { - EXPECT_EQ(E_ACCESSDENIED, - VerifyFileAndExecute(_T("C:\\Windows"), _T(""))); -} - -TEST(ExecuteRepairFileTest, VerifyFileAndExecute_UnsignedFile) { - CString expected_copy_path = - MSP_TEST_EXPECTED_DIR _T("\\GoogleUpdate_unsigned.exe"); - EXPECT_SUCCEEDED(ExpandStringWithSpecialFolders(&expected_copy_path)); - CString repair_file(app_util::GetCurrentModuleDirectory()); - EXPECT_TRUE(::PathAppend(CStrBuf(repair_file, MAX_PATH), - _T("GoogleUpdate_unsigned.exe"))); - - if (vista_util::IsUserAdmin()) { - EXPECT_EQ(TRUST_E_NOSIGNATURE, VerifyFileAndExecute(repair_file, _T(""))); - - EXPECT_TRUE(File::Exists(expected_copy_path)); - EXPECT_TRUE(::DeleteFile(expected_copy_path)); - } else { - const bool expected_file_exists = File::Exists(expected_copy_path); - EXPECT_EQ(E_ACCESSDENIED, VerifyFileAndExecute(repair_file, _T(""))); - - // We can't force the file to be deleted, so make sure it wasn't created - // or deleted by the above method. - EXPECT_EQ(expected_file_exists, File::Exists(expected_copy_path)); - } -} - -TEST(ExecuteRepairFileTest, VerifyFileAndExecute_ValidRepairFileWithArgs) { - RunAndVerifySavedArgs(_T("These /are the args.")); -} - -TEST(ExecuteRepairFileTest, VerifyFileAndExecute_ValidRepairFileWithoutArgs) { - RunAndVerifySavedArgs(_T("")); -} - -} // namespace omaha diff --git a/omaha/recovery/repair_exe/custom_action/executecustomaction.cc b/omaha/recovery/repair_exe/custom_action/executecustomaction.cc deleted file mode 100644 index 2f1a5d4c6..000000000 --- a/omaha/recovery/repair_exe/custom_action/executecustomaction.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Custom action helper DLL: -// The custom actions created in this project are for Google Update MSI patch, -// which is installed temporarily and uses this DLL to verify a downloaded -// executable and run it with elevated privileges. - - -#include "omaha/recovery/repair_exe/custom_action/executecustomaction.h" -#include -#include "omaha/base/debug.h" -#include "omaha/recovery/repair_exe/mspexecutableelevator.h" -#include "omaha/recovery/repair_exe/custom_action/execute_repair_file.h" - -namespace { - -omaha::CustomActionModule _AtlModule; - -} // namespace - -// DLL Entry Point -extern "C" BOOL WINAPI DllMain(HINSTANCE, - DWORD dwReason, - LPVOID lpReserved) { - return _AtlModule.DllMain(dwReason, lpReserved); -} - -// Verify an executable and run it -UINT __stdcall VerifyFileAndExecute(MSIHANDLE install_handle) { - TCHAR custom_action_data[2048] = {0}; - DWORD size = ARRAYSIZE(custom_action_data) - 1; - *custom_action_data = _T('\0'); - if (ERROR_SUCCESS == ::MsiGetProperty(install_handle, - _T("CustomActionData"), - custom_action_data, - &size) && - _T('\0') != *custom_action_data) { - custom_action_data[ARRAYSIZE(custom_action_data) - 1] = _T('\0'); - - TCHAR* executable = NULL; - TCHAR* arguments = NULL; - DWORD calling_process_id = 0; - if (omaha::msp_executable_elevator::ParseMSPCommandLine( - custom_action_data, - &executable, - &arguments, - &calling_process_id) && - executable && arguments) { - HRESULT hr = omaha::VerifyFileAndExecute(executable, arguments); - VERIFY1(omaha::msp_executable_elevator::SetResultOfExecute(NULL, hr)); - } - } - return 0; -} - -// 4505: unreferenced local function has been removed -#pragma warning(disable : 4505) diff --git a/omaha/recovery/repair_exe/custom_action/executecustomaction.def b/omaha/recovery/repair_exe/custom_action/executecustomaction.def deleted file mode 100644 index 827e1fdec..000000000 --- a/omaha/recovery/repair_exe/custom_action/executecustomaction.def +++ /dev/null @@ -1,19 +0,0 @@ -; Copyright 2007-2009 Google Inc. -; -; Licensed under the Apache License, Version 2.0 (the "License"); -; you may not use this file except in compliance with the License. -; You may obtain a copy of the License at -; -; http://www.apache.org/licenses/LICENSE-2.0 -; -; Unless required by applicable law or agreed to in writing, software -; distributed under the License is distributed on an "AS IS" BASIS, -; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -; See the License for the specific language governing permissions and -; limitations under the License. -; ======================================================================== - -LIBRARY "executecustomaction.dll" - -EXPORTS - VerifyFileAndExecute diff --git a/omaha/recovery/repair_exe/custom_action/executecustomaction.h b/omaha/recovery/repair_exe/custom_action/executecustomaction.h deleted file mode 100644 index d2377d312..000000000 --- a/omaha/recovery/repair_exe/custom_action/executecustomaction.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_RECOVERY_REPAIR_EXE_CUSTOM_ACTION_EXECUTECUSTOMACTION_H__ -#define OMAHA_RECOVERY_REPAIR_EXE_CUSTOM_ACTION_EXECUTECUSTOMACTION_H__ - -#include -#include -#include - -namespace omaha { - -class CustomActionModule : public CAtlDllModuleT { - public: - CustomActionModule() {} - ~CustomActionModule() {} -}; - -} // namespace omaha - -extern "C" -UINT __stdcall VerifyFileAndExecute(MSIHANDLE); - -#endif // OMAHA_RECOVERY_REPAIR_EXE_CUSTOM_ACTION_EXECUTECUSTOMACTION_H__ diff --git a/omaha/recovery/repair_exe/custom_action/testelevateusingmsp.cc b/omaha/recovery/repair_exe/custom_action/testelevateusingmsp.cc deleted file mode 100644 index f99695e56..000000000 --- a/omaha/recovery/repair_exe/custom_action/testelevateusingmsp.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Command line utility that elevates an executable using an MSI Patch. -// The MSI patch is assumed to be in the same directory as this executable. - -#include -#include -#include "omaha/base/constants.h" -#include "omaha/recovery/repair_exe/mspexecutableelevator.h" - -int main(int argc, char** argv) { - DWORD process_id = 0; - if (2 <= argc) { - CString arguments; - if (3 <= argc) { - arguments = argv[2]; - } - HANDLE process = NULL; - HRESULT hr = omaha::msp_executable_elevator::ExecuteGoogleSignedExe( - CString(argv[1]), - arguments, - omaha::kHelperInstallerProductGuid, - omaha::kHelperPatchGuid, - omaha::kHelperPatchName, - &process); - if (process) { - process_id = ::GetProcessId(process); - ::CloseHandle(process); - } - wprintf(_T("%s (process handle:%p process id: %u hresult:%x)"), - (SUCCEEDED(hr) ? _T("Success") : _T("Failure")), - process, - process_id, - static_cast(hr)); - } else { - static TCHAR explain_test[] = - _T("testelevateusingmsp\n\n") - _T("To use this test, pass the full path to an executable containing ") - _T("the Google Update Repair resource and is signed with a Google ") - _T("code-signing certificate that has a certain subject and ") - _T("organization unit name.") - _T("\n\nAn optional parameter to that executable may be passed as well."); - wprintf(_T("%s"), explain_test); - } - return process_id; -} diff --git a/omaha/recovery/repair_exe/msp/build.scons b/omaha/recovery/repair_exe/msp/build.scons deleted file mode 100644 index d9ddaf947..000000000 --- a/omaha/recovery/repair_exe/msp/build.scons +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -# -# Hammer file to create the stub .msi and .msp files -# - -import os -import stat - -Import('env') - - -def CreateMsiInstallerFiles(env, wxs_file, suffix=''): - _repair_exe_obj_dir = '$OBJ_ROOT/recovery/repair_exe/' - _custom_actions_path = ( - _repair_exe_obj_dir + 'custom_action/executecustomaction.dll') - _cert_file = env.File(GetOption('patching_certificate')).abspath - _required_file = '$MAIN_DIR/recovery/repair_exe/msp/requiredfile.txt' - - # TODO(omaha): Update the build machine command lines to use - # patching_certificate instead of authenticode_file and remove this. - # The following is for backwards compatibility. Historically, the build - # server, as specified the patching_certificate with --authenticode_file. - if (env.Bit('build_server') and - GetOption('patching_certificate') == '$MAIN_DIR/data/OmahaTestCert.cer'): - _cert_file = env.File(GetOption('authenticode_file')).abspath - - omaha_version_info = env['omaha_versions_info'][0] - omaha_version_string = omaha_version_info.GetVersionString() - - old_unsigned_env = env.Clone() - old_unsigned_env.Append( - WIXCANDLEFLAGS = [ - '-dFinalMsi=0', - '-dCertificateFile=' + _cert_file, - '-dRequiredFile=' + env.File(_required_file).abspath, - '-dGoogleUpdateVersion=' + omaha_version_string, - ], - WIXLIGHTFLAGS = [ - '-dRequiredFile=' + env.File(_required_file).abspath, - '-sval', - ], - ) - - old_unsigned_output = old_unsigned_env.WiX( - 'GoogleUpdateHelper%s_unsigned.msi' % suffix, - wxs_file) - - env.Depends(old_unsigned_output, [_custom_actions_path, _required_file]) - - - new_unsigned_env = env.Clone() - new_unsigned_env.Append( - WIXCANDLEFLAGS = [ - '-dFinalMsi=1', - '-dExecuteCustomActionDLL=' + env.File(_custom_actions_path).abspath, - '-dCertificateFile=' + _cert_file, - '-dRequiredFile=' + env.File(_required_file).abspath, - '-dGoogleUpdateVersion=' + omaha_version_string, - ], - WIXLIGHTFLAGS = [ - '-dRequiredFile=' + env.File(_required_file).abspath, - '-sval', - ], - ) - - # Output to a subdirectory to avoid build breaks caused by two different - # actions referencing files with the same name and path. - new_unsigned_env['WIXOBJPREFIX'] = new_unsigned_env['WIXOBJPREFIX'] + 'new/' - - new_unsigned_output = new_unsigned_env.WiX( - target='new/GoogleUpdateHelper%s_unsigned.msi' % suffix, - source=wxs_file - ) - - env.Depends(new_unsigned_output, [_custom_actions_path, _required_file]) - return (old_unsigned_output, new_unsigned_output) - - -# -# Create the MSP file -# -def CreateMsiPatchFile(env, wxs_patch_file, old_msi, new_msi, suffix=''): - msp_env = env.Clone() - - patch_output = msp_env.Command( - target='patch%s.wixobj' % suffix, - source=wxs_patch_file, - action=('@candle.exe -nologo -out $TARGET $SOURCE -dAfterImage=%s' - ' -dBeforeImage=%s' % (env.File(new_msi[0]).abspath, - env.File(old_msi[0]).abspath)) - ) - - # Required because the before and after images are not in the source. - Depends(patch_output, [old_msi, new_msi]) - - pcp_output = msp_env.Command( - target='patch%s.pcp' % suffix, - source=patch_output, - action='@light.exe -nologo -out $TARGET $SOURCE' - ) - - # The PCP, and thus the MSP, fail to rebuild when the MSI files change without - # this explicit dependency, probably because the .wixobj hash does not change. - Depends(pcp_output, [patch_output, old_msi, new_msi]) - - # Delete temp dir that vista sdk version of msimsp.exe cannot remove for - # itself. - _temp_dir = os.path.join(env['ENV']['TMP'], '~pcw_tmp.tmp') - if os.path.exists(_temp_dir): - # Recursively delete subdirectories - def rm_rf(dir): - for file in os.listdir(dir): - path = os.path.join(dir, file) - if os.path.isdir(path): - rm_rf(path) - else: - os.chmod(path, stat.S_IWRITE) # Make sure file is writeable. - os.remove(path) - os.rmdir(dir) - - # Remove the temp dir. - rm_rf(_temp_dir) - - unsigned_msp_name = 'GoogleUpdateHelperPatch%s_unsigned.msp' % suffix - unsigned_msp_path = '$OBJ_ROOT/recovery/repair_exe/msp/' + unsigned_msp_name - - msp_output = msp_env.Command( - target=unsigned_msp_name, - source=pcp_output, - action='@msimsp.exe -s $SOURCE -p $TARGET -l %s' % ( - env.File(unsigned_msp_path + '.log').abspath), - ) - - # For unknown reasons, the PCP fails to rebuild and cause the MSP to rebuild - # when the MSI files change without this explicit dependency. - Depends(msp_output, pcp_output) - return msp_output - - -old_msi, new_msi = CreateMsiInstallerFiles( - env, 'patchableinstaller.wxs') -old_msi_for_legacy, new_msi_for_legacy = CreateMsiInstallerFiles( - env, 'legacy/patchableinstaller.wxs', 'legacy') - -msp_output = CreateMsiPatchFile(env, 'patch.wxs', old_msi, new_msi) -msp_output_for_legacy = CreateMsiPatchFile( - env, - 'legacy/patch.wxs', - old_msi_for_legacy, - new_msi_for_legacy, - 'legacy') - -# -# Sign the old MSI and MSP files -# -signed_msi = env.SignedBinary( - target='GoogleUpdateHelper.msi', - source=old_msi, -) - -signed_msp = env.SignedBinary( - target='GoogleUpdateHelperPatch.msp', - source=msp_output, -) - -signed_msp_for_legacy = env.SignedBinary( - target='GoogleUpdateHelperPatchWithLegacyID.msp', - source=msp_output_for_legacy, -) - -env.Replicate('$STAGING_DIR', [signed_msi, signed_msp, signed_msp_for_legacy]) diff --git a/omaha/recovery/repair_exe/msp/legacy/patch.wxs b/omaha/recovery/repair_exe/msp/legacy/patch.wxs deleted file mode 100644 index 601b381b3..000000000 --- a/omaha/recovery/repair_exe/msp/legacy/patch.wxs +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/omaha/recovery/repair_exe/msp/legacy/patchableinstaller.wxs b/omaha/recovery/repair_exe/msp/legacy/patchableinstaller.wxs deleted file mode 100644 index 026b8ef95..000000000 --- a/omaha/recovery/repair_exe/msp/legacy/patchableinstaller.wxs +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NOT EXECUTABLECOMMANDLINE="" - NOT EXECUTABLECOMMANDLINE="" - - - - - - - - - - - - - - diff --git a/omaha/recovery/repair_exe/msp/patch.wxs b/omaha/recovery/repair_exe/msp/patch.wxs deleted file mode 100644 index 601b381b3..000000000 --- a/omaha/recovery/repair_exe/msp/patch.wxs +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/omaha/recovery/repair_exe/msp/patchableinstaller.wxs b/omaha/recovery/repair_exe/msp/patchableinstaller.wxs deleted file mode 100644 index 66edcea48..000000000 --- a/omaha/recovery/repair_exe/msp/patchableinstaller.wxs +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NOT EXECUTABLECOMMANDLINE="" - NOT EXECUTABLECOMMANDLINE="" - - - - - - - - - - - - - - diff --git a/omaha/recovery/repair_exe/msp/requiredfile.txt b/omaha/recovery/repair_exe/msp/requiredfile.txt deleted file mode 100644 index 0b410612a..000000000 --- a/omaha/recovery/repair_exe/msp/requiredfile.txt +++ /dev/null @@ -1 +0,0 @@ -A file is required to patch an MSI. \ No newline at end of file diff --git a/omaha/recovery/repair_exe/msp/setup_env.bat b/omaha/recovery/repair_exe/msp/setup_env.bat deleted file mode 100644 index 3ddcc7655..000000000 --- a/omaha/recovery/repair_exe/msp/setup_env.bat +++ /dev/null @@ -1,5 +0,0 @@ -:: This script must not rely on any external tools or PATH values. -@echo OFF - -:: Creating the MSP requires the msimsp.exe from the 2003 R2 SDK -set PATH=%PATH%;%~dp0..\..\..\..\third_party\platformsdk_win_server_2003_r2_partial\Samples\SysMgmt\Msi\Patching diff --git a/omaha/recovery/repair_exe/msp/testApplying300Patches.bat b/omaha/recovery/repair_exe/msp/testApplying300Patches.bat deleted file mode 100644 index db59a5b76..000000000 --- a/omaha/recovery/repair_exe/msp/testApplying300Patches.bat +++ /dev/null @@ -1,23 +0,0 @@ -@echo off - -rem normally, only 127 patches can be applied to an msi - -set /A ii=0 - -rem msiexec /i GoogleUpdateHelper.msi /qn -rem echo original product installed - -:repeat - -msiexec /update GoogleUpdateHelperPatch.msp REINSTALL=ALL /qn /L*v patchapply%ii%.log -echo patch %ii% applied -if %ii% GEQ 127 pause -msiexec /uninstall {E0D0D2C9-5836-4023-AB1D-54EC3B90AD03} /package {A92DAB39-4E2C-4304-9AB6-BC44E68B55E2} /qn /L*v patchremove%ii%.log -echo patch %ii% removed - -set /A ii=%ii%+1 - -if %ii% NEQ 300 goto repeat -rem if %ii% NEQ 127 goto repeat - -set ii= diff --git a/omaha/recovery/repair_exe/mspexecutableelevator.cc b/omaha/recovery/repair_exe/mspexecutableelevator.cc deleted file mode 100644 index 4209c8495..000000000 --- a/omaha/recovery/repair_exe/mspexecutableelevator.cc +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// This is the implementation of the API for verifying and executing -// an executable under high integrity using an Msi Patch. -// -// This class assumes the following: -// 1) its needed Msi has already been installed, -// 2) its needed Msp is in the same directory as this module, -// 3) the name of the Msp file, -// 4) the name of the property passed as CustomActionData to the custom action, -// 5) the guid of the patch, and -// 6) the guid of the Msi install which will be patched. - -#define _WIN32_MSI 300 - -#include "omaha/recovery/repair_exe/mspexecutableelevator.h" -#include -#include -#include "omaha/base/app_util.h" -#include "omaha/base/debug.h" -#include "omaha/base/safe_format.h" -#include "omaha/base/string.h" - -namespace omaha { - -namespace msp_executable_elevator { - -// Used to return information back to the process that called -// ExecuteGoogleSignedExe. -struct SharedMemoryInfo { - HANDLE process; - HRESULT launch_result; -}; - -// Used to store the name of the shared memory. The name is retrieved from -// the MSP command line when parsing the command line. The code assumes that -// only one thread per process will call ParseMSPCommandLine followed by -// SetResultOfExecute (which is a safe assumption if this functionality is only -// used for the purpose for which it was originally written). -// Assumes that the MSP is in the same directory as the current process. -static TCHAR parsed_shared_memory_name[200]; - -HRESULT ExecuteGoogleSignedExe(const TCHAR* exe, - const TCHAR* args, - const TCHAR* kProductGuid, - const TCHAR* kPatchGuid, - const TCHAR* kPatchName, - HANDLE* process) { - ASSERT1(exe); - ASSERT1(args); - ASSERT1(process); - ASSERT1(kProductGuid); - ASSERT1(kPatchGuid); - ASSERT1(kPatchName); - - // Create shared memory in which to receive result of attempt to launch - // process and a handle to the launched process. - HRESULT hr = E_FAIL; - GUID random_guid = {0}; - TCHAR shared_memory_name[200] = {0}; - if (SUCCEEDED(::CoCreateGuid(&random_guid)) && - 0 < ::StringFromGUID2(random_guid, - shared_memory_name, - ARRAYSIZE(shared_memory_name))) { - HANDLE file_mapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, - NULL, - PAGE_READWRITE, - 0, - sizeof(file_mapping), - shared_memory_name); - if (file_mapping) { - SharedMemoryInfo* shared_info = reinterpret_cast - (::MapViewOfFileEx(file_mapping, - FILE_MAP_ALL_ACCESS, - 0, - 0, - sizeof(*shared_info), - 0)); - if (shared_info) { - shared_info->launch_result = E_FAIL; - shared_info->process = NULL; - // Create command line to pass to patch. Parameters are the name of the - // shared memory just created, the current process id, the executable - // to launch, and the executable's arguments. - CString command_line; - SafeCStringFormat(&command_line, - _T("EXECUTABLECOMMANDLINE=\"%s %u \"\"%s\"\" %s\" ") - _T("REINSTALL=ALL"), - shared_memory_name, - GetCurrentProcessId(), - exe, - args); - // Generate path to patch using path to current module. - CString module_name = app_util::GetModulePath( - _AtlBaseModule.GetModuleInstance()); - if (!module_name.IsEmpty() && !command_line.IsEmpty()) { - CPath path = module_name.GetString(); - if (path.RemoveFileSpec() && path.Append(kPatchName)) { - path.Canonicalize(); - // Set install level to none so that user does not see - // an Msi window and so that a restore point is not created. - ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); - UINT res = ::MsiApplyPatch(path, - NULL, - INSTALLTYPE_DEFAULT, - command_line); - // MsiApplyPatch will not return until the passed executable has - // been launched (or not) and the shared memory has been updated. - *process = shared_info->process; - hr = HRESULT_FROM_WIN32(res); - if (SUCCEEDED(hr)) - hr = shared_info->launch_result; - } - } - ::MsiRemovePatches(kPatchGuid, - kProductGuid, - INSTALLTYPE_SINGLE_INSTANCE, - NULL); - VERIFY1(::UnmapViewOfFile(shared_info)); - } - VERIFY1(::CloseHandle(file_mapping)); - } - } - return hr; -} - -bool ParseMSPCommandLine(TCHAR* command_line, - TCHAR** executable_result, - TCHAR** arguments_result, - DWORD* calling_process_id) { - ASSERT1(command_line); - ASSERT1(executable_result); - ASSERT1(arguments_result); - ASSERT1(calling_process_id); - // Parse command line. First extract name of shared memory into which - // the process handle of launched executable will be written. Then extract - // the process id of calling process. This process id will be used to create - // for the calling process a handle to the launched executable. Finally, - // extract the path to executable so that we can verify the executable. - TCHAR* shared_memory_name = NULL; - TCHAR* process_id_param = NULL; - TCHAR* arguments = NULL; - if (SplitCommandLineInPlace(command_line, &shared_memory_name, &arguments) && - shared_memory_name && arguments && - SplitCommandLineInPlace(arguments, &process_id_param, &arguments) && - process_id_param && arguments && - SplitCommandLineInPlace(arguments, executable_result, &arguments) && - *executable_result && arguments) { - *calling_process_id = static_cast(_wtoi(process_id_param)); - *arguments_result = arguments; - _tcsncpy(parsed_shared_memory_name, - shared_memory_name, - ARRAYSIZE(parsed_shared_memory_name)); - parsed_shared_memory_name[ARRAYSIZE(parsed_shared_memory_name) - 1] = - _T('\0'); - return true; - } - return false; -} - -// Copy process handle and result to shared memory. -// Process can be NULL. -bool SetResultOfExecute(HANDLE process, HRESULT result) { - bool success = false; - if (_T('\0') != *parsed_shared_memory_name) { - HANDLE file_mapping = ::OpenFileMapping(FILE_MAP_WRITE, - FALSE, - parsed_shared_memory_name); - if (file_mapping) { - SharedMemoryInfo* shared_info = reinterpret_cast - (::MapViewOfFileEx(file_mapping, - FILE_MAP_WRITE, - 0, - 0, - sizeof(SharedMemoryInfo), - 0)); - if (shared_info) { - shared_info->process = process; - shared_info->launch_result = result; - VERIFY1(::UnmapViewOfFile(shared_info)); - success = true; - } else { - ASSERT(false, (_T("::MapViewOfFileEx failed."))); - } - - VERIFY1(::CloseHandle(file_mapping)); - } else { - ASSERT(false, (_T("::OpenFileMapping failed."))); - } - } else { - ASSERT(false, (_T("parsed_shared_memory_name is empty."))); - } - return success; -} - -} // namespace msp_executable_elevator - -} // namespace omaha - diff --git a/omaha/recovery/repair_exe/mspexecutableelevator.h b/omaha/recovery/repair_exe/mspexecutableelevator.h deleted file mode 100644 index 0cc35d50b..000000000 --- a/omaha/recovery/repair_exe/mspexecutableelevator.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// This is the API for verifying and executing an executable under high -// integrity using an Msi Patch. This API assumes its needed Msi has already -// been installed and its needed Msp is in the same directory as this module. -// -// This class encapsulates the code for passing information between a process -// requesting that an executable be elevated and the custom action DLL -// in the patch which actually elevates the executable. - -#ifndef OMAHA_RECOVERY_REPAIR_EXE_MSPEXECUTABLEELEVATOR_H__ -#define OMAHA_RECOVERY_REPAIR_EXE_MSPEXECUTABLEELEVATOR_H__ - -#include -#include - -namespace omaha { - -namespace msp_executable_elevator { - -// The following function should be called by the code requesting that -// an executable be elevated: - -// Use an MSI patch to verify and execute an executable. Returns a handle -// to the process executed. -HRESULT ExecuteGoogleSignedExe(const TCHAR* executable, - const TCHAR* arguments, - const TCHAR* kProductGuid, - const TCHAR* kPatchGuid, - const TCHAR* kPatchName, - HANDLE* process); - -// The following functions should be called by the code (i.e., the custom action -// DLL) that actually elevates the executable: - -// From the command line passed to the MSP, retrieve the parameters that will be -// passed to VerifyFileAndExecute. -// This function is destructive to the passed command line buffer. The pointers -// "executable" and "arguments" will point into the command line buffer. -bool ParseMSPCommandLine(TCHAR* command_line, - TCHAR** executable, - TCHAR** arguments, - DWORD* calling_process_id); - -// Records the result of the call to VerifyFileAndExecute. -bool SetResultOfExecute(HANDLE process, HRESULT result); - -} // namespace msp_executable_elevator - -} // namespace omaha - -#endif // OMAHA_RECOVERY_REPAIR_EXE_MSPEXECUTABLEELEVATOR_H__ diff --git a/omaha/recovery/repair_exe/mspexecutableelevator_unittest.cc b/omaha/recovery/repair_exe/mspexecutableelevator_unittest.cc deleted file mode 100644 index 2d0a59cac..000000000 --- a/omaha/recovery/repair_exe/mspexecutableelevator_unittest.cc +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Unit tests for msp_executable_elevator. -// -// Note for Windows Vista and later: These tests will fail because the Msi* -// methods return an error because they do not elevate due to the UI level NONE. -// Enabling the UI would cause UAC prompts and the creation of restore points. -// The workaround is to run from an administrator command prompt. - -#include -#include -#include "omaha/base/app_util.h" -#include "omaha/base/constants.h" -#include "omaha/base/file.h" -#include "omaha/base/path.h" -#include "omaha/base/reg_key.h" -#include "omaha/base/utils.h" -#include "omaha/base/vistautil.h" -#include "omaha/recovery/repair_exe/mspexecutableelevator.h" -#include "omaha/setup/msi_test_utils.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -// Note: For some reason, the product ID GUIDs are swizzled in the registry. -extern const TCHAR kMsiProductPatchesKey[] = - _T("HKEY_LOCAL_MACHINE[64]\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\") - _T("Installer\\UserData\\S-1-5-18\\Products\\") - _T("93BAD29AC2E44034A96BCB446EB8552E\\Patches"); - -void InstallMsi() { - CString msi_path(app_util::GetCurrentModuleDirectory()); - EXPECT_TRUE(::PathAppend(CStrBuf(msi_path, MAX_PATH), - kHelperInstallerName)); - ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); - - UINT res = ::MsiInstallProduct(msi_path, _T("")); - - if (vista_util::IsUserAdmin()) { - if (ERROR_SUCCESS != res) { - EXPECT_EQ(ERROR_PRODUCT_VERSION, res); - // The product may already be installed. Force a reinstall of everything. - res = ::MsiInstallProduct(msi_path, - _T("REINSTALL=ALL REINSTALLMODE=vamus")); - } - EXPECT_EQ(ERROR_SUCCESS, res); - } else { - if (IsMsiHelperInstalled()) { - EXPECT_EQ(ERROR_INSTALL_PACKAGE_REJECTED, res); - } else { - EXPECT_EQ(ERROR_INSTALL_FAILURE, res); - } - } -} - -void RemoveMsi() { - ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); - UINT res = ::MsiConfigureProduct(kHelperInstallerProductGuid, - INSTALLLEVEL_DEFAULT, - INSTALLSTATE_ABSENT); - if (vista_util::IsUserAdmin()) { - EXPECT_TRUE((ERROR_SUCCESS == res) || - ((ERROR_UNKNOWN_PRODUCT == res) && !IsMsiHelperInstalled())); - } else { - if (IsMsiHelperInstalled()) { - EXPECT_EQ(ERROR_INSTALL_FAILURE, res); - } else { - EXPECT_EQ(ERROR_UNKNOWN_PRODUCT, res); - } - } -} - -HRESULT ExecuteGoogleSignedExeWithCorrectPatchInfo(const TCHAR* executable, - const TCHAR* arguments, - HANDLE* process) { - return msp_executable_elevator::ExecuteGoogleSignedExe( - executable, - arguments, - kHelperInstallerProductGuid, - kHelperPatchGuid, - kHelperPatchName, - process); -} - -// Base class for tests that expect the MSI to be installed. -// The elevation mechanism will not be installed when these test complete. -class RepairGoopdateWithMsiInstalledTest : public testing::Test { - protected: - static void SetUpTestCase() { - InstallMsi(); - } - - static void TearDownTestCase() { - RemoveMsi(); - } -}; - -// Base class for tests that expect the MSI not to be installed. -// The elevation mechanism will not be installed when these test complete. -class RepairGoopdateWithoutMsiInstalledTest : public testing::Test { - protected: - static void SetUpTestCase() { - RemoveMsi(); - } -}; - -TEST_F(RepairGoopdateWithMsiInstalledTest, - ExecuteGoogleSignedExe_RepairFileDoesNotExist) { - CString repair_file(_T("no_such_file.exe")); - HANDLE process = NULL; - HRESULT hr = ExecuteGoogleSignedExeWithCorrectPatchInfo(repair_file, - _T(""), - &process); - if (vista_util::IsUserAdmin() || IsMsiHelperInstalled()) { - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr); - } else { - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), hr); - } - - EXPECT_TRUE(NULL == process); -} - -TEST_F(RepairGoopdateWithMsiInstalledTest, - ExecuteGoogleSignedExe_UnsignedFile) { - CString repair_file(app_util::GetCurrentModuleDirectory()); - EXPECT_TRUE(::PathAppend(CStrBuf(repair_file, MAX_PATH), - _T("GoogleUpdate_unsigned.exe"))); - HANDLE process = NULL; - HRESULT hr = ExecuteGoogleSignedExeWithCorrectPatchInfo(repair_file, - _T(""), - &process); - if (vista_util::IsUserAdmin() || IsMsiHelperInstalled()) { - EXPECT_EQ(TRUST_E_NOSIGNATURE, hr); - } else { - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), hr); - } - EXPECT_TRUE(NULL == process); -} - -// This valid repair file saves the arguments passed to it to a file. -TEST_F(RepairGoopdateWithMsiInstalledTest, - ExecuteGoogleSignedExe_ValidRepairFile) { - const TCHAR kArgs[] = _T("These /are the args."); - CString repair_file(app_util::GetCurrentModuleDirectory()); - EXPECT_TRUE(::PathAppend(CStrBuf(repair_file, MAX_PATH), - _T("unittest_support\\SaveArguments.exe"))); - CString program_files_path; - EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path)); - CString saved_arguments_file_path = program_files_path + - + _T("\\") OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\saved_arguments.txt"); - - ::DeleteFile(saved_arguments_file_path); - - bool is_msi_installed = IsMsiHelperInstalled(); - - if (vista_util::IsUserAdmin() || is_msi_installed) { - if (vista_util::IsUserAdmin()) { - EXPECT_FALSE(File::Exists(saved_arguments_file_path)); - } - - // Verify that no patch is installed. - EXPECT_TRUE(RegKey::HasKey(kMsiProductPatchesKey)); - RegKey product_patches_key; - EXPECT_SUCCEEDED(product_patches_key.Open(kMsiProductPatchesKey, KEY_READ)); - EXPECT_EQ(0, product_patches_key.GetSubkeyCount()); - - HANDLE process = NULL; - EXPECT_SUCCEEDED(ExecuteGoogleSignedExeWithCorrectPatchInfo(repair_file, - kArgs, - &process)); - EXPECT_TRUE(NULL == process); - - // Verify that patch was uninstalled. - // GetSubkeyCount fails if we don't re-open the key. - EXPECT_SUCCEEDED(product_patches_key.Open(kMsiProductPatchesKey, KEY_READ)); - EXPECT_EQ(0, product_patches_key.GetSubkeyCount()); - - bool is_found = false; - for (int tries = 0; tries < 100 && !is_found; ++tries) { - ::Sleep(50); - is_found = File::Exists(saved_arguments_file_path); - } - ASSERT_TRUE(is_found); - - scoped_hfile file; - for (int tries = 0; tries < 100 && !valid(file); ++tries) { - ::Sleep(50); - reset(file, ::CreateFile(saved_arguments_file_path, - GENERIC_READ, - 0, // do not share - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, - NULL)); // no template - } - ASSERT_TRUE(valid(file)); - - const int kBufferLen = 50; - TCHAR buffer[kBufferLen + 1] = {0}; - DWORD bytes_read = 0; - - EXPECT_TRUE(::ReadFile(get(file), - buffer, - kBufferLen * sizeof(TCHAR), - &bytes_read, - NULL)); - EXPECT_EQ(0, bytes_read % sizeof(TCHAR)); - buffer[bytes_read / sizeof(TCHAR)] = _T('\0'); - EXPECT_STREQ(kArgs, buffer); - - reset(file); - - bool succeeded = !!::DeleteFile(saved_arguments_file_path); - if (vista_util::IsUserAdmin()) { - EXPECT_TRUE(succeeded); - } - } else { - bool expected_file_exists = File::Exists(saved_arguments_file_path); - HANDLE process = NULL; - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), - ExecuteGoogleSignedExeWithCorrectPatchInfo(repair_file, - kArgs, - &process)); - EXPECT_TRUE(NULL == process); - - // We can't force the file to be deleted, so make sure it wasn't created - // or deleted by the above method. - EXPECT_EQ(expected_file_exists, File::Exists(saved_arguments_file_path)); - } -} - -TEST_F(RepairGoopdateWithoutMsiInstalledTest, - ExecuteGoogleSignedExe_MsiNotInstalled) { - if (!vista_util::IsUserAdmin() && IsMsiHelperInstalled()) { - std::wcout << _T("\tThis test did not run because the user is not an ") - _T("admin and the MSI is already installed.") << std::endl; - return; - } - - CString repair_file(_T("notepad.exe")); - HANDLE process = NULL; - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), - ExecuteGoogleSignedExeWithCorrectPatchInfo(repair_file, - _T(""), - &process)); - EXPECT_TRUE(NULL == process); -} - -} // namespace omaha diff --git a/omaha/recovery/repair_exe/repair_goopdate.cc b/omaha/recovery/repair_exe/repair_goopdate.cc deleted file mode 100644 index f881bf442..000000000 --- a/omaha/recovery/repair_exe/repair_goopdate.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Helper for the Google Update repair file. It launches the same repair file -// elevated using the MSP. - -#include "omaha/recovery/repair_exe/repair_goopdate.h" -#include "omaha/base/debug.h" -#include "omaha/base/logging.h" -#include "omaha/base/utils.h" -#include "omaha/base/vistautil.h" -#include "omaha/recovery/repair_exe/mspexecutableelevator.h" - -namespace omaha { - -// Returns without launching the repair file if is_machine is false, running -// on a pre-Windows Vista OS, or the current user is Local System. -// This method does not always launch the repair file because we expect this -// to be called from the repair file, and there is no reason to launch another -// process. -bool LaunchRepairFileElevated(bool is_machine, - const TCHAR* repair_file, - const TCHAR* args, - HRESULT* elevation_hr) { - ASSERT1(elevation_hr); - - *elevation_hr = S_OK; - - if (!is_machine) { - UTIL_LOG(L2, (_T("[user instance - not elevating]"))); - return false; - } - if (!vista_util::IsVistaOrLater()) { - UTIL_LOG(L2, (_T("[Pre-Windows Vista OS - not elevating]"))); - return false; - } - - bool is_user_local_system = false; - HRESULT hr = IsSystemProcess(&is_user_local_system); - if (SUCCEEDED(hr) && is_user_local_system) { - UTIL_LOG(L2, (_T("[User is already SYSTEM - not elevating]"))); - return false; - } - - HANDLE process = NULL; - *elevation_hr = msp_executable_elevator::ExecuteGoogleSignedExe( - repair_file, - args, - kHelperInstallerProductGuid, - kHelperPatchGuid, - kHelperPatchName, - &process); - // Our implementation of msp_executable_elevator does not set the process - // handle parameter, but we did not remove it from the borrowed code. - ASSERT1(!process); - - if (FAILED(*elevation_hr)) { - UTIL_LOG(LE, (_T("[ExecuteGoogleSignedExe failed][error 0x%08x]"), - *elevation_hr)); - return false; - } - - return true; -} - -} // namespace omaha diff --git a/omaha/recovery/repair_exe/repair_goopdate.h b/omaha/recovery/repair_exe/repair_goopdate.h deleted file mode 100644 index bfb2a3c64..000000000 --- a/omaha/recovery/repair_exe/repair_goopdate.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Helper for the Google Update repair file. It launches the same repair file -// elevated using the MSP. - -#ifndef OMAHA_RECOVERY_REPAIR_EXE_REPAIR_GOOPDATE_H__ -#define OMAHA_RECOVERY_REPAIR_EXE_REPAIR_GOOPDATE_H__ - -#include -#include - -namespace omaha { - -// Launches the repair file elevated if necessary. -// Returns whether the repair file was successfully launched elevated. -// When the method returns false, the caller should execute the repair -// operations unelevated. Otherwise, the caller is done. -// Example: -// if (!LaunchRepairFileElevated(...)) { -// DoRepairUnelevated(); -// } -bool LaunchRepairFileElevated(bool is_machine, - const TCHAR* repair_file, - const TCHAR* args, - HRESULT* elevation_hr); - -} // namespace omaha - -#endif // OMAHA_RECOVERY_REPAIR_EXE_REPAIR_GOOPDATE_H__ diff --git a/omaha/recovery/repair_exe/repair_goopdate_unittest.cc b/omaha/recovery/repair_exe/repair_goopdate_unittest.cc deleted file mode 100644 index cd7d3a80f..000000000 --- a/omaha/recovery/repair_exe/repair_goopdate_unittest.cc +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== -// -// Unit tests for the Omaha repair mechanism. - -#include "omaha/base/app_util.h" -#include "omaha/base/file.h" -#include "omaha/base/path.h" -#include "omaha/base/reg_key.h" -#include "omaha/base/vistautil.h" -#include "omaha/recovery/repair_exe/repair_goopdate.h" -#include "omaha/setup/msi_test_utils.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -const TCHAR kArgumentSavingExecutableRelativePath[] = - _T("unittest_support\\SaveArguments.exe"); - -extern const TCHAR kMsiProductPatchesKey[]; -void InstallMsi(); -void RemoveMsi(); - -class RepairGoopdateTest : public testing::Test { - protected: - static void SetUpTestCase() { - valid_path = app_util::GetCurrentModuleDirectory(); - VERIFY1(::PathAppend(CStrBuf(valid_path, MAX_PATH), - kArgumentSavingExecutableRelativePath)); - } - - static CString valid_path; -}; - -CString RepairGoopdateTest::valid_path; - -TEST_F(RepairGoopdateTest, LaunchRepairFileElevated_UserInstance) { - HRESULT hr = E_FAIL; - EXPECT_FALSE(LaunchRepairFileElevated(false, valid_path, _T("/update"), &hr)); - EXPECT_SUCCEEDED(hr); -} - -// This test only runs on pre-Windows Vista OSes. -TEST_F(RepairGoopdateTest, LaunchRepairFileElevated_MachineInstancePreVista) { - if (!vista_util::IsVistaOrLater()) { - HRESULT hr = E_FAIL; - EXPECT_FALSE(LaunchRepairFileElevated(true, valid_path, _T(""), &hr)); - EXPECT_SUCCEEDED(hr); - } -} - -// This test only runs on Windows Vista and later OSes. -TEST_F(RepairGoopdateTest, - LaunchRepairFileElevated_MachineInstanceVistaWithMsiInstalledValidFile) { - if (!vista_util::IsVistaOrLater()) { - std::wcout << _T("\tThis test did not run because it requires Windows ") - _T("Vista or later.") << std::endl; - return; - } - - const TCHAR kArgs[] = _T("/update"); - CString saved_arguments_file_path = _T("%PROGRAMFILES%\\") \ - OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\saved_arguments.txt"); - EXPECT_SUCCEEDED(ExpandStringWithSpecialFolders(&saved_arguments_file_path)); - - ::DeleteFile(saved_arguments_file_path); - - bool is_msi_installed = IsMsiHelperInstalled(); - - if (vista_util::IsUserAdmin() || is_msi_installed) { - if (vista_util::IsUserAdmin()) { - EXPECT_FALSE(File::Exists(saved_arguments_file_path)); - } - - InstallMsi(); - - // Verify that no patch is installed. - EXPECT_TRUE(RegKey::HasKey(kMsiProductPatchesKey)); - RegKey product_patches_key; - EXPECT_SUCCEEDED(product_patches_key.Open(kMsiProductPatchesKey, KEY_READ)); - EXPECT_EQ(0, product_patches_key.GetSubkeyCount()); - - HRESULT hr = E_FAIL; - EXPECT_TRUE(LaunchRepairFileElevated(true, valid_path, kArgs, &hr)); - EXPECT_SUCCEEDED(hr); - - // Verify that patch was uninstalled. - // GetSubkeyCount fails if we don't re-open the key. - EXPECT_SUCCEEDED(product_patches_key.Open(kMsiProductPatchesKey, KEY_READ)); - EXPECT_EQ(0, product_patches_key.GetSubkeyCount()); - - bool is_found = false; - for (int tries = 0; tries < 100 && !is_found; ++tries) { - ::Sleep(50); - is_found = File::Exists(saved_arguments_file_path); - } - ASSERT_TRUE(is_found); - - scoped_hfile file; - for (int tries = 0; tries < 100 && !valid(file); ++tries) { - ::Sleep(50); - reset(file, ::CreateFile(saved_arguments_file_path, - GENERIC_READ, - 0, // do not share - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, - NULL)); // no template - } - ASSERT_TRUE(valid(file)); - - const int kBufferLen = 50; - TCHAR buffer[kBufferLen + 1] = {0}; - DWORD bytes_read = 0; - - EXPECT_TRUE(::ReadFile(get(file), - buffer, - kBufferLen * sizeof(TCHAR), - &bytes_read, - NULL)); - - EXPECT_EQ(0, bytes_read % sizeof(TCHAR)); - buffer[bytes_read / sizeof(TCHAR)] = _T('\0'); - EXPECT_STREQ(kArgs, buffer); - - reset(file); - - BOOL succeeded = ::DeleteFile(saved_arguments_file_path); - if (vista_util::IsUserAdmin()) { - EXPECT_TRUE(succeeded); - } - - RemoveMsi(); - } else { - const bool expected_file_exists = File::Exists(saved_arguments_file_path); - HRESULT hr = E_FAIL; - EXPECT_FALSE(LaunchRepairFileElevated(true, valid_path, kArgs, &hr)); - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), hr); - - // We can't force the file to be deleted, so make sure it wasn't created - // or deleted by the above method. - EXPECT_EQ(expected_file_exists, File::Exists(saved_arguments_file_path)); - } -} - -// This test only runs on Windows Vista and later OSes. -// The MSP to runs, but fails because the specified file (the unit tests) -// is not a valid repair file. -TEST_F(RepairGoopdateTest, - LaunchRepairFileElevated_MachineInstanceVistaWithMsiInstalledBadFile) { - if (!vista_util::IsVistaOrLater()) { - std::wcout << _T("\tThis test did not run because it requires Windows ") - _T("Vista or later.") << std::endl; - return; - } - - if (vista_util::IsUserAdmin() || IsMsiHelperInstalled()) { - InstallMsi(); - - // Verify that no patch is installed. - EXPECT_TRUE(RegKey::HasKey(kMsiProductPatchesKey)); - RegKey product_patches_key; - EXPECT_SUCCEEDED(product_patches_key.Open(kMsiProductPatchesKey, KEY_READ)); - EXPECT_EQ(0, product_patches_key.GetSubkeyCount()); - - HRESULT hr = E_FAIL; - EXPECT_FALSE(LaunchRepairFileElevated(true, - app_util::GetCurrentModulePath(), - _T("/update"), - &hr)); - EXPECT_EQ(TRUST_E_NOSIGNATURE, hr); - - // Verify that patch was uninstalled. - // GetSubkeyCount fails if we don't re-open the key. - EXPECT_SUCCEEDED(product_patches_key.Open(kMsiProductPatchesKey, KEY_READ)); - EXPECT_EQ(0, product_patches_key.GetSubkeyCount()); - - RemoveMsi(); - } else { - HRESULT hr = E_FAIL; - EXPECT_FALSE(LaunchRepairFileElevated(true, - app_util::GetCurrentModulePath(), - _T("/update"), - &hr)); - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), hr); - } -} - -// This test only runs on Windows Vista and later OSes. -TEST_F(RepairGoopdateTest, - LaunchRepairFileElevated_MachineInstanceVistaWithoutMsiInstalled) { - if (!vista_util::IsVistaOrLater()) { - std::wcout << _T("\tThis test did not run because it requires Windows ") - _T("Vista or later.") << std::endl; - return; - } - - if (vista_util::IsUserAdmin()) { - RemoveMsi(); - } else if (IsMsiHelperInstalled()) { - std::wcout << _T("\tThis test did not run because the user is not an ") - _T("admin and the MSI is already installed.") << std::endl; - return; - } - // else the user is not an admin but the MSI is not installed, so continue. - - HRESULT hr = E_FAIL; - EXPECT_FALSE(LaunchRepairFileElevated(true, valid_path, _T("/update"), &hr)); - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATCH_TARGET_NOT_FOUND), hr); -} - -} // namespace omaha diff --git a/omaha/service/build.scons b/omaha/service/build.scons index 7ba41a6e4..fcef89815 100644 --- a/omaha/service/build.scons +++ b/omaha/service/build.scons @@ -19,12 +19,6 @@ Import('env') # unaffected by changes made here. local_env = env.Clone() -local_env.Append( - CPPPATH = [ - '$OBJ_ROOT', # Needed for generated files. - ], -) - inputs = [ 'service_main.cc', ] diff --git a/omaha/service/service_main.cc b/omaha/service/service_main.cc index 95322a6c9..7c24bd960 100644 --- a/omaha/service/service_main.cc +++ b/omaha/service/service_main.cc @@ -33,6 +33,7 @@ END_OBJECT_MAP() BEGIN_OBJECT_MAP(object_map_google_update_medium) OBJECT_ENTRY(__uuidof(OnDemandMachineAppsServiceClass), OnDemandService) OBJECT_ENTRY(__uuidof(GoogleUpdate3WebServiceClass), Update3WebService) + OBJECT_ENTRY(__uuidof(PolicyStatusMachineServiceClass), PolicyStatusService) OBJECT_ENTRY(__uuidof(GoogleUpdateCoreClass), GoogleUpdateCoreService) END_OBJECT_MAP() @@ -73,7 +74,10 @@ HRESULT Update3ServiceMode::PreMessageLoop() { SERVICE_LOG(L1, (_T("[Starting Google Update core...]"))); CommandLineBuilder builder(COMMANDLINE_MODE_CORE); CString args = builder.GetCommandLineArgs(); - return goopdate_utils::StartGoogleUpdateWithArgs(true, args, NULL); + return goopdate_utils::StartGoogleUpdateWithArgs(true, + StartMode::kBackground, + args, + NULL); } CommandLineMode UpdateMediumServiceMode::commandline_mode() { diff --git a/omaha/service/service_main.h b/omaha/service/service_main.h index c8aa5b9af..95100ae40 100644 --- a/omaha/service/service_main.h +++ b/omaha/service/service_main.h @@ -68,6 +68,7 @@ #include "omaha/goopdate/google_update3.h" #include "omaha/goopdate/non_localized_resource.h" #include "omaha/goopdate/ondemand.h" +#include "omaha/goopdate/policy_status.h" #include "omaha/goopdate/update3web.h" #include "omaha/goopdate/worker.h" #include "omaha/net/network_config.h" @@ -370,8 +371,8 @@ class ServiceModule while (true) { ::WaitForSingleObject(this->m_hEventShutdown, INFINITE); - SERVICE_LOG(L4, (_T("[Infinite Wait][%d][%d]"), this->m_bActivity, this->m_nLockCnt)); - + SERVICE_LOG(L4, (_T("[Infinite Wait][%d][%d]"), + this->m_bActivity, this->m_nLockCnt)); DWORD wait = 0; do { this->m_bActivity = false; diff --git a/omaha/setup/build.scons b/omaha/setup/build.scons index 72b286bb2..041fcf0de 100644 --- a/omaha/setup/build.scons +++ b/omaha/setup/build.scons @@ -24,11 +24,5 @@ inputs = [ 'setup_metrics.cc', ] -# Need to look in output dir to find .h files generated by midl compiler. -# This also allows Hammer to understand dependencies between this subdir -# and the .idl files in the goopdate folder. -# TODO(omaha): It would be nice to eliminate references to COM files from setup. -local_env['CPPPATH'] += ['$OBJ_ROOT'] - # Build these into a static library. local_env.ComponentLibrary('setup', inputs) diff --git a/omaha/setup/msi_test_utils.cc b/omaha/setup/msi_test_utils.cc deleted file mode 100644 index 127d49278..000000000 --- a/omaha/setup/msi_test_utils.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -// IsMsiHelperInstalled requires MSI version 3.0 for MsiEnumProductsEx. -// Omaha does not require MSI 3.0. -#ifdef _WIN32_MSI -#if (_WIN32_MSI < 300) -#undef _WIN32_MSI -#define _WIN32_MSI 300 -#endif -#else -#define _WIN32_MSI 300 -#endif - -#include -#include -#include "omaha/base/constants.h" -#include "omaha/testing/unit_test.h" - -namespace omaha { - -bool IsMsiHelperInstalled() { - int res = ::MsiEnumProductsEx(kHelperInstallerProductGuid, - NULL, - MSIINSTALLCONTEXT_MACHINE, - 0, - NULL, - NULL, - NULL, - NULL); - - bool is_msi_installed = (ERROR_SUCCESS == res); - EXPECT_TRUE(is_msi_installed || ERROR_NO_MORE_ITEMS == res); - - return is_msi_installed; -} - -} // namespace omaha diff --git a/omaha/setup/msi_test_utils.h b/omaha/setup/msi_test_utils.h deleted file mode 100644 index bcdf8cf11..000000000 --- a/omaha/setup/msi_test_utils.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#ifndef OMAHA_SETUP_MSI_TEST_UTILS_H_ -#define OMAHA_SETUP_MSI_TEST_UTILS_H_ - -namespace omaha { - -// Returns true if the Helper MSI is installed. -bool IsMsiHelperInstalled(); - -} // namespace omaha - -#endif // OMAHA_SETUP_MSI_TEST_UTILS_H_ diff --git a/omaha/setup/setup.cc b/omaha/setup/setup.cc index 24e316b97..dd57d397c 100644 --- a/omaha/setup/setup.cc +++ b/omaha/setup/setup.cc @@ -120,11 +120,9 @@ void IncrementProcessWaitFailCount(CommandLineMode mode) { case COMMANDLINE_MODE_REGSERVER: case COMMANDLINE_MODE_UNREGSERVER: - case COMMANDLINE_MODE_NETDIAGS: case COMMANDLINE_MODE_CRASH: case COMMANDLINE_MODE_INSTALL: case COMMANDLINE_MODE_RECOVER: - case COMMANDLINE_MODE_WEBPLUGIN: case COMMANDLINE_MODE_COMSERVER: case COMMANDLINE_MODE_REGISTER_PRODUCT: case COMMANDLINE_MODE_UNREGISTER_PRODUCT: @@ -137,7 +135,6 @@ void IncrementProcessWaitFailCount(CommandLineMode mode) { case COMMANDLINE_MODE_UNINSTALL: case COMMANDLINE_MODE_PING: case COMMANDLINE_MODE_HEALTH_CHECK: - case COMMANDLINE_MODE_REGISTER_MSI_HELPER: default: ++metric_setup_process_wait_failed_other; break; @@ -209,7 +206,7 @@ Setup::~Setup() { // * Shutdown Event - Tells existing other instances and any instances that may // start during Setup to exit. // Setup-related operations do not include installation of the app. -HRESULT Setup::Install(bool set_keepalive) { +HRESULT Setup::Install(RuntimeMode runtime_mode) { SETUP_LOG(L3, (_T("[Admin=%d, NEAdmin=%d, Update3Svc=%d, MedSvc=%d, Machine=%d"), vista_util::IsUserAdmin(), @@ -240,7 +237,7 @@ HRESULT Setup::Install(bool set_keepalive) { metric_setup_lock_acquire_ms.AddSample(lock_metrics_timer.GetElapsedMs()); SETUP_LOG(L1, (_T("[Setup Locks acquired]"))); - HRESULT hr = DoProtectedInstall(set_keepalive); + HRESULT hr = DoProtectedInstall(runtime_mode); if (FAILED(hr)) { SETUP_LOG(LE, (_T("[Setup::DoProtectedInstall failed][0x%08x]"), hr)); } @@ -346,7 +343,7 @@ HRESULT Setup::HandleLockFailed(int lock_version) { } // Assumes the necessary locks have been acquired. -HRESULT Setup::DoProtectedInstall(bool set_keepalive) { +HRESULT Setup::DoProtectedInstall(RuntimeMode runtime_mode) { SETUP_LOG(L2, (_T("[Setup::DoProtectedInstall]"))); SetupFiles setup_files(is_machine_); @@ -388,16 +385,14 @@ HRESULT Setup::DoProtectedInstall(bool set_keepalive) { return hr; } - // If we've been asked to defer uninstall (typically because we're doing - // an Omaha-only install to expose the COM API to a later process), set - // it on a successful install. - if (set_keepalive) { - SetDelayUninstall(true); - } - ++metric_setup_do_self_install_succeeded; } + // We set or reset the Runtime mode here, regardless of whether Omaha was + // already installed or we just installed it. This is to allow for turning + // on/off the Runtime mode independent of Omaha installation. + SetRuntimeMode(runtime_mode); + return S_OK; } @@ -469,6 +464,7 @@ bool Setup::ShouldOverinstall() { CString cmd_line = builder.GetCommandLineArgs(); scoped_process process; HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_, + StartMode::kBackground, cmd_line, address(process)); if (FAILED(hr)) { @@ -504,7 +500,7 @@ HRESULT Setup::DoProtectedGoogleUpdateInstall(SetupFiles* setup_files) { // TODO(omaha3): Enable. Prefer to move out of Setup if possible. #if 0 - VERIFY1(SUCCEEDED(ResetMetrics(is_machine_))); + VERIFY_SUCCEEDED(ResetMetrics(is_machine_)); #endif hr = RegKey::GetValue( @@ -546,14 +542,14 @@ void Setup::RollBack(SetupFiles* setup_files) { SETUP_LOG(L1, (_T("[Rolling back version to %s]"), saved_version_)); ++metric_setup_rollback_version; - VERIFY1(SUCCEEDED(RegKey::SetValue( + VERIFY_SUCCEEDED(RegKey::SetValue( ConfigManager::Instance()->registry_clients_goopdate(is_machine_), kRegValueProductVersion, - saved_version_))); + saved_version_)); } // TODO(omaha3): Rollback SetupGoogleUpdate. - VERIFY1(SUCCEEDED(setup_files->RollBack())); + VERIFY_SUCCEEDED(setup_files->RollBack()); } // Assumes the caller is ensuring this is the only running instance of setup. @@ -585,14 +581,6 @@ HRESULT Setup::SetupGoogleUpdate() { } } - // Registration of browser plugins is only done after the shutdown event has - // been released; this prevents race conditions where a browser could start - // a new install while the shutdown event was still being held. - HRESULT plugin_hr = setup_google_update.InstallBrowserPlugins(); - if (FAILED(plugin_hr)) { - SETUP_LOG(LE, (_T("[InstallBrowserPlugins failed][0x%08x]"), plugin_hr)); - } - // Setup is now complete. metric_setup_phase2_ms.AddSample(phase2_metrics_timer.GetElapsedMs()); @@ -644,7 +632,7 @@ HRESULT Setup::DoProtectedUninstall(bool send_uninstall_ping) { } if (FAILED(hr)) { - VERIFY1(SUCCEEDED(AggregateMetrics(is_machine_))); + VERIFY_SUCCEEDED(AggregateMetrics(is_machine_)); return hr; } hr = AggregateAndReportMetrics(is_machine_, true); @@ -842,7 +830,7 @@ HRESULT Setup::WaitForOtherInstancesToExit(const Pids& pids, SETUP_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error)); hr = HRESULT_FROM_WIN32(error); } else if (WAIT_OBJECT_0 != res) { - OPT_LOG(LEVEL_ERROR, (_T("[Other GoogleUpdate.exe instances failed to ") + OPT_LOG(LEVEL_ERROR, (_T("[Other ") MAIN_EXE_BASE_NAME _T(".exe instances failed to ") _T("shutdown in time][%u]"), res)); extra_code1_ = COMMANDLINE_MODE_UNKNOWN; @@ -1277,18 +1265,23 @@ bool Setup::CanUninstallGoogleUpdate() const { CORE_LOG(L2, (_T("[Found install workers. Not uninstalling]"))); return false; } - if (ShouldDelayUninstall()) { - // If the DelayUninstall flag is set, that implies that someone has + + RuntimeMode runtime_mode = GetRuntimeMode(); + if (runtime_mode == RUNTIME_MODE_PERSIST) { + OPT_LOG(L1, (_T("[RUNTIME_MODE_PERSIST is set. Not uninstalling.]"))); + return false; + } else if (runtime_mode == RUNTIME_MODE_TRUE) { + // If the RUNTIME_MODE_TRUE flag is set, that implies that someone has // installed us with the runtime=true flag, expecting that they can // use our API later from another process. If 24 hours have passed // since that initial Omaha install, we clear the flag but still // return false for this check. (That way, if a machine has been // suspended in mid-install, they still have a grace period until // the next /ua to get something installed.) - CORE_LOG(L3, (_T("[DelayUninstall is set. Not uninstalling.]"))); + CORE_LOG(L3, (_T("[RUNTIME_MODE_TRUE is set. Not uninstalling.]"))); if (ConfigManager::Instance()->Is24HoursSinceLastUpdate(is_machine_)) { - CORE_LOG(L4, (_T("[24 hours elapsed; clearing DelayUninstall.]"))); - SetDelayUninstall(false); + CORE_LOG(L4, (_T("[24 hours elapsed; clearing RUNTIME_MODE_TRUE.]"))); + SetRuntimeMode(RUNTIME_MODE_FALSE); } return false; } @@ -1302,26 +1295,37 @@ bool Setup::CanUninstallGoogleUpdate() const { return true; } -bool Setup::ShouldDelayUninstall() const { +RuntimeMode Setup::GetRuntimeMode() const { const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_); - if (!RegKey::HasValue(key, kRegValueDelayOmahaUninstall)) { - return false; + if (!RegKey::HasValue(key, kRegValueRuntimeMode)) { + return RUNTIME_MODE_NOT_SET; } - DWORD should_delay = 0; - if (FAILED(RegKey::GetValue(key, - kRegValueDelayOmahaUninstall, - &should_delay))) { - return false; + DWORD runtime_mode = static_cast(RUNTIME_MODE_NOT_SET); + if (FAILED(RegKey::GetValue(key, kRegValueRuntimeMode, &runtime_mode))) { + return RUNTIME_MODE_NOT_SET; + } + + if (runtime_mode != RUNTIME_MODE_TRUE && + runtime_mode != RUNTIME_MODE_PERSIST) { + return RUNTIME_MODE_NOT_SET; } - return should_delay != 0; + + return static_cast(runtime_mode); } -HRESULT Setup::SetDelayUninstall(bool should_delay) const { +HRESULT Setup::SetRuntimeMode(RuntimeMode runtime_mode) const { + if (runtime_mode == RUNTIME_MODE_NOT_SET) { + return S_FALSE; + } + const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_); - if (should_delay) { - return RegKey::SetValue(key, kRegValueDelayOmahaUninstall, 1UL); + if (runtime_mode == RUNTIME_MODE_TRUE || + runtime_mode == RUNTIME_MODE_PERSIST) { + return RegKey::SetValue(key, + kRegValueRuntimeMode, + static_cast(runtime_mode)); } else { - return RegKey::DeleteValue(key, kRegValueDelayOmahaUninstall); + return RegKey::DeleteValue(key, kRegValueRuntimeMode); } } @@ -1350,7 +1354,7 @@ HRESULT Setup::SendUninstallPing() { // network activity on a no-clients case, we will have to start passing the // session ID from /ua to /uninstall in the future. CString session_id; - VERIFY1(SUCCEEDED(GetGuid(&session_id))); + VERIFY_SUCCEEDED(GetGuid(&session_id)); // Send uninstall ping for uninstalled apps. HRESULT hr = S_OK; diff --git a/omaha/setup/setup.h b/omaha/setup/setup.h index 708eac3c6..d470e394f 100644 --- a/omaha/setup/setup.h +++ b/omaha/setup/setup.h @@ -31,6 +31,7 @@ #include #include "base/basictypes.h" +#include "omaha/common/const_goopdate.h" #include "omaha/third_party/smartany/scoped_any.h" namespace omaha { @@ -47,7 +48,7 @@ class Setup { ~Setup(); // Installs Omaha if necessary. - HRESULT Install(bool set_keepalive); + HRESULT Install(RuntimeMode runtime_mode); // Acquires the Setup Lock and uninstalls all Omaha versions if Omaha can be // uninstalled. @@ -75,7 +76,7 @@ class Setup { // Does the install work within all necessary locks, which have already been // acquired. - HRESULT DoProtectedInstall(bool set_keepalive); + HRESULT DoProtectedInstall(RuntimeMode runtime_mode); // Uninstalls all Google Update versions after checking if Google Update can // be uninstalled. @@ -173,10 +174,9 @@ class Setup { // Returns true if GoogleUpdate can be uninstalled now. bool CanUninstallGoogleUpdate() const; - // Control the state of the DelayUninstall flag. If set, uninstall will - // be delayed for at least 24 hours after initial install. - bool ShouldDelayUninstall() const; - HRESULT SetDelayUninstall(bool should_delay) const; + // The state of the RuntimeMode in the registry. + RuntimeMode GetRuntimeMode() const; + HRESULT SetRuntimeMode(RuntimeMode runtime_mode) const; // Sends the uninstall ping and waits for the ping to be sent. HRESULT SendUninstallPing(); diff --git a/omaha/setup/setup_files.cc b/omaha/setup/setup_files.cc index 43e0e74f4..8440bf93c 100644 --- a/omaha/setup/setup_files.cc +++ b/omaha/setup/setup_files.cc @@ -41,13 +41,6 @@ namespace omaha { -namespace { - -const int kNumberOfCreateServiceRetries = 5; -const int kSleepBetweenCreateServiceRetryMs = 200; - -} // namespace - SetupFiles::SetupFiles(bool is_machine) : is_machine_(is_machine) { SETUP_LOG(L2, (_T("[SetupFiles::SetupFiles]"))); @@ -58,7 +51,7 @@ SetupFiles::~SetupFiles() { if (!saved_shell_path_.IsEmpty()) { // Delete the saved copy of the previous shell. - VERIFY1(SUCCEEDED(File::Remove(saved_shell_path_))); + VERIFY_SUCCEEDED(File::Remove(saved_shell_path_)); } } @@ -110,21 +103,6 @@ HRESULT SetupFiles::Install() { return hr; } - // Copy the metainstaller. Since the metainstaller is tagged, its file size - // may vary; so, we always overwrite, even if it's the same version. - // TODO(omaha): Once we refactor our tag management functions to be capable - // of stripping the tag from a MI, change this so that we always copy over - // an untagged metainstaller instead. Untagged metainstallers should always - // be the same size. - hr = CopyInstallFiles(metainstaller_files_, install_dir, true); - if (FAILED(hr)) { - OPT_LOG(LEVEL_ERROR, (_T("[Failed to copy metainstaller][0x%08x]"), hr)); - if (E_ACCESSDENIED == hr) { - return GOOPDATE_E_ACCESSDENIED_COPYING_MI; - } - return hr; - } - // Attempt to copy the optional files. hr = CopyInstallFiles(optional_files_, install_dir, should_over_install); if (FAILED(hr)) { @@ -178,6 +156,20 @@ void SetupFiles::Uninstall() { SETUP_LOG(LE, (_T("[DeleteDirectory failed][%s][0x%08x]"), install_dir, hr)); } + + // Best-effort attempt to delete the company directory if it is empty. + CString company_dir( + is_machine_ ? ConfigManager::Instance()->GetMachineCompanyDir() : + ConfigManager::Instance()->GetUserCompanyDir()); + if (!File::IsDirectory(company_dir) || !::PathIsDirectoryEmpty(company_dir)) { + return; + } + + // `::RemoveDirectory` deletes an existing empty directory. + if (!::RemoveDirectory(company_dir)) { + hr = HRESULTFromLastError(); + SETUP_LOG(LE, (_T("[::RemoveDirectory failed][%s][%#x]"), company_dir, hr)); + } } HRESULT SetupFiles::CopyShell() { @@ -196,7 +188,7 @@ HRESULT SetupFiles::CopyShell() { if (should_copy) { if (already_exists) { ++metric_setup_files_replace_shell; - VERIFY1(SUCCEEDED(SaveShellForRollback(shell_path))); + VERIFY_SUCCEEDED(SaveShellForRollback(shell_path)); } std::vector shell_files; @@ -271,8 +263,13 @@ HRESULT SetupFiles::ShouldCopyShell(const CString& shell_install_path, } HRESULT SetupFiles::SaveShellForRollback(const CString& shell_install_path) { + const CString temp_dir = ConfigManager::Instance()->GetTempDir(); + if (temp_dir.IsEmpty()) { + return E_UNEXPECTED; + } + // Copy existing file to a temporary file in case we need to roll back. - CString temp_file = GetTempFilename(_T("gsh")); + CString temp_file = GetTempFilenameAt(temp_dir, _T("gsh")); if (temp_file.IsEmpty()) { const DWORD error = ::GetLastError(); SETUP_LOG(LEVEL_WARNING, (_T("[::GetTempFilename failed][%d]"), error)); @@ -289,11 +286,9 @@ HRESULT SetupFiles::SaveShellForRollback(const CString& shell_install_path) { } // The list of files below needs to be kept in sync with payload_files in -// omaha_version_utils.py. The one exception is kOmahaMetainstallerFileName, -// which is not part of the payload. +// omaha_version_utils.py. HRESULT SetupFiles::BuildFileLists() { ASSERT1(core_program_files_.empty()); - ASSERT1(metainstaller_files_.empty()); ASSERT1(optional_files_.empty()); core_program_files_.clear(); @@ -308,23 +303,16 @@ HRESULT SetupFiles::BuildFileLists() { // files using wildcards. ResourceManager::GetSupportedLanguageDllNames(&core_program_files_); - core_program_files_.push_back(kHelperInstallerName); - core_program_files_.push_back(kPSFileNameUser); core_program_files_.push_back(kPSFileNameUser64); core_program_files_.push_back(kPSFileNameMachine); core_program_files_.push_back(kPSFileNameMachine64); - metainstaller_files_.clear(); - metainstaller_files_.push_back(kOmahaMetainstallerFileName); - // If files are removed from this list, unit tests such as // ShouldInstall_SameVersionOptionalFileMissing may need to be updated. optional_files_.clear(); - optional_files_.push_back(UPDATE_PLUGIN_FILENAME); optional_files_.push_back(kOmahaBrokerFileName); optional_files_.push_back(kOmahaOnDemandFileName); - optional_files_.push_back(kOmahaWebPluginFileName); // Machine-specific files are always installed, to support cross installs from // user to machine and machine to user. @@ -410,7 +398,7 @@ HRESULT SetupFiles::CopyAndValidateFiles( for (size_t i = 0; i != destination_file_paths.size(); ++i) { const CString cur_file = destination_file_paths[i]; const CString dot_old(cur_file + _T(".old")); - VERIFY1(SUCCEEDED(File::Remove(dot_old))); + VERIFY_SUCCEEDED(File::Remove(dot_old)); HRESULT hr = File::Move(cur_file, dot_old, true); if (SUCCEEDED(hr)) { // Delete after reboot only works for admins. .old files will be left @@ -458,7 +446,7 @@ HRESULT SetupFiles::CopyAndValidateFiles( OPT_LOG(LE, (_T("[postcopy verification failed][from=%s][to=%s][0x%x]"), source_file, destination_file, hr)); ++metric_setup_files_verification_failed_post; - VERIFY1(SUCCEEDED(File::Remove(destination_file))); + VERIFY_SUCCEEDED(File::Remove(destination_file)); return hr; } } diff --git a/omaha/setup/setup_files.h b/omaha/setup/setup_files.h index 2d8406a1a..c9f2ca3fb 100644 --- a/omaha/setup/setup_files.h +++ b/omaha/setup/setup_files.h @@ -81,7 +81,6 @@ class SetupFiles { const bool is_machine_; CString saved_shell_path_; // Path of the previous shell saved for roll back. std::vector core_program_files_; - std::vector metainstaller_files_; std::vector optional_files_; int extra_code1_; diff --git a/omaha/setup/setup_files_unittest.cc b/omaha/setup/setup_files_unittest.cc index 100839a12..205637524 100644 --- a/omaha/setup/setup_files_unittest.cc +++ b/omaha/setup/setup_files_unittest.cc @@ -14,6 +14,7 @@ // ======================================================================== #include +#include #include #include "omaha/base/app_util.h" @@ -35,16 +36,15 @@ namespace { // TODO(omaha3): Update the numbers in the else block as we build more files. // Eventually use the original values in the if block. const int kNumberOfLanguageDlls = 55; -const int kNumberOfCoreFiles = 11; -const int kNumberOfMetainstallerFiles = 1; -const int kNumberOfOptionalFiles = 4; +const int kNumberOfCoreFiles = 10; +const int kNumberOfOptionalFiles = 2; const int kNumberOfInstalledRequiredFiles = kNumberOfLanguageDlls + kNumberOfCoreFiles; // FindFiles returns "." and ".." in addition to the actual files. const int kExtraFilesReturnedByFindFiles = 2; -const int kExpectedFilesReturnedByFindFiles = - kNumberOfInstalledRequiredFiles + kNumberOfMetainstallerFiles + - kNumberOfOptionalFiles + kExtraFilesReturnedByFindFiles; +const int kExpectedFilesReturnedByFindFiles = kNumberOfInstalledRequiredFiles + + kNumberOfOptionalFiles + + kExtraFilesReturnedByFindFiles; const TCHAR kFutureVersionString[] = _T("9.8.7.6"); const ULONGLONG kFutureVersion = 0x0009000800070006; @@ -67,14 +67,10 @@ void CopyGoopdateFiles(const CString& omaha_path, const CString& version) { kCrashHandlerFileName, kCrashHandler64FileName, kOmahaShellFileName, - kHelperInstallerName, kOmahaCOMRegisterShell64, kOmahaDllName, - kOmahaMetainstallerFileName, kOmahaBrokerFileName, kOmahaOnDemandFileName, - kOmahaWebPluginFileName, - UPDATE_PLUGIN_FILENAME, kPSFileNameMachine, kPSFileNameMachine64, kPSFileNameUser, @@ -159,8 +155,6 @@ class SetupFilesTest : public testing::Test { ASSERT_EQ(kNumberOfInstalledRequiredFiles, setup_files_->core_program_files_.size()); - ASSERT_EQ(kNumberOfMetainstallerFiles, - setup_files_->metainstaller_files_.size()); ASSERT_EQ(kNumberOfOptionalFiles, setup_files_->optional_files_.size()); DeleteDirectory(version_path); @@ -177,77 +171,149 @@ class SetupFilesTest : public testing::Test { EXPECT_SUCCEEDED(FindFiles(version_path, _T("*.*"), &files)); ASSERT_EQ(kExpectedFilesReturnedByFindFiles, files.size()); int file_index = kExtraFilesReturnedByFindFiles; - EXPECT_STREQ(kCrashHandlerFileName, files[file_index++]); - EXPECT_STREQ(kCrashHandler64FileName, files[file_index++]); - EXPECT_STREQ(kOmahaShellFileName, files[file_index++]); - EXPECT_STREQ(kOmahaBrokerFileName, files[file_index++]); - EXPECT_STREQ(kOmahaCOMRegisterShell64, files[file_index++]); - EXPECT_STREQ(kOmahaCoreFileName, files[file_index++]); - EXPECT_STREQ(kHelperInstallerName, files[file_index++]); - EXPECT_STREQ(kOmahaOnDemandFileName, files[file_index++]); - EXPECT_STREQ(kOmahaMetainstallerFileName, files[file_index++]); - EXPECT_STREQ(kOmahaWebPluginFileName, files[file_index++]); - EXPECT_STREQ(kOmahaDllName, files[file_index++]); - EXPECT_STREQ(_T("goopdateres_am.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ar.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_bg.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_bn.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ca.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_cs.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_da.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_de.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_el.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_en-GB.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_en.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_es-419.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_es.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_et.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_fa.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_fi.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_fil.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_fr.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_gu.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_hi.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_hr.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_hu.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_id.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_is.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_it.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_iw.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ja.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_kn.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ko.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_lt.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_lv.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ml.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_mr.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ms.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_nl.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_no.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_pl.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_pt-BR.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_pt-PT.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ro.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ru.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_sk.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_sl.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_sr.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_sv.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_sw.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ta.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_te.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_th.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_tr.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_uk.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_ur.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_vi.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_zh-CN.dll"), files[file_index++]); - EXPECT_STREQ(_T("goopdateres_zh-TW.dll"), files[file_index++]); - EXPECT_STREQ(UPDATE_PLUGIN_FILENAME, files[file_index++]); - EXPECT_STREQ(kPSFileNameMachine, files[file_index++]); - EXPECT_STREQ(kPSFileNameMachine64, files[file_index++]); - EXPECT_STREQ(kPSFileNameUser, files[file_index++]); - EXPECT_STREQ(kPSFileNameUser64, files[file_index++]); + + std::set extra_files; + for (int i = file_index; i < kExpectedFilesReturnedByFindFiles; ++i) { + extra_files.insert(files[i]); + } + EXPECT_EQ( + extra_files.size(), + kExpectedFilesReturnedByFindFiles - kExtraFilesReturnedByFindFiles); + + EXPECT_NE( + extra_files.find(kCrashHandlerFileName), extra_files.end()); + EXPECT_NE( + extra_files.find(kCrashHandler64FileName), extra_files.end()); + EXPECT_NE( + extra_files.find(kOmahaShellFileName), extra_files.end()); + EXPECT_NE( + extra_files.find(kOmahaBrokerFileName), extra_files.end()); + EXPECT_NE( + extra_files.find(kOmahaCOMRegisterShell64), extra_files.end()); + EXPECT_NE( + extra_files.find(kOmahaCoreFileName), extra_files.end()); + EXPECT_NE( + extra_files.find(kOmahaOnDemandFileName), extra_files.end()); + EXPECT_NE( + extra_files.find(kOmahaDllName), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_am.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ar.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_bg.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_bn.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ca.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_cs.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_da.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_de.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_el.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_en-GB.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_en.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_es-419.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_es.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_et.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_fa.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_fi.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_fil.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_fr.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_gu.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_hi.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_hr.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_hu.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_id.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_is.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_it.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_iw.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ja.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_kn.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ko.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_lt.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_lv.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ml.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_mr.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ms.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_nl.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_no.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_pl.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_pt-BR.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_pt-PT.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ro.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ru.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_sk.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_sl.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_sr.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_sv.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_sw.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ta.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_te.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_th.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_tr.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_uk.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_ur.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_vi.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_zh-CN.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(_T("goopdateres_zh-TW.dll")), extra_files.end()); + EXPECT_NE( + extra_files.find(kPSFileNameMachine), extra_files.end()); + EXPECT_NE( + extra_files.find(kPSFileNameMachine64), extra_files.end()); + EXPECT_NE( + extra_files.find(kPSFileNameUser), extra_files.end()); + EXPECT_NE( + extra_files.find(kPSFileNameUser64), extra_files.end()); EXPECT_SUCCEEDED(DeleteDirectory(version_path)); } diff --git a/omaha/setup/setup_google_update.cc b/omaha/setup/setup_google_update.cc index 715ee434b..06b0701ee 100644 --- a/omaha/setup/setup_google_update.cc +++ b/omaha/setup/setup_google_update.cc @@ -15,9 +15,7 @@ #include "omaha/setup/setup_google_update.h" -#include #include -#include #include #include "base/basictypes.h" #include "omaha/base/app_util.h" @@ -173,8 +171,8 @@ HRESULT SetupGoogleUpdate::FinishInstall() { // Fall through for installs. Omaha will attempt to install using the // in-proc mode. Not installing the launch mechanisms does mean that Omaha - // will not be able to update itself or the product. But OneClick and - // Handoffs should continue to work. + // will not be able to update itself or the product. But Handoffs should + // continue to work. // // extra_code1_ contains the HRESULT from the Scheduled Task install // failure, but it is more useful to send the service install failure in the @@ -183,13 +181,6 @@ HRESULT SetupGoogleUpdate::FinishInstall() { extra_code1_ = hr; } - // Reset kRegValueIsMSIHelperRegistered so that the MSI helper is registered - // on the next UA run. - const TCHAR* key_name = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE; - VERIFY1(SUCCEEDED(RegKey::SetValue(key_name, - kRegValueIsMSIHelperRegistered, - static_cast(0)))); - hr = RegisterOrUnregisterCOMLocalServer(true); if (FAILED(hr)) { OPT_LOG(LW, (_T("[RegisterOrUnregisterCOMLocalServer failed][0x%x]"), hr)); @@ -199,13 +190,11 @@ HRESULT SetupGoogleUpdate::FinishInstall() { ASSERT1(SUCCEEDED(VerifyCOMLocalServerRegistration(is_machine_))); - // We would prefer to uninstall previous versions last, but the web plugin - // requires that the old plugin is uninstalled before installing the new one. - VERIFY1(SUCCEEDED(UninstallPreviousVersions())); + VERIFY_SUCCEEDED(UninstallPreviousVersions()); // Set the LastOSVersion to the currently installed OS version. This is used // by the core to determine when an OS upgrade has occurred. - VERIFY1(SUCCEEDED(app_registry_utils::SetLastOSVersion(is_machine_, NULL))); + VERIFY_SUCCEEDED(app_registry_utils::SetLastOSVersion(is_machine_, NULL)); // Writing this value indicates that this Omaha version was successfully // installed. This is an artifact of Omaha 2 when pv was set earlier in Setup. @@ -224,8 +213,8 @@ HRESULT SetupGoogleUpdate::FinishInstall() { // The LastCodeRedCheck value is cleaned up on every install/update of Omaha. const ConfigManager* cm = ConfigManager::Instance(); - VERIFY1(SUCCEEDED(RegKey::DeleteValue( - cm->registry_update(is_machine_), kRegValueLastCodeRedCheck))); + VERIFY_SUCCEEDED(RegKey::DeleteValue( + cm->registry_update(is_machine_), kRegValueLastCodeRedCheck)); return S_OK; } @@ -310,19 +299,19 @@ HRESULT SetupGoogleUpdate::InstallRegistryValues() { ASSERT1(false); omaha_name = kAppName; } - VERIFY1(SUCCEEDED(RegKey::SetValue(omaha_clients_key_path, + VERIFY_SUCCEEDED(RegKey::SetValue(omaha_clients_key_path, kRegValueAppName, - omaha_name))); + omaha_name)); // Set pv in ClientState for consistency. Optional, so ignore errors. const CString omaha_client_state_key_path = cm->registry_client_state_goopdate(is_machine_); - VERIFY1(SUCCEEDED(RegKey::SetValue(omaha_client_state_key_path, + VERIFY_SUCCEEDED(RegKey::SetValue(omaha_client_state_key_path, kRegValueProductVersion, - this_version_))); + this_version_)); if (is_machine_) { - VERIFY1(SUCCEEDED(goopdate_utils::EnableSEHOP(true))); + VERIFY_SUCCEEDED(goopdate_utils::EnableSEHOP(true)); } return S_OK; @@ -404,10 +393,10 @@ void SetupGoogleUpdate::UninstallLaunchMechanisms() { } else { // We only need to do this in case of the user goopdate, as // there is no machine Run at startup installation. - VERIFY1(SUCCEEDED(ConfigureUserRunAtStartup(false))); // delete entry + VERIFY_SUCCEEDED(ConfigureUserRunAtStartup(false)); // delete entry } - VERIFY1(SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(is_machine_))); + VERIFY_SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(is_machine_)); } HRESULT SetupGoogleUpdate::InstallScheduledTask() { @@ -511,10 +500,11 @@ HRESULT SetupGoogleUpdate::InstallUserLaunchMechanisms() { // Sets a value in the Run key in the user registry to start the core. HRESULT SetupGoogleUpdate::ConfigureUserRunAtStartup(bool install) { SETUP_LOG(L3, (_T("SetupGoogleUpdate::ConfigureUserRunAtStartup"))); - // Always send false argument as this method is only called for user - // goopdate installs. - CString core_cmd = BuildCoreProcessCommandLine(); - return ConfigureRunAtStartup(USER_KEY_NAME, kRunValueName, core_cmd, install); + + return ConfigureRunAtStartup(USER_KEY_NAME, + kRunValueName, + BuildCoreProcessCommandLine(), + install); } HRESULT SetupGoogleUpdate::RegisterOrUnregisterCOMLocalServer(bool reg) { @@ -533,115 +523,6 @@ HRESULT SetupGoogleUpdate::RegisterOrUnregisterCOMLocalServer(bool reg) { return S_OK; } -// Assumes that the MSI is in the current directory. -// To debug MSI failures, use the following statement: -// ::MsiEnableLog(INSTALLLOGMODE_VERBOSE, _T("C:\\msi.log"), NULL); -HRESULT SetupGoogleUpdate::InstallMsiHelper() { - SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallMsiHelper]"))); - if (!is_machine_) { - return S_OK; - } - - ++metric_setup_helper_msi_install_total; - HighresTimer metrics_timer; - - const CPath msi_path(BuildSupportFileInstallPath(kHelperInstallerName)); - ASSERT1(File::Exists(msi_path)); - - // Setting INSTALLUILEVEL_NONE causes installation to be silent and not - // create a restore point. - ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); - - // Try a normal install. - UINT res = ::MsiInstallProduct(msi_path, kMsiSuppressAllRebootsCmdLine); - if (ERROR_PRODUCT_VERSION == res) { - // The product may already be installed. Force a reinstall of everything. - SETUP_LOG(L3, (_T("[ERROR_PRODUCT_VERSION returned - forcing reinstall]"))); - CString force_install_cmd_line; - SafeCStringFormat(&force_install_cmd_line, - _T("REINSTALL=ALL REINSTALLMODE=vamus %s"), - kMsiSuppressAllRebootsCmdLine); - res = ::MsiInstallProduct(msi_path, force_install_cmd_line); - } - - HRESULT hr = HRESULT_FROM_WIN32(res); - if (FAILED(hr)) { - SETUP_LOG(L1, (_T("[MsiInstallProduct failed][0x%08x][%u]"), hr, res)); - return hr; - } - - metric_setup_helper_msi_install_ms.AddSample(metrics_timer.GetElapsedMs()); - ++metric_setup_helper_msi_install_succeeded; - return S_OK; -} - -// The MSI is uninstalled. -// TODO(omaha): Make sure this works after deleting the MSI. -HRESULT SetupGoogleUpdate::UninstallMsiHelper() { - SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallMsiHelper]"))); - if (!is_machine_) { - return S_OK; - } - - // Setting INSTALLUILEVEL_NONE causes installation to be silent and not - // create a restore point. - ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); - - // MSDN says that eInstallState must be INSTALLSTATE_DEFAULT in order for the - // command line to be used. Therefore, instead of using INSTALLSTATE_ABSENT - // to uninstall, we must pass REMOVE=ALL in the command line. - CString uninstall_cmd_line; - SafeCStringFormat(&uninstall_cmd_line, _T("REMOVE=ALL %s"), - kMsiSuppressAllRebootsCmdLine); - UINT res = ::MsiConfigureProductEx(kHelperInstallerProductGuid, - INSTALLLEVEL_DEFAULT, - INSTALLSTATE_DEFAULT, - uninstall_cmd_line); - - // Ignore the product not currently installed result. - if ((ERROR_SUCCESS != res) && (ERROR_UNKNOWN_PRODUCT != res)) { - HRESULT hr = HRESULT_FROM_WIN32(res); - if (FAILED(hr)) { - SETUP_LOG(L1, (_T("[MsiInstallProduct failed][0x%08x][%u]"), hr, res)); - return hr; - } - } - - return S_OK; -} - -HRESULT SetupGoogleUpdate::InstallBrowserPlugins() { - SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallBrowserPlugins]"))); - ASSERT1(have_called_uninstall_previous_versions_); - // Failure of registration of optional components is acceptable in release - // builds. - HRESULT hr = S_OK; - - CString plugin_path = - BuildSupportFileInstallPath(UPDATE_PLUGIN_FILENAME); - hr = RegisterDll(plugin_path); - if (FAILED(hr)) { - SETUP_LOG(L1, (_T("[Register plugin DLL failed][0x%08x]"), hr)); - } - - return hr; -} - -HRESULT SetupGoogleUpdate::UninstallBrowserPlugins() { - SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallBrowserPlugins]"))); - // Unregistration. Failure is acceptable in release builds. - HRESULT hr = S_OK; - - CString plugin_path = - BuildSupportFileInstallPath(UPDATE_PLUGIN_FILENAME); - hr = UnregisterDll(plugin_path); - if (FAILED(hr)) { - SETUP_LOG(L1, (_T("[Unregister plugin DLL failed][0x%08x]"), hr)); - } - - return hr; -} - CString SetupGoogleUpdate::BuildSupportFileInstallPath( const CString& filename) const { SETUP_LOG(L3, (_T("[SetupGoogleUpdate::BuildSupportFileInstallPath][%s]"), @@ -658,7 +539,9 @@ CString SetupGoogleUpdate::BuildCoreProcessCommandLine() const { CPath full_file_path(goopdate_utils::BuildInstallDirectory( is_machine_, GetVersionString())); VERIFY1(full_file_path.Append(kOmahaCoreFileName)); - return full_file_path; + CString core_command_line(full_file_path); + EnclosePath(&core_command_line); + return core_command_line; } HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { @@ -666,8 +549,8 @@ HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { have_called_uninstall_previous_versions_ = true; #endif - VERIFY1(SUCCEEDED( - scheduled_task_utils::UninstallLegacyGoopdateTasks(is_machine_))); + VERIFY_SUCCEEDED( + scheduled_task_utils::UninstallLegacyGoopdateTasks(is_machine_)); CString install_path( is_machine_ ? ConfigManager::Instance()->GetMachineGoopdateInstallDir() : @@ -695,14 +578,12 @@ HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { return HRESULT_FROM_WIN32(err); } - // The following subdirectories are not deleted here. - std::set excluded_directories = { - _T(".."), - _T("."), - this_version_, - DOWNLOAD_DIR_NAME, // Managed by DownloadManager::Initialize(). - INSTALL_WORKING_DIR_NAME, // Managed by InstallManager::InstallManager(). - }; + // The download and install directories are left alone here. They are cleaned + // up by DownloadManager::Initialize() and InstallManager::InstallManager(). + CPath download_dir(OMAHA_REL_DOWNLOAD_STORAGE_DIR); + download_dir.StripPath(); + CPath install_dir(OMAHA_REL_INSTALL_WORKING_DIR); + install_dir.StripPath(); BOOL found_next = TRUE; for (; found_next; found_next = ::FindNextFile(get(find_handle), @@ -714,7 +595,12 @@ HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { if (_tcsicmp(file_data.cFileName, kOmahaShellFileName)) { DeleteBeforeOrAfterReboot(file_or_directory); } - } else if (!excluded_directories.count(file_data.cFileName)) { + } else if (_tcscmp(file_data.cFileName, _T("..")) && + _tcscmp(file_data.cFileName, _T(".")) && + _tcsicmp(file_data.cFileName, this_version_) && + _tcsicmp(file_data.cFileName, download_dir) && + _tcsicmp(file_data.cFileName, install_dir) && + !(file_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { // Unregister the previous version OneClick if it exists. Ignore // failures. The file is named npGoogleOneClick*.dll. CPath old_oneclick(file_or_directory); @@ -725,7 +611,7 @@ HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { if (found_oneclick) { CPath old_oneclick_file(file_or_directory); VERIFY1(old_oneclick_file.Append(old_oneclick_file_data.cFileName)); - VERIFY1(SUCCEEDED(UnregisterDll(old_oneclick_file))); + VERIFY_SUCCEEDED(UnregisterDll(old_oneclick_file)); } // Unregister the previous version of the plugin if it exists. Ignore @@ -738,7 +624,7 @@ HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { if (found_plugin) { CPath old_plugin_file(file_or_directory); VERIFY1(old_plugin_file.Append(old_plugin_file_data.cFileName)); - VERIFY1(SUCCEEDED(UnregisterDll(old_plugin_file))); + VERIFY_SUCCEEDED(UnregisterDll(old_plugin_file)); } // Delete entire sub-directory. @@ -769,12 +655,6 @@ HRESULT SetupGoogleUpdate::UninstallPreviousVersions() { void SetupGoogleUpdate::Uninstall() { OPT_LOG(L1, (_T("[SetupGoogleUpdate::Uninstall]"))); - HRESULT hr = UninstallBrowserPlugins(); - if (FAILED(hr)) { - SETUP_LOG(LW, (_T("[UninstallBrowserPlugins failed][0x%08x]"), hr)); - ASSERT1(HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) == hr); - } - // If running from the installed location instead of a temporary location, // we assume that Omaha had been properly installed and can verify the COM // registration. @@ -782,7 +662,7 @@ void SetupGoogleUpdate::Uninstall() { ASSERT1(SUCCEEDED(VerifyCOMLocalServerRegistration(is_machine_))); } - hr = RegisterOrUnregisterCOMLocalServer(false); + HRESULT hr = RegisterOrUnregisterCOMLocalServer(false); if (FAILED(hr)) { SETUP_LOG(LW, (_T("[RegisterOrUnregisterCOMLocalServer failed][0x%08x]"), hr)); @@ -790,12 +670,6 @@ void SetupGoogleUpdate::Uninstall() { HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr); } - hr = UninstallMsiHelper(); - if (FAILED(hr)) { - SETUP_LOG(L1, (_T("[UninstallMsiHelper failed][0x%08x]"), hr)); - ASSERT1(HRESULT_FROM_WIN32(ERROR_INSTALL_SERVICE_FAILURE) == hr); - } - UninstallLaunchMechanisms(); // Remove everything under top level Google Update registry key. @@ -808,7 +682,7 @@ HRESULT SetupGoogleUpdate::DeleteRegistryKeys() { OPT_LOG(L3, (_T("[SetupGoogleUpdate::DeleteRegistryKeys]"))); if (is_machine_) { - VERIFY1(SUCCEEDED(goopdate_utils::EnableSEHOP(false))); + VERIFY_SUCCEEDED(goopdate_utils::EnableSEHOP(false)); } CString root_key = ConfigManager::Instance()->registry_update(is_machine_); @@ -834,7 +708,7 @@ HRESULT SetupGoogleUpdate::DeleteRegistryKeys() { ASSERT1(num_keys == static_cast(sub_keys.size())); // Delete all the sub keys of the root key. for (int i = 0; i < num_keys; ++i) { - VERIFY1(SUCCEEDED(root.RecurseDeleteSubKey(sub_keys[i]))); + VERIFY_SUCCEEDED(root.RecurseDeleteSubKey(sub_keys[i])); } // Now delete all the values of the root key. @@ -862,11 +736,11 @@ HRESULT SetupGoogleUpdate::DeleteRegistryKeys() { } for (size_t i = 0; i < value_names.size(); ++i) { - VERIFY1(SUCCEEDED(root.DeleteValue(value_names[i]))); + VERIFY_SUCCEEDED(root.DeleteValue(value_names[i])); } if (0 == root.GetValueCount() && 0 == root.GetSubkeyCount()) { - VERIFY1(SUCCEEDED(RegKey::DeleteKey(root_key, false))); + VERIFY_SUCCEEDED(RegKey::DeleteKey(root_key, false)); } return S_OK; diff --git a/omaha/setup/setup_google_update.h b/omaha/setup/setup_google_update.h index 2daaa0553..903b0f5a4 100644 --- a/omaha/setup/setup_google_update.h +++ b/omaha/setup/setup_google_update.h @@ -30,11 +30,6 @@ class SetupGoogleUpdate { HRESULT FinishInstall(); - HRESULT InstallBrowserPlugins(); - - // Installs the helper (MSI). - HRESULT InstallMsiHelper(); - // Uninstalls Google Update registrations created by FinishInstall(). void Uninstall(); @@ -80,11 +75,6 @@ class SetupGoogleUpdate { // Register COM classes and interfaces. HRESULT RegisterOrUnregisterCOMLocalServer(bool register_server); - // Uninstalls the helper (MSI). - HRESULT UninstallMsiHelper(); - - HRESULT UninstallBrowserPlugins(); - // Build the install file path for support files. For example, CString BuildSupportFileInstallPath(const CString& filename) const; diff --git a/omaha/setup/setup_google_update_unittest.cc b/omaha/setup/setup_google_update_unittest.cc index 8d370c2ce..af61c12f6 100644 --- a/omaha/setup/setup_google_update_unittest.cc +++ b/omaha/setup/setup_google_update_unittest.cc @@ -14,7 +14,6 @@ // ======================================================================== #include -#include #include #include "omaha/base/app_util.h" #include "omaha/base/atlregmapex.h" @@ -33,7 +32,6 @@ #include "omaha/common/const_cmd_line.h" #include "omaha/common/const_goopdate.h" #include "omaha/common/scheduled_task_utils.h" -#include "omaha/setup/msi_test_utils.h" #include "omaha/setup/setup_google_update.h" #include "omaha/testing/unit_test.h" #include "omaha/third_party/smartany/scoped_any.h" @@ -42,21 +40,12 @@ namespace omaha { namespace { -const TCHAR kMsiInstallRegValue[] = _T("MsiStubRun"); -const TCHAR kMsiUninstallParentKey[] = - _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\") - _T("Uninstall\\"); const TCHAR kRunKey[] = _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"); const TCHAR* const kAppId1 = _T("{B7E61EF9-AAE5-4cdf-A2D3-E4C8DF975145}"); const TCHAR* const kAppId2 = _T("{35F1A986-417D-4039-8718-781DD418232A}"); -CString GetMsiUninstallKey() { - CString msi_uninstall_parent_key(kMsiUninstallParentKey); - return msi_uninstall_parent_key + kHelperInstallerProductGuid; -} - // Copies the shell and DLLs that FinishInstall needs. // Does not replace files if they already exist. void CopyFilesRequiredByFinishInstall(bool is_machine, const CString& version) { @@ -75,14 +64,12 @@ void CopyFilesRequiredByFinishInstall(bool is_machine, const CString& version) { ASSERT_SUCCEEDED(CreateDir(version_path, NULL)); - const TCHAR* files[] = {kHelperInstallerName, - kOmahaDllName, + const TCHAR* files[] = {kOmahaDllName, kOmahaCOMRegisterShell64, kPSFileNameMachine, kPSFileNameMachine64, kPSFileNameUser, - kPSFileNameUser64, - UPDATE_PLUGIN_FILENAME}; + kPSFileNameUser64}; for (size_t i = 0; i < arraysize(files); ++i) { ASSERT_SUCCEEDED(File::Copy( ConcatenatePath(app_util::GetCurrentModuleDirectory(), @@ -95,8 +82,8 @@ void CopyFilesRequiredByFinishInstall(bool is_machine, const CString& version) { // RegisterOrUnregisterCOMLocalServer() called from FinishInstall() runs in a // separate process. When using registry redirection in the test, the new // process writes to the real registry, so the unit test fails. This function -// creates dummy entries that VerifyCOMLocalServerRegistration() verifies, and -// is happy about. +// creates test entries that VerifyCOMLocalServerRegistration() verifies, and is +// happy about. void SetupCOMLocalServerRegistration(bool is_machine) { // Setup the following for the unit test: // * LocalServer32 under CLSID_OnDemandMachineAppsClass or @@ -288,14 +275,6 @@ class SetupGoogleUpdateTest : public testing::Test { return setup_google_update_->CreateClientStateMedium(); } - HRESULT InstallMsiHelper() { - return setup_google_update_->InstallMsiHelper(); - } - - HRESULT UninstallMsiHelper() { - return setup_google_update_->UninstallMsiHelper(); - } - bool is_machine_; std::unique_ptr setup_google_update_; }; @@ -303,10 +282,11 @@ class SetupGoogleUpdateTest : public testing::Test { class SetupGoogleUpdateUserTest : public SetupGoogleUpdateTest { protected: SetupGoogleUpdateUserTest() : SetupGoogleUpdateTest(false) { - CString expected_shell_path = - ConcatenatePath(GetGoogleUpdateUserPath(), GetVersionString()); - expected_run_key_value_ = ConcatenatePath(expected_shell_path, - kOmahaCoreFileName); + CString expected_core_command_line = ConcatenatePath( + ConcatenatePath(GetGoogleUpdateUserPath(), GetVersionString()), + kOmahaCoreFileName); + EnclosePath(&expected_core_command_line); + expected_run_key_value_ = expected_core_command_line; } void SetUp() override { @@ -496,9 +476,9 @@ TEST_F(SetupGoogleUpdateMachineTest, InstallRegistryValues) { CString expected_shell_path; EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &expected_shell_path)); - expected_shell_path.Append(_T("\\") SHORT_COMPANY_NAME + expected_shell_path.Append(_T("\\") PATH_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\GoogleUpdate.exe")); + _T("\\") MAIN_EXE_BASE_NAME _T(".exe")); CString shell_path; EXPECT_SUCCEEDED( RegKey::GetValue(MACHINE_REG_UPDATE, _T("path"), &shell_path)); @@ -644,124 +624,6 @@ TEST_F(SetupGoogleUpdateUserTest, InstallLaunchMechanisms_RunKeyDoesNotExist) { EXPECT_FALSE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false)); } -// The helper can be installed when the test begins. -// It will not be installed when the test successfully completes. -TEST_F(SetupGoogleUpdateMachineTest, InstallAndUninstallMsiHelper) { - const TCHAR* MsiInstallRegValueKey = - ConfigManager::Instance()->machine_registry_update(); - - CopyFilesRequiredByFinishInstall(is_machine_, GetVersionString()); - - if (vista_util::IsUserAdmin()) { - // Prepare for the test - make sure the helper isn't installed. - EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper()); - - RegKey::DeleteValue(MsiInstallRegValueKey, kMsiInstallRegValue); - RegKey::DeleteKey(GetMsiUninstallKey()); - - // Verify installation. - DWORD reg_value = 0xffffffff; - EXPECT_HRESULT_SUCCEEDED(InstallMsiHelper()); - EXPECT_TRUE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(MsiInstallRegValueKey, - kMsiInstallRegValue, - ®_value)); - EXPECT_EQ(0, reg_value); - EXPECT_TRUE(RegKey::HasKey(GetMsiUninstallKey())); - - // Verify over-install. - EXPECT_HRESULT_SUCCEEDED(InstallMsiHelper()); - EXPECT_TRUE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(MsiInstallRegValueKey, - kMsiInstallRegValue, - ®_value)); - EXPECT_EQ(0, reg_value); - EXPECT_TRUE(RegKey::HasKey(GetMsiUninstallKey())); - - // Verify uninstall. - EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper()); - EXPECT_FALSE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_FALSE(RegKey::HasKey(GetMsiUninstallKey())); - - // Verify uninstall when not currently installed. - EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper()); - } else { - { - // This method expects to be called elevated and makes an assumption - // about a return value. - ExpectAsserts expect_asserts; - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INSTALL_PACKAGE_REJECTED), - InstallMsiHelper()); - } - if (IsMsiHelperInstalled()) { - // If the MSI is installed UninstallMsiHelper returns - // ERROR_INSTALL_FAILURE. - EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE), - UninstallMsiHelper()); - } else { - // If the MSI is not installed UninstallMsiHelper returns S_OK. - EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper()); - } - } -} - -// This test installs a different build of the helper installer then calls -// InstallMsiHelper to install the one that has been built. -// If run without the REINSTALL property, ERROR_PRODUCT_VERSION would occur. -// This test verifies that such overinstalls are correctly handled and that the -// registry value is correctly changed. -// Note: The name of the installer cannot be different. MSI tries to find the -// original filename in the new directory. -// The helper can be installed when the test begins. -// It will not be installed when the test successfully completes. -TEST_F(SetupGoogleUpdateMachineTest, - InstallMsiHelper_OverinstallDifferentMsiBuild) { - if (!vista_util::IsUserAdmin()) { - std::wcout << _T("\tThis test did not run because it must be run as admin.") - << std::endl; - return; - } - - const TCHAR kDifferentMsi[] = - _T("unittest_support\\GoogleUpdateHelper.msi"); - const TCHAR* MsiInstallRegValueKey = - ConfigManager::Instance()->machine_registry_update(); - - CopyFilesRequiredByFinishInstall(is_machine_, GetVersionString()); - - CString different_msi_path(app_util::GetCurrentModuleDirectory()); - ASSERT_TRUE(::PathAppend(CStrBuf(different_msi_path, MAX_PATH), - kDifferentMsi)); - - // Prepare for the test - make sure the helper is not installed. - EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper()); - EXPECT_FALSE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_FALSE(RegKey::HasKey(GetMsiUninstallKey())); - - // Install an older version of the MSI. - DWORD reg_value = 0xffffffff; - ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); - EXPECT_EQ(ERROR_SUCCESS, ::MsiInstallProduct(different_msi_path, _T(""))); - EXPECT_TRUE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_HRESULT_SUCCEEDED( - RegKey::GetValue(MsiInstallRegValueKey, kMsiInstallRegValue, ®_value)); - EXPECT_EQ(9, reg_value); - EXPECT_TRUE(RegKey::HasKey(GetMsiUninstallKey())); - - // Over-install. - EXPECT_HRESULT_SUCCEEDED(InstallMsiHelper()); - EXPECT_TRUE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_HRESULT_SUCCEEDED( - RegKey::GetValue(MsiInstallRegValueKey, kMsiInstallRegValue, ®_value)); - EXPECT_EQ(0, reg_value); - EXPECT_TRUE(RegKey::HasKey(GetMsiUninstallKey())); - - // Clean up. - EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper()); - EXPECT_FALSE(RegKey::HasValue(MsiInstallRegValueKey, kMsiInstallRegValue)); - EXPECT_FALSE(RegKey::HasKey(GetMsiUninstallKey())); -} - TEST_F(SetupGoogleUpdateMachineTest, WriteClientStateMedium) { bool is_uac_on(false); EXPECT_SUCCEEDED(vista_util::IsUACOn(&is_uac_on)); diff --git a/omaha/setup/setup_service.h b/omaha/setup/setup_service.h index 11866d0e3..268f505f7 100644 --- a/omaha/setup/setup_service.h +++ b/omaha/setup/setup_service.h @@ -114,8 +114,8 @@ class SetupService { return hr; } - VERIFY1(SUCCEEDED(SetDescription(GetServiceDescription()))); - VERIFY1(SUCCEEDED(SetDelayedAutoStart())); + VERIFY_SUCCEEDED(SetDescription(GetServiceDescription())); + VERIFY_SUCCEEDED(SetDelayedAutoStart()); return InstallCOMService(); } @@ -132,7 +132,7 @@ class SetupService { ASSERT1(HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr); } - VERIFY1(SUCCEEDED(UninstallCOMService())); + VERIFY_SUCCEEDED(UninstallCOMService()); hr = DeleteService(); if (FAILED(hr)) { @@ -187,8 +187,8 @@ class SetupService { // Delete the previous version of the service. Then create a new service // name, and fall through to install that. - VERIFY1(SUCCEEDED(DeleteService())); - VERIFY1(SUCCEEDED(CreateAndSetVersionedServiceNameInRegistry())); + VERIFY_SUCCEEDED(DeleteService()); + VERIFY_SUCCEEDED(CreateAndSetVersionedServiceNameInRegistry()); ASSERT1(!IsServiceInstalled()); } diff --git a/omaha/setup/setup_unittest.cc b/omaha/setup/setup_unittest.cc index 7326b1b0f..16648ccd1 100644 --- a/omaha/setup/setup_unittest.cc +++ b/omaha/setup/setup_unittest.cc @@ -48,27 +48,6 @@ const int kProcessesCleanupWait = 30000; const TCHAR* const kFutureVersionString = _T("9.8.7.6"); -const TCHAR* const kAppMachineClientsPath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\Clients\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\"); -const TCHAR* const kAppMachineClientStatePath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\"); -const TCHAR* const kApp2MachineClientsPath = - _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\Clients\\{CB8E8A3C-7295-4529-B083-D5F76DCD4CC2}\\"); - -const TCHAR* const kAppUserClientsPath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\Clients\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\"); -const TCHAR* const kAppUserClientStatePath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\"); -const TCHAR* const kApp2UserClientsPath = - _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME - _T("\\Clients\\{CB8E8A3C-7295-4529-B083-D5F76DCD4CC2}\\"); - - class HoldLock : public Runnable { public: explicit HoldLock(bool is_machine) @@ -154,7 +133,7 @@ class SetupTest : public testing::Test { not_listening_exe_opposite_path_(!is_machine ? not_listening_machine_exe_path_ : not_listening_user_exe_path_) { - omaha_exe_path_ = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe")); + omaha_exe_path_ = ConcatenatePath(omaha_path_, MAIN_EXE_BASE_NAME _T(".exe")); } virtual void SetUp() { @@ -185,7 +164,8 @@ class SetupTest : public testing::Test { thread.Start(&hold_lock); hold_lock.WaitForLockToBeAcquired(); - EXPECT_EQ(GOOPDATE_E_FAILED_TO_GET_LOCK, setup_->Install(false)); + EXPECT_EQ(GOOPDATE_E_FAILED_TO_GET_LOCK, + setup_->Install(RUNTIME_MODE_NOT_SET)); hold_lock.Stop(); thread.WaitTillExit(1000); @@ -562,7 +542,7 @@ class SetupTest : public testing::Test { kProcessesCleanupWait)); } - // Starts dummy process that doesn't exit and assigns it to the specified job. + // Starts test process that doesn't exit and assigns it to the specified job. // The handle returned by ShellExecute does not have PROCESS_SET_QUOTA access // rights, so we get a new handle with the correct access rights to return to // the caller. @@ -619,42 +599,46 @@ class SetupTest : public testing::Test { _T("Last Error: ") << ::GetLastError() << std::endl; } - void TestShouldDelayUninstall() { - EXPECT_FALSE(setup_->ShouldDelayUninstall()); + void TestGetRuntimeMode() { + EXPECT_EQ(RUNTIME_MODE_NOT_SET, setup_->GetRuntimeMode()); const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_); - DWORD value = 1; EXPECT_SUCCEEDED(RegKey::SetValue(key, - kRegValueDelayOmahaUninstall, - value)); - EXPECT_TRUE(setup_->ShouldDelayUninstall()); + kRegValueRuntimeMode, + static_cast(RUNTIME_MODE_TRUE))); + EXPECT_EQ(RUNTIME_MODE_TRUE, setup_->GetRuntimeMode()); + + EXPECT_SUCCEEDED(RegKey::SetValue( + key, kRegValueRuntimeMode, static_cast(RUNTIME_MODE_PERSIST))); + EXPECT_EQ(RUNTIME_MODE_PERSIST, setup_->GetRuntimeMode()); - value = 0; EXPECT_SUCCEEDED(RegKey::SetValue(key, - kRegValueDelayOmahaUninstall, - value)); - EXPECT_FALSE(setup_->ShouldDelayUninstall()); + kRegValueRuntimeMode, + static_cast(RUNTIME_MODE_FALSE))); + EXPECT_EQ(RUNTIME_MODE_NOT_SET, setup_->GetRuntimeMode()); + + EXPECT_SUCCEEDED(RegKey::SetValue(key, kRegValueRuntimeMode, 999UL)); + EXPECT_EQ(RUNTIME_MODE_NOT_SET, setup_->GetRuntimeMode()); - EXPECT_SUCCEEDED(RegKey::DeleteValue(key, - kRegValueDelayOmahaUninstall)); - EXPECT_FALSE(setup_->ShouldDelayUninstall()); + EXPECT_SUCCEEDED(RegKey::DeleteValue(key, kRegValueRuntimeMode)); + EXPECT_EQ(RUNTIME_MODE_NOT_SET, setup_->GetRuntimeMode()); } - void TestSetDelayUninstall() { - EXPECT_FALSE(setup_->ShouldDelayUninstall()); + void TestSetRuntimeMode() { + EXPECT_EQ(RUNTIME_MODE_NOT_SET, setup_->GetRuntimeMode()); - EXPECT_SUCCEEDED(setup_->SetDelayUninstall(true)); - EXPECT_TRUE(setup_->ShouldDelayUninstall()); + EXPECT_SUCCEEDED(setup_->SetRuntimeMode(RUNTIME_MODE_TRUE)); + EXPECT_EQ(RUNTIME_MODE_TRUE, setup_->GetRuntimeMode()); - EXPECT_SUCCEEDED(setup_->SetDelayUninstall(true)); - EXPECT_TRUE(setup_->ShouldDelayUninstall()); + EXPECT_SUCCEEDED(setup_->SetRuntimeMode(RUNTIME_MODE_PERSIST)); + EXPECT_EQ(RUNTIME_MODE_PERSIST, setup_->GetRuntimeMode()); - EXPECT_SUCCEEDED(setup_->SetDelayUninstall(false)); - EXPECT_FALSE(setup_->ShouldDelayUninstall()); + EXPECT_SUCCEEDED(setup_->SetRuntimeMode(RUNTIME_MODE_NOT_SET)); + EXPECT_EQ(RUNTIME_MODE_PERSIST, setup_->GetRuntimeMode()); - EXPECT_SUCCEEDED(setup_->SetDelayUninstall(false)); - EXPECT_FALSE(setup_->ShouldDelayUninstall()); + EXPECT_SUCCEEDED(setup_->SetRuntimeMode(RUNTIME_MODE_FALSE)); + EXPECT_EQ(RUNTIME_MODE_NOT_SET, setup_->GetRuntimeMode()); } const bool is_machine_; @@ -815,7 +799,7 @@ TEST_F(SetupFutureVersionInstalledUserTest, DISABLED_Install_NoRunKey) { ASSERT_FALSE(File::Exists(dll_path)); EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), - setup_->Install(false)); + setup_->Install(RUNTIME_MODE_NOT_SET)); EXPECT_EQ(0, setup_->extra_code1()); RestoreRegistryHives(); @@ -843,7 +827,7 @@ TEST_F(SetupFutureVersionInstalledUserTest, Install_ValidRunKey) { ASSERT_SUCCEEDED(File::Remove(dll_path)); ASSERT_FALSE(File::Exists(dll_path)); - EXPECT_SUCCEEDED(setup_->Install(false)); + EXPECT_SUCCEEDED(setup_->Install(RUNTIME_MODE_NOT_SET)); EXPECT_EQ(0, setup_->extra_code1()); RestoreRegistryHives(); @@ -1043,7 +1027,7 @@ TEST_F(SetupRegistryProtectedUserTest, CopyGoopdateFiles(omaha_path_, this_version_); CString path = ConcatenatePath(ConcatenatePath(omaha_path_, this_version_), - UPDATE_PLUGIN_FILENAME); + kPSFileNameMachine64); ASSERT_SUCCEEDED(File::Remove(path)); ASSERT_FALSE(File::Exists(path)); @@ -1068,23 +1052,23 @@ TEST_F(SetupRegistryProtectedUserTest, ShouldInstall_SameVersionShellMissing) { } // -// ShouldDelayUninstall/SetDelayUninstall tests. +// GetRuntimeMode/SetRuntimeMode tests. // -TEST_F(SetupRegistryProtectedUserTest, ShouldDelayUninstall) { - TestShouldDelayUninstall(); +TEST_F(SetupRegistryProtectedUserTest, GetRuntimeMode) { + TestGetRuntimeMode(); } -TEST_F(SetupRegistryProtectedUserTest, SetDelayUninstall) { - TestSetDelayUninstall(); +TEST_F(SetupRegistryProtectedUserTest, SetRuntimeMode) { + TestSetRuntimeMode(); } -TEST_F(SetupRegistryProtectedMachineTest, ShouldDelayUninstall) { - TestShouldDelayUninstall(); +TEST_F(SetupRegistryProtectedMachineTest, GetRuntimeMode) { + TestGetRuntimeMode(); } -TEST_F(SetupRegistryProtectedMachineTest, SetDelayUninstall) { - TestSetDelayUninstall(); +TEST_F(SetupRegistryProtectedMachineTest, SetRuntimeMode) { + TestSetRuntimeMode(); } // diff --git a/omaha/site_scons/site_tools/atlmfc_vc16_0.py b/omaha/site_scons/site_tools/atlmfc_vc16_0.py index fffcdabd1..3dbd7ef72 100644 --- a/omaha/site_scons/site_tools/atlmfc_vc16_0.py +++ b/omaha/site_scons/site_tools/atlmfc_vc16_0.py @@ -13,7 +13,7 @@ # limitations under the License. # ======================================================================== -# Windows ATL MFC for VC15 (Visual Studio 2017) tool for SCons. +# Windows ATL MFC for VC16 (Visual Studio 2019) tool for SCons. import os diff --git a/omaha/site_scons/site_tools/atlmfc_vc17_0.py b/omaha/site_scons/site_tools/atlmfc_vc17_0.py new file mode 100644 index 000000000..0a3256173 --- /dev/null +++ b/omaha/site_scons/site_tools/atlmfc_vc17_0.py @@ -0,0 +1,42 @@ +# Copyright 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +# Windows ATL MFC for VC16 (Visual Studio 2019) tool for SCons. + +import os + + +def _FindLocalInstall(): + """Returns the directory containing the local install of the tool. + + Returns: + Path to tool (as a string), or None if not found. + """ + default_dir = os.environ['VCToolsInstallDir'] + 'atlmfc' + if os.path.exists(default_dir): + return default_dir + else: + return None + + +def generate(env): + # NOTE: SCons requires the use of this name, which fails gpylint. + """SCons entry point for this tool.""" + + if not env.get('ATLMFC_VC17_0_DIR'): + env['ATLMFC_VC17_0_DIR'] = _FindLocalInstall() + + env.AppendENVPath('INCLUDE', env.Dir('$ATLMFC_VC17_0_DIR/include').abspath) + env.AppendENVPath('LIB', env.Dir('$ATLMFC_VC17_0_DIR/lib/x86').abspath) diff --git a/omaha/site_scons/site_tools/atlmfc_vc17_amd64.py b/omaha/site_scons/site_tools/atlmfc_vc17_amd64.py new file mode 100644 index 000000000..0b0a9c286 --- /dev/null +++ b/omaha/site_scons/site_tools/atlmfc_vc17_amd64.py @@ -0,0 +1,42 @@ +# Copyright 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +# Windows ATL MFC for VC15 (Visual Studio 2017) tool for SCons. + +import os + + +def _FindLocalInstall(): + """Returns the directory containing the local install of the tool. + + Returns: + Path to tool (as a string), or None if not found. + """ + default_dir = os.environ['VCToolsInstallDir'] + 'atlmfc' + if os.path.exists(default_dir): + return default_dir + else: + return None + + +def generate(env): + # NOTE: SCons requires the use of this name, which fails gpylint. + """SCons entry point for this tool.""" + + if not env.get('ATLMFC_VC17_0_DIR'): + env['ATLMFC_VC17_0_DIR'] = _FindLocalInstall() + + env.AppendENVPath('INCLUDE', env.Dir('$ATLMFC_VC17_0_DIR/include').abspath) + env.AppendENVPath('LIB', env.Dir('$ATLMFC_VC17_0_DIR/lib/x64').abspath) diff --git a/omaha/site_scons/site_tools/code_signing.py b/omaha/site_scons/site_tools/code_signing.py index 800bc12c1..8465c892e 100644 --- a/omaha/site_scons/site_tools/code_signing.py +++ b/omaha/site_scons/site_tools/code_signing.py @@ -59,11 +59,11 @@ def generate(env): # No certificate password by default. CERTIFICATE_PASSWORD='', # The default timestamp server. - TIMESTAMP_SERVER='http://timestamp.verisign.com/scripts/timestamp.dll', + TIMESTAMP_SERVER='http://timestamp.digicert.com', # The default timestamp server when dual-signing. - SHA1_TIMESTAMP_SERVER='http://timestamp.comodoca.com/authenticode', + SHA1_TIMESTAMP_SERVER='http://timestamp.digicert.com', # The default timestamp server for sha256 timestamps. - SHA2_TIMESTAMP_SERVER='http://timestamp.comodoca.com/rfc3161', + SHA2_TIMESTAMP_SERVER='http://timestamp.digicert.com', # The default certificate store. CERTIFICATE_STORE='my', # Set the certificate name from the command line. @@ -109,7 +109,7 @@ def SignedBinaryGenerator(source, target, env, for_signature): # Only do signing if there is a certificate file or certificate name. if env.subst('$CERTIFICATE_PATH') or env.subst('$CERTIFICATE_NAME'): # The command used to do signing (target added on below). - signing_cmd = '$SIGNTOOL sign ' + signing_cmd = '$SIGNTOOL sign /fd sha1' # Add in certificate file if any. if env.subst('$CERTIFICATE_PATH'): signing_cmd += ' /f "$CERTIFICATE_PATH"' @@ -161,6 +161,7 @@ def DualSignedBinaryGenerator(source, target, env, for_signature): # /t http://timestamp.globalsign.com/scripts/timstamp.dll # /i "Verisign" someFile.exe sha1_signing_cmd = base_signing_cmd + sha1_signing_cmd += ' /fd sha1' # Add in certificate file if any. if env.subst('$SHA1_CERTIFICATE_PATH'): sha1_signing_cmd += ' /f "$SHA1_CERTIFICATE_PATH"' diff --git a/omaha/site_scons/site_tools/omaha_builders.py b/omaha/site_scons/site_tools/omaha_builders.py index 316815621..8d66fdade 100644 --- a/omaha/site_scons/site_tools/omaha_builders.py +++ b/omaha/site_scons/site_tools/omaha_builders.py @@ -17,9 +17,13 @@ """Omaha builders tool for SCons.""" +import binascii from copy import copy from copy import deepcopy +import enterprise.installer.utils as ei_utils +import enterprise.installer.build_enterprise_installer as build_enterprise_installer import os.path +import struct import SCons.Action import SCons.Builder import SCons.Tool @@ -27,65 +31,83 @@ import omaha_version_utils -def SignDotNetManifest(env, target, unsigned_manifest): - """Signs a .NET manifest. + +def OmahaCertificateTag(env, target, source): + """Adds a superfluous certificate with a magic signature to an EXE or MSI. + + The file must be signed with Authenticode in order for Certificate Tagging to + succeed. Args: env: The environment. - target: Name of signed manifest. - unsigned_manifest: Unsigned manifest. + target: Name of the certificate-tagged file. + source: Name of the file to be certificate-tagged. Returns: Output node list from env.Command(). """ - mage_sign_path = ('python $MAIN_DIR/tools/retry.py 10 15 %s/%s' % - (os.getenv('OMAHA_NETFX_TOOLS_DIR'), 'mage.exe -Sign')) - sign_manifest_cmd = (mage_sign_path + - ' $SOURCE -ToFile $TARGET -TimestampUri ' + - '$TIMESTAMP_SERVER ') - - if env.Bit('build_server'): - # If signing fails with the following error, the hash may not match any - # certificates: "Internal error, please try again. Object reference not set - # to an instance of an object." - sign_manifest_cmd += ('-CertHash ' + env['CERTIFICATE_HASH']) - else: - sign_manifest_cmd += '-CertFile %s -Password %s' % ( - env.GetOption('authenticode_file'), - env.GetOption('authenticode_password')) - - signed_manifest = env.Command( + + certificate_tag = ('"' + env['ENV']['GOROOT'] + '/bin/go.exe' + '"' + + ' run ' + + '"$MAIN_DIR/../common/certificate_tag/certificate_tag.go"') + magic_bytes = 'Gact2.0Omaha' + padded_length = len(magic_bytes) + 2 + 8192 + certificate_tag_cmd = env.Command( target=target, - source=unsigned_manifest, - action=sign_manifest_cmd + source=source, + action=certificate_tag + ' -set-superfluous-cert-tag=' + magic_bytes + + ' -padded-length=' + str(padded_length) + ' -out $TARGET $SOURCE', ) - return signed_manifest + return certificate_tag_cmd + +def OmahaCertificateTagForTesting(env, + target, + source, + magic_bytes=None, + tag='', + tag_length=None): + """Adds a superfluous certificate with a magic signature to an EXE or MSI. -def OmahaCertificateTagExe(env, target, source): - """Adds a superfluous certificate with a magic signature to an EXE. The file - must be signed with Authenticode in order for Certificate Tagging to succeed. + The file must be signed with Authenticode in order for Certificate Tagging to + succeed. + This function allows caller to overwrite some parts of the tag with invalid + values for testing purpose. Args: env: The environment. target: Name of the certificate-tagged file. source: Name of the file to be certificate-tagged. + magic_bytes: Optional customized magic bytes. + tag: Optional tag value. + tag_length: Optional tag length (only last two bytes are accountable). Returns: Output node list from env.Command(). """ certificate_tag = ('"' + env['ENV']['GOROOT'] + '/bin/go.exe' + '"' + - ' run $MAIN_DIR/../common/certificate_tag/certificate_tag.go') - magic_bytes = 'Gact2.0Omaha' - padded_length = len(magic_bytes) + 2 + 8192 + ' run ' + + '"$MAIN_DIR/../common/certificate_tag/certificate_tag.go"') + if magic_bytes is None: + magic_bytes = 'Gact2.0Omaha' + if tag_length is None: + tag_length = len(tag) + if tag_length > 0xFFFF: + raise ValueError('Input tag is too long') + + bin_tag = bytearray(binascii.hexlify(magic_bytes.encode())) + bin_tag.extend(binascii.hexlify(struct.pack('>H', tag_length))) + bin_tag.extend(binascii.hexlify(tag.encode())) + full_tag_encoded = '0x' + bin_tag.decode() + padded_length = len(bin_tag) + 8192 certificate_tag_cmd = env.Command( target=target, source=source, - action=certificate_tag + - ' -set-superfluous-cert-tag=' + magic_bytes + - ' -padded-length=' + str(padded_length) + ' -out $TARGET $SOURCE', + action=certificate_tag + ' -set-superfluous-cert-tag=' + + full_tag_encoded + ' -padded-length=' + str(padded_length) + + ' -out $TARGET $SOURCE', ) return certificate_tag_cmd @@ -107,12 +129,132 @@ def OmahaTagExe(env, target, source, tag): tag_cmd = env.Command( target=target, source=source, - action=tag_exe + ' $SOURCES $TARGET ' + - '%s append' % tag, + action='"%s" $SOURCES $TARGET %s append' % (tag_exe, tag), ) return tag_cmd +def OmahaBuildTestExe(env, version, major, minor, build, patch): + """Builds e.g. test_foo_v1_0_101_1.exe + + Returns: + Output node list from env.Command(). + """ + exe_env = env.Clone() + exe_env.Append( + LIBS = [ + exe_env['crt_libs'][exe_env.Bit('debug')], + 'version.lib', + ], + RCFLAGS = [ + '-DVERSION_STRING=%s' % version, + '-DMAJOR=%s' % major, + '-DMINOR=%s' % minor, + '-DBUILD=%s' % build, + '-DPATCH=%s' % patch + ], + ) + exe_env['OBJPREFIX'] = '%s%s/' % (exe_env['OBJPREFIX'], version) + base_name = 'test_foo_v%s' % version.replace('.', '_') + signed_target_name = base_name + '.exe' + target_name = base_name + '_unsigned' + unsigned_exe = exe_env.ComponentTestProgram( + prog_name=target_name, + source=[ + '$OBJ_ROOT/testing/test_foo.cc', + exe_env.RES('$OBJ_ROOT/testing/test_foo_v%s.res' % version, 'test_foo.rc'), + ], + COMPONENT_TEST_RUNNABLE=False + ) + signed_output = exe_env.SignedBinary( + target=signed_target_name, + source=unsigned_exe, + ) + return signed_output + +def OmahaBuildTestMsi(env, version, namespace, exe_name, wxs_template, msi_base_name, prefix=''): + """Builds a test MSI from an exe. + + Returns: + Output node list from env.Command(). + """ + msi_base_name = prefix + msi_base_name + # Have to use 'copy' here because we are renaming the file, and it is being + # renamed to match the final msi name to avoid collisions in the wixobj files. + copy_target = env.Command( + target=msi_base_name + '.wxs', + source=wxs_template, + action='@copy /y $SOURCE $TARGET', + ) + PRODUCT_GUID = ei_utils.GenerateNameBasedGUID( + namespace, + 'Product ' + version + ) + COMPONENT_GUID = ei_utils.GenerateNameBasedGUID( + namespace, + 'Component ' + version + ) + COMPONENT_GUID_REGISTRY = ei_utils.GenerateNameBasedGUID( + namespace, + 'Component Registry ' + version + ) + COMPONENT_GUID_NOTIFY_SUCCESS = ( + ei_utils.GenerateNameBasedGUID( + namespace, + 'Component Notify Success ' + version + )) + COMPONENT_GUID_REGISTER_LAUNCH = ( + ei_utils.GenerateNameBasedGUID( + namespace, + 'Component Register Launch Command ' + version + )) + COMPONENT_GUID_NOTIFY_FAILED = ( + ei_utils.GenerateNameBasedGUID( + namespace, + 'Component Notify Failed ' + version + )) + COMPONENT_GUID_PROPERTY_BAR = ( + ei_utils.GenerateNameBasedGUID( + namespace, + 'Component Property Bar ' + version + )) + wix_env = env.Clone() + wix_env.Append( + WIXLIGHTFLAGS = [ + # Add a supress for: + # warning LGHT1076 : ICE91: The file will be installed to the per user + # directory that doesn't vary based on ALLUSERS value. This file won't + # be copied to each user's profile even if a per machine installation + # is desired. + # This warning is generated by light when we produce a user only + # installer, and can be ignored as this is a user only installer. + '-sw1076' + ], + WIXCANDLEFLAGS = [ + '-dFooExePath=' + wix_env.File(exe_name).abspath, + '-dFooVersion=' + version, + '-dFooProductGuid=' + PRODUCT_GUID, + '-dFooComponentGuid=' + COMPONENT_GUID, + '-dFooComponentGuidRegistry=' + COMPONENT_GUID_REGISTRY, + '-dFooComponentGuidNotifySuccess=' + COMPONENT_GUID_NOTIFY_SUCCESS, + '-dFooComponentRegisterLaunchCommand=' + + COMPONENT_GUID_REGISTER_LAUNCH, + '-dFooComponentGuidNotifyFailed=' + COMPONENT_GUID_NOTIFY_FAILED, + '-dFooComponentGuidPropertyBar=' + COMPONENT_GUID_PROPERTY_BAR, + ], + ) + wix_inputs = copy_target + wix_env['WIXCANDLEFLAGS'] += ['-dIsEnterprise=0'] + unsigned_msi = wix_env.WiX('unsigned_%s.msi' % msi_base_name, wix_inputs) + # Force a rebuild when the exe file changes. + wix_env.Depends(unsigned_msi, [exe_name]) + # Single-signed here because it is not possible to dual-sign an msi. + signed_output = env.SignedBinary( + target=msi_base_name + '.msi', + source=unsigned_msi, + ) + return signed_output + # # Custom Library and Program builders. # @@ -254,6 +396,10 @@ def ConfigureEnvFor64Bit(env): '$ATLMFC_VC16_0_DIR/lib/x64', '$WINDOWS_SDK_10_0_LIB_DIR/um/x64', '$WINDOWS_SDK_10_0_LIB_DIR/ucrt/x64',], + omaha_version_utils.VC170: [ '$VC17_0_DIR/lib/x64', + '$ATLMFC_VC17_0_DIR/lib/x64', + '$WINDOWS_SDK_10_0_LIB_DIR/um/x64', + '$WINDOWS_SDK_10_0_LIB_DIR/ucrt/x64',], }[env['msc_ver']] env.Prepend(LIBPATH=lib_paths) @@ -265,7 +411,8 @@ def ConfigureEnvFor64Bit(env): omaha_version_utils.VC120 : '$VC12_0_DIR/vc/bin/x86_amd64', omaha_version_utils.VC140 : '$VC14_0_DIR/vc/bin/x86_amd64', omaha_version_utils.VC150 : '$VC15_0_DIR/bin/HostX64/x64', - omaha_version_utils.VC160 : '$VC16_0_DIR/bin/HostX64/x64'} + omaha_version_utils.VC160 : '$VC16_0_DIR/bin/HostX64/x64', + omaha_version_utils.VC170 : '$VC17_0_DIR/bin/HostX64/x64'} [env['msc_ver']])) # Filter x86 options that are not supported or conflict with x86-x64 options. @@ -393,7 +540,7 @@ def CompileProtoBuf(env, input_proto_files): Returns: Output node list of generated .cc files. """ - proto_compiler_path = '%s/protoc.exe' % os.getenv('OMAHA_PROTOBUF_BIN_DIR', + proto_compiler_path = '"%s/protoc.exe"' % os.getenv('OMAHA_PROTOBUF_BIN_DIR', '$OBJ_ROOT/base/Default') proto_path = env['PROTO_PATH'] cpp_out = env['CPP_OUT'] @@ -402,10 +549,10 @@ def CompileProtoBuf(env, input_proto_files): for base in [RelativePath(in_file, proto_path) for in_file in input_proto_files] for ext in ('.pb.cc', '.pb.h')] - proto_arguments = (' --proto_path=%s --cpp_out=%s %s ' % + proto_arguments = (' --proto_path="%s" --cpp_out="%s" %s ' % (proto_path, cpp_out, - ' '.join(input_proto_files))) + ' '.join('"{0}"'.format(w) for w in input_proto_files))) proto_cmd_line = proto_compiler_path + proto_arguments compile_proto_buf = env.Command( target=targets, @@ -421,11 +568,13 @@ def CompileProtoBuf(env, input_proto_files): # NOTE: SCons requires the use of this name, which fails gpylint. -def generate(env): # pylint: disable-msg=C6409 +def generate(env): # pylint: disable=C6409 """SCons entry point for this tool.""" - env.AddMethod(SignDotNetManifest) - env.AddMethod(OmahaCertificateTagExe) + env.AddMethod(OmahaCertificateTag) + env.AddMethod(OmahaCertificateTagForTesting) env.AddMethod(OmahaTagExe) + env.AddMethod(OmahaBuildTestExe) + env.AddMethod(OmahaBuildTestMsi) env.AddMethod(IsBuildingModule) env.AddMethod(GetAllInOneUnittestSources) env.AddMethod(GetAllInOneUnittestLibs) diff --git a/omaha/site_scons/site_tools/windows_vc.py b/omaha/site_scons/site_tools/windows_vc.py index bb0402f68..f4c0bf8fe 100644 --- a/omaha/site_scons/site_tools/windows_vc.py +++ b/omaha/site_scons/site_tools/windows_vc.py @@ -52,18 +52,23 @@ def SetMsvcCompilerVersion( Raises: ValueError: An error if vc_flavor is not valid. """ - if version_num in (15.0, 16.0): + if version_num in ('15.0', '16.0', '17.0'): if not vc_flavor: vc_flavor = 'x64_x86' if vc_flavor not in ['x86_x86', 'x86_x64', 'x64_x86', 'x64_x64']: raise ValueError('Invalid vc_flavor %s.' % str(vc_flavor)) - if version_num == 16.0: + if version_num == '17.0': + env.Replace(VC17_0_DIR=os.environ.get('VCToolsInstallDir')) + _SetMsvcCompilerVersion(env, + vc_version='17.0', + vc_flavor=vc_flavor) + elif version_num == '16.0': env.Replace(VC16_0_DIR=os.environ.get('VCToolsInstallDir')) _SetMsvcCompilerVersion(env, vc_version='16.0', vc_flavor=vc_flavor) - elif version_num == 15.0: + elif version_num == '15.0': env.Replace(VC15_0_DIR=os.environ.get('VCToolsInstallDir')), _SetMsvcCompilerVersion(env, vc_version='15.0', @@ -88,6 +93,7 @@ def _SetMsvcCompilerVersion(env, vc_version, vc_flavor='x64_x86'): platform_sdk_lib_dir = platform_sdk_dir + 'lib/' + platform_sdk_version env['GOOGLECLIENT'] = '$MAIN_DIR/..' + env['GOOGLE3'] = '$GOOGLECLIENT' env['THIRD_PARTY'] = '$GOOGLECLIENT/third_party/' env.Replace( diff --git a/omaha/site_scons/site_tools/windows_vc12_0.py b/omaha/site_scons/site_tools/windows_vc12_0.py index a0cab26cf..48c5ee239 100644 --- a/omaha/site_scons/site_tools/windows_vc12_0.py +++ b/omaha/site_scons/site_tools/windows_vc12_0.py @@ -55,6 +55,7 @@ def _SetMsvcCompiler( platform_sdk_dir = os.environ.get('OMAHA_PLATFORM_SDK_DIR') env['GOOGLECLIENT'] = '$MAIN_DIR/..' + env['GOOGLE3'] = '$GOOGLECLIENT' env['THIRD_PARTY'] = '$GOOGLECLIENT/third_party/' env.Replace( diff --git a/omaha/site_scons/site_tools/windows_vc14_0.py b/omaha/site_scons/site_tools/windows_vc14_0.py index 0c11693ec..3ea31bc38 100644 --- a/omaha/site_scons/site_tools/windows_vc14_0.py +++ b/omaha/site_scons/site_tools/windows_vc14_0.py @@ -59,6 +59,7 @@ def _SetMsvcCompiler( platform_sdk_lib_dir = platform_sdk_dir + 'lib/' + platform_sdk_version env['GOOGLECLIENT'] = '$MAIN_DIR/..' + env['GOOGLE3'] = '$GOOGLECLIENT' env['THIRD_PARTY'] = '$GOOGLECLIENT/third_party/' env.Replace( diff --git a/omaha/site_scons/site_tools/windows_vc15_0.py b/omaha/site_scons/site_tools/windows_vc15_0.py deleted file mode 100644 index 576a986b6..000000000 --- a/omaha/site_scons/site_tools/windows_vc15_0.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2015 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -"""Tool-specific initialization for Visual Studio 2013. - -NOTE: This tool is order-dependent, and must be run before -any other tools that modify the binary, include, or lib paths because it will -wipe out any existing values for those variables. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -import os -import SCons - - -def _SetMsvcCompiler( - env, vc_flavor='x86'): - """When run on a non-Windows system, this function does nothing. - - When run on a Windows system, this function wipes the binary, include - and lib paths so that any non-hermetic tools won't be used. - These paths will be set up by target_platform_windows and other tools. - By wiping the paths here, we make the adding the other tools - order-independent. - Args: - env: The SCons environment in question. - vc_flavor: Defaults to x86, can be 'x86', 'amd64' or 'x86_amd64'. The - latter is using the 32-bit executable of the 64-bit compiler. - Returns: - Nothing. - Raises: - ValueError: An error if vc_flavor is not valid. - """ - if vc_flavor not in ('x86', 'amd64', 'x86_amd64'): - raise ValueError('vc_flavor must be one of: amd64, x86, x86_amd64.') - - vc_dir = os.environ.get('VCToolsInstallDir') - platform_sdk_dir = os.environ.get('OMAHA_PLATFORM_SDK_DIR') - platform_sdk_version = os.environ.get('WindowsSDKVersion') - platform_sdk_include_dir = platform_sdk_dir + 'include/' + \ - platform_sdk_version - platform_sdk_lib_dir = platform_sdk_dir + 'lib/' + platform_sdk_version - - env['GOOGLECLIENT'] = '$MAIN_DIR/..' - env['THIRD_PARTY'] = '$GOOGLECLIENT/third_party/' - - env.Replace( - PLATFORM_SDK_DIR=platform_sdk_dir, - MSVC_FLAVOR=('amd64', 'x86')[vc_flavor == 'x86'], - VC14_1_DIR=vc_dir, - WINDOWS_SDK_10_0_DIR=platform_sdk_dir, - ) - - # Clear any existing paths. - env['ENV']['PATH'] = '' - env['ENV']['INCLUDE'] = '' - env['ENV']['LIB'] = '' - - tools_paths = [] - include_paths = [] - lib_paths = [] - vc_bin_dir = vc_dir + 'bin/HostX86' - if vc_flavor == 'amd64': - vc_bin_dir += '/x64' - elif vc_flavor == 'x86': - vc_bin_dir += '/x86' - - tools_paths += [vc_bin_dir,] - - include_paths.append(vc_dir + '/include') - if vc_flavor == 'x86': - lib_paths.append(vc_dir + '/lib/x86') - else: - lib_paths.append(vc_dir + '/lib/x64') - - # Add explicit location of platform SDK to tools path - tools_paths.append(platform_sdk_dir + '/bin') - include_paths += [ - platform_sdk_include_dir + 'um', # Windows SDKs - platform_sdk_include_dir + 'shared', # Windows SDKs - platform_sdk_include_dir + 'ucrt', # Windows CRT - ] - if vc_flavor == 'x86': - lib_paths += [ - platform_sdk_lib_dir + 'um/x86', # Windows SDK - platform_sdk_lib_dir + 'ucrt/x86', # Windows CRT - ] - tools_paths.append(platform_sdk_dir + '/bin/x86') # VC 12 only - else: - lib_paths += [ - platform_sdk_lib_dir + 'um/x64', # Windows SDK - platform_sdk_lib_dir + 'ucrt/x64', # Windows CRT - ] - - for p in tools_paths: - env.AppendENVPath('PATH', env.Dir(p).abspath) - for p in include_paths: - env.AppendENVPath('INCLUDE', env.Dir(p).abspath) - for p in lib_paths: - env.AppendENVPath('LIB', env.Dir(p).abspath) - - -def generate(env): - _SetMsvcCompiler( - env=env, vc_flavor='x86') diff --git a/omaha/site_scons/site_tools/windows_vc15_0_host64_x64.py b/omaha/site_scons/site_tools/windows_vc15_0_host64_x64.py index bf3f59836..74b446360 100644 --- a/omaha/site_scons/site_tools/windows_vc15_0_host64_x64.py +++ b/omaha/site_scons/site_tools/windows_vc15_0_host64_x64.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=15.0, + version_num='15.0', vc_flavor='x64_x64') diff --git a/omaha/site_scons/site_tools/windows_vc15_0_host64_x86.py b/omaha/site_scons/site_tools/windows_vc15_0_host64_x86.py index 17ca81f45..90e404e7e 100644 --- a/omaha/site_scons/site_tools/windows_vc15_0_host64_x86.py +++ b/omaha/site_scons/site_tools/windows_vc15_0_host64_x86.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=15.0, + version_num='15.0', vc_flavor='x64_x86') diff --git a/omaha/site_scons/site_tools/windows_vc15_0_host86_x64.py b/omaha/site_scons/site_tools/windows_vc15_0_host86_x64.py index fdcce8b21..51d6a1292 100644 --- a/omaha/site_scons/site_tools/windows_vc15_0_host86_x64.py +++ b/omaha/site_scons/site_tools/windows_vc15_0_host86_x64.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=15.0, + version_num='15.0', vc_flavor='x86_x64') diff --git a/omaha/site_scons/site_tools/windows_vc15_0_host86_x86.py b/omaha/site_scons/site_tools/windows_vc15_0_host86_x86.py index 59161b189..f92e5b20d 100644 --- a/omaha/site_scons/site_tools/windows_vc15_0_host86_x86.py +++ b/omaha/site_scons/site_tools/windows_vc15_0_host86_x86.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=15.0, + version_num='15.0', vc_flavor='x86_x86') diff --git a/omaha/site_scons/site_tools/windows_vc16_0_host64_x64.py b/omaha/site_scons/site_tools/windows_vc16_0_host64_x64.py index adf0c0de9..90cfed302 100644 --- a/omaha/site_scons/site_tools/windows_vc16_0_host64_x64.py +++ b/omaha/site_scons/site_tools/windows_vc16_0_host64_x64.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=16.0, + version_num='16.0', vc_flavor='x64_x64') diff --git a/omaha/site_scons/site_tools/windows_vc16_0_host64_x86.py b/omaha/site_scons/site_tools/windows_vc16_0_host64_x86.py index 17861edf0..01e4869ee 100644 --- a/omaha/site_scons/site_tools/windows_vc16_0_host64_x86.py +++ b/omaha/site_scons/site_tools/windows_vc16_0_host64_x86.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=16.0, + version_num='16.0', vc_flavor='x64_x86') diff --git a/omaha/site_scons/site_tools/windows_vc16_0_host86_x64.py b/omaha/site_scons/site_tools/windows_vc16_0_host86_x64.py index 8cc3ba0d0..981166d02 100644 --- a/omaha/site_scons/site_tools/windows_vc16_0_host86_x64.py +++ b/omaha/site_scons/site_tools/windows_vc16_0_host86_x64.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=16.0, + version_num='16.0', vc_flavor='x86_x64') diff --git a/omaha/site_scons/site_tools/windows_vc16_0_host86_x86.py b/omaha/site_scons/site_tools/windows_vc16_0_host86_x86.py index c16655d9f..208103fd3 100644 --- a/omaha/site_scons/site_tools/windows_vc16_0_host86_x86.py +++ b/omaha/site_scons/site_tools/windows_vc16_0_host86_x86.py @@ -30,5 +30,5 @@ def generate(env): windows_vc.SetMsvcCompilerVersion(env=env, - version_num=16.0, + version_num='16.0', vc_flavor='x86_x86') diff --git a/omaha/site_scons/site_tools/windows_vc17_0_host64_x64.py b/omaha/site_scons/site_tools/windows_vc17_0_host64_x64.py new file mode 100644 index 000000000..52b973f43 --- /dev/null +++ b/omaha/site_scons/site_tools/windows_vc17_0_host64_x64.py @@ -0,0 +1,34 @@ +# Copyright 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +"""Tool-specific initialization for Visual Studio 2017. + +NOTE: This tool is order-dependent, and must be run before +any other tools that modify the binary, include, or lib paths because it will +wipe out any existing values for those variables. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +import windows_vc + + +def generate(env): + windows_vc.SetMsvcCompilerVersion(env=env, + version_num='17.0', + vc_flavor='x64_x64') diff --git a/omaha/site_scons/site_tools/windows_vc17_0_host64_x86.py b/omaha/site_scons/site_tools/windows_vc17_0_host64_x86.py new file mode 100644 index 000000000..52cd4f208 --- /dev/null +++ b/omaha/site_scons/site_tools/windows_vc17_0_host64_x86.py @@ -0,0 +1,34 @@ +# Copyright 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +"""Tool-specific initialization for Visual Studio 2017. + +NOTE: This tool is order-dependent, and must be run before +any other tools that modify the binary, include, or lib paths because it will +wipe out any existing values for those variables. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +import windows_vc + + +def generate(env): + windows_vc.SetMsvcCompilerVersion(env=env, + version_num='17.0', + vc_flavor='x64_x86') diff --git a/omaha/site_scons/site_tools/windows_vc17_0_host86_x64.py b/omaha/site_scons/site_tools/windows_vc17_0_host86_x64.py new file mode 100644 index 000000000..92185b152 --- /dev/null +++ b/omaha/site_scons/site_tools/windows_vc17_0_host86_x64.py @@ -0,0 +1,34 @@ +# Copyright 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +"""Tool-specific initialization for Visual Studio 2017. + +NOTE: This tool is order-dependent, and must be run before +any other tools that modify the binary, include, or lib paths because it will +wipe out any existing values for those variables. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +import windows_vc + + +def generate(env): + windows_vc.SetMsvcCompilerVersion(env=env, + version_num='17.0', + vc_flavor='x86_x64') diff --git a/omaha/site_scons/site_tools/windows_vc17_0_host86_x86.py b/omaha/site_scons/site_tools/windows_vc17_0_host86_x86.py new file mode 100644 index 000000000..e66b9b13f --- /dev/null +++ b/omaha/site_scons/site_tools/windows_vc17_0_host86_x86.py @@ -0,0 +1,34 @@ +# Copyright 2023 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + +"""Tool-specific initialization for Visual Studio 2017. + +NOTE: This tool is order-dependent, and must be run before +any other tools that modify the binary, include, or lib paths because it will +wipe out any existing values for those variables. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +import windows_vc + + +def generate(env): + windows_vc.SetMsvcCompilerVersion(env=env, + version_num='17.0', + vc_flavor='x86_x86') diff --git a/omaha/standalone/utils.py b/omaha/standalone/utils.py index 624c5a021..d7a09a971 100644 --- a/omaha/standalone/utils.py +++ b/omaha/standalone/utils.py @@ -6,13 +6,7 @@ import array import base64 -try: - # Import _hashlib instead of hashlib, otherwise, when used in an Omaha build, - # it collides with the incompatible Scons hashlib hack in: - # googleclient/third_party/scons/hammer_version/scons-local/SCons/compat/_scons_hashlib.py - import _hashlib -except ImportError: - from third_party.hashlib import _hashlib +import hashlib import os @@ -41,7 +35,7 @@ def GenerateUpdateResponseFile(target, sources, version_list, has_x64_binaries): arch_requirement = 'x64' manifest_content_list = [xml_header, response_header] - for file_index in xrange(0, len(sources), 2): + for file_index in range(0, len(sources), 2): source_manifest_path = sources[file_index] binary_path = sources[file_index + 1] size = os.stat(os.path.abspath(binary_path)).st_size @@ -49,7 +43,7 @@ def GenerateUpdateResponseFile(target, sources, version_list, has_x64_binaries): installer_file = open(os.path.abspath(binary_path), mode='rb') data.fromfile(installer_file, size) installer_file.close() - sha256 = _hashlib.openssl_sha256() + sha256 = hashlib.sha256() sha256.update(data) hash_value = sha256.hexdigest() @@ -72,7 +66,7 @@ def GenerateUpdateResponseFile(target, sources, version_list, has_x64_binaries): resp = manifest_content[response_body_start_index:response_body_end_index] resp = resp.replace('${INSTALLER_SIZE}', str(size)) resp = resp.replace('${INSTALLER_HASH_SHA256}', hash_value) - resp = resp.replace('${INSTALLER_VERSION}', version_list[file_index/2]) + resp = resp.replace('${INSTALLER_VERSION}', version_list[int(file_index/2)]) resp = resp.replace('${ARCH_REQUIREMENT}', arch_requirement) manifest_content_list.append(resp) manifest_content_list.append(response_footer) diff --git a/omaha/statsreport/aggregator-win32_unittest.cc b/omaha/statsreport/aggregator-win32_unittest.cc index f802a056d..20f088bc8 100644 --- a/omaha/statsreport/aggregator-win32_unittest.cc +++ b/omaha/statsreport/aggregator-win32_unittest.cc @@ -21,7 +21,7 @@ using namespace stats_report; #define APP_NAME_STRING L"aggregator-win32_unittest" -#define PREFIX_KEY_STRING L"Software\\" _T(SHORT_COMPANY_NAME_ANSI) L"\\" +#define PREFIX_KEY_STRING L"Software\\" _T(PATH_COMPANY_NAME_ANSI) L"\\" #define SUFFIX_KEY_STRING L"\\UsageStats\\Daily" #define ROOT_KEY_STRING PREFIX_KEY_STRING APP_NAME_STRING #define KEY_STRING ROOT_KEY_STRING SUFFIX_KEY_STRING diff --git a/omaha/statsreport/const-win32.cc b/omaha/statsreport/const-win32.cc index ce25d9139..dd86ff63a 100644 --- a/omaha/statsreport/const-win32.cc +++ b/omaha/statsreport/const-win32.cc @@ -25,7 +25,7 @@ const wchar_t kCountsKeyName[] = L"Counts"; const wchar_t kIntegersKeyName[] = L"Integers"; const wchar_t kBooleansKeyName[] = L"Booleans"; const wchar_t kStatsKeyFormatString[] = L"Software\\" - _T(SHORT_COMPANY_NAME_ANSI) + _T(PATH_COMPANY_NAME_ANSI) L"\\%ws\\UsageStats\\Daily"; const wchar_t kLastTransmissionTimeValueName[] = L"LastTransmission"; diff --git a/omaha/statsreport/persistent_iterator-win32_unittest.cc b/omaha/statsreport/persistent_iterator-win32_unittest.cc index 871b787fb..d2447998d 100644 --- a/omaha/statsreport/persistent_iterator-win32_unittest.cc +++ b/omaha/statsreport/persistent_iterator-win32_unittest.cc @@ -110,8 +110,8 @@ TEST_F(PersistentMetricsIteratorWin32Test, Basic) { } // Test to see whether we can reliably roundtrip metrics through -// the registry without molestation -TEST_F(PersistentMetricsIteratorWin32Test, UnmolestedValues) { +// the registry without corruption. +TEST_F(PersistentMetricsIteratorWin32Test, WriteStats) { EXPECT_TRUE(WriteStats()); MetricsMap metrics; @@ -122,7 +122,7 @@ TEST_F(PersistentMetricsIteratorWin32Test, UnmolestedValues) { for (; it != end; ++it) { MetricsMap::iterator found = metrics.find(it->name()); - // make sure we found it, and that it's unmolested in value + // Make sure we found it, and that it's the correct value. EXPECT_TRUE(found != metrics.end() && equals(found->second, *it)); count++; } diff --git a/omaha/test/build.scons b/omaha/test/build.scons deleted file mode 100644 index c4646ae6a..000000000 --- a/omaha/test/build.scons +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2009-2010 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ======================================================================== - -Import('env') - -import binascii -import md5 -from enterprise.installer import build_enterprise_installer - - -def BuildMSI(version, namespace, exe_name, wxs_template, msi_base_name, - is_enterprise=False, prefix=''): - if is_enterprise: - msi_base_name = 'enterprise_' + msi_base_name - msi_base_name = prefix + msi_base_name - - # Have to use 'copy' here because we are renaming the file, and it is being - # renamed to match the final msi name to avoid collisions in the wixobj files. - copy_target = env.Command( - target=msi_base_name + '.wxs', - source=wxs_template, - action='@copy /y $SOURCE $TARGET', - ) - - PRODUCT_GUID = build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Product ' + version - ) - COMPONENT_GUID = build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Component ' + version - ) - COMPONENT_GUID_REGISTRY = build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Component Registry ' + version - ) - COMPONENT_GUID_NOTIFY_SUCCESS = ( - build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Component Notify Success ' + version - )) - COMPONENT_GUID_REGISTER_LAUNCH = ( - build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Component Register Launch Command ' + version - )) - COMPONENT_GUID_NOTIFY_FAILED = ( - build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Component Notify Failed ' + version - )) - COMPONENT_GUID_PROPERTY_BAR = ( - build_enterprise_installer.GenerateNameBasedGUID( - namespace, - 'Component Property Bar ' + version - )) - - wix_env = env.Clone() - wix_env.Append( - WIXLIGHTFLAGS = [ - # Add a supress for: - # warning LGHT1076 : ICE91: The file will be installed to the per user - # directory that doesn't vary based on ALLUSERS value. This file won't - # be copied to each user's profile even if a per machine installation - # is desired. - # This warning is generated by light when we produce a user only - # installer, and can be ignored as this is a user only installer. - '-sw1076' - ], - WIXCANDLEFLAGS = [ - '-dFooExePath=' + wix_env.File(exe_name).abspath, - '-dFooVersion=' + version, - '-dFooProductGuid=' + PRODUCT_GUID, - '-dFooComponentGuid=' + COMPONENT_GUID, - '-dFooComponentGuidRegistry=' + COMPONENT_GUID_REGISTRY, - '-dFooComponentGuidNotifySuccess=' + COMPONENT_GUID_NOTIFY_SUCCESS, - '-dFooComponentRegisterLaunchCommand=' + - COMPONENT_GUID_REGISTER_LAUNCH, - '-dFooComponentGuidNotifyFailed=' + COMPONENT_GUID_NOTIFY_FAILED, - '-dFooComponentGuidPropertyBar=' + COMPONENT_GUID_PROPERTY_BAR, - ], - ) - - wix_inputs = copy_target - # Force a rebuild when the exe file changes. - additional_dependencies = [exe_name] - - if is_enterprise: - output_dir = '$TARGET_ROOT/Test_Installers' - wix_env['WIXCANDLEFLAGS'] += ['-dIsEnterprise=1'] - - # The metainstaller change does not get passed through even though the - # .wixobj file is rebuilt because the hash of the .wixobj does not change. - metainstaller_path = '$STAGING_DIR/%sGoogleUpdateSetup.exe' % (prefix) - google_update_wixobj = build_enterprise_installer.BuildGoogleUpdateFragment( - env, - metainstaller_path, - 'Test Foo', - version, - '{D6B08267-B440-4c85-9F79-E195E80D9937}', - '&ap=enterprise', - msi_base_name, - ('$MAIN_DIR/enterprise/installer/' - 'google_update_installer_fragment.wxs.xml') - ) - - wix_inputs += [google_update_wixobj] - additional_dependencies += [metainstaller_path] - else: - output_dir = '$TESTS_DIR' - wix_env['WIXCANDLEFLAGS'] += ['-dIsEnterprise=0'] - - unsigned_msi = wix_env.WiX('unsigned_%s.msi' % msi_base_name, wix_inputs) - - wix_env.Depends(unsigned_msi, additional_dependencies) - - signed_output = env.DualSignedBinary( - target=msi_base_name + '.msi', - source=unsigned_msi, - ) - - env.Replicate(output_dir, signed_output) - -_GUID_NAMESPACE = binascii.a2b_hex('BE19B3E4502845af8B3E67A99FCDCFB1') -_USER_GUID_NAMESPACE = binascii.a2b_hex('2B599C061F7C4eed9D686616EEBDDFDB') - -# test_foo.wxs.xml is so named because SCons tries to apply WiX building -# rules to any input file with the .wxs suffix even in a custom command. -_WXS_TEMPLATE_NAME = 'test_foo.wxs.xml' -_USER_WXS_TEMPLATE_NAME = 'user_app.wxs.xml' - -for (major, minor, build, patch) in [(1,0,101,0), (1,0,102,0)]: - version = '%d.%d.%d.%d' % (major, minor, build, patch) - msi_base_name = 'test_foo_v' + version - - ver_env = env.Clone() - ver_env.Append( - LIBS = [ - ver_env['crt_libs'][ver_env.Bit('debug')], - 'version.lib', - ], - RCFLAGS = [ - '-DVERSION_STRING=%s' % version, - '-DMAJOR=%s' % major, - '-DMINOR=%s' % minor, - '-DBUILD=%s' % build, - '-DPATCH=%s' % patch - ], - ) - - ver_env['OBJPREFIX'] = '%s%s/' % (ver_env['OBJPREFIX'], version) - - base_name = 'test_foo_v%s' % version.replace('.', '_') - signed_target_name = base_name + '.exe' - target_name = base_name + '_unsigned' - - unsigned_exe = ver_env.ComponentTestProgram( - prog_name=target_name, - source=[ - 'test_foo.cc', - ver_env.RES('test_foo_v%s.res' % version, 'test_foo.rc'), - ], - COMPONENT_TEST_RUNNABLE=False - ) - - signed_output = ver_env.DualSignedBinary( - target=signed_target_name, - source=unsigned_exe, - ) - - ver_env.Replicate('$TESTS_DIR', signed_output) - - signed_exe = signed_output[0] - - BuildMSI(version, _GUID_NAMESPACE, signed_exe, _WXS_TEMPLATE_NAME, - msi_base_name) - - # Build the enterprise installer for each version of Omaha. - for omaha_version_info in ver_env['omaha_versions_info']: - prefix = omaha_version_info.filename_prefix - - BuildMSI(version, _GUID_NAMESPACE, signed_exe, _WXS_TEMPLATE_NAME, - msi_base_name, is_enterprise=True, prefix=prefix) - - BuildMSI(version, _USER_GUID_NAMESPACE, signed_exe, _USER_WXS_TEMPLATE_NAME, - 'user_app_v' + version) - -bar_env = env.Clone() -bar_env.Append( - LIBS = [ - bar_env['crt_libs'][bar_env.Bit('debug')], - ], -) -bar_env.ComponentTestProgram(prog_name='test_bar', - source='test_bar.cc', - COMPONENT_TEST_RUNNABLE=False) - - -""" This is commented out because it step_test is currently unused, - and may be removed. Remove this if/when step_test is removed. - -omaha_system_env = env.Clone() -omaha_system_env.Append( - CPPDEFINES = [ - 'UNICODE', - '_UNICODE' - ], - LIBS = [ - '$LIB_PATH/common.lib', - omaha_system_env['atls_libs'][env.Bit('debug')], - omaha_system_env['crt_libs'][omaha_system_env.Bit('debug')],, - ], -) - - -omaha_system_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS']) -omaha_system_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE'] - -omaha_system_env.ComponentTestProgram([ - 'omaha_system_test.cc', - 'step_test.cc', - ], - COMPONENT_TEST_RUNNABLE=False -) -""" - diff --git a/omaha/test/omaha_system_test.cc b/omaha/test/omaha_system_test.cc deleted file mode 100644 index c578d4fb9..000000000 --- a/omaha/test/omaha_system_test.cc +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#pragma warning(push) -// C4548: expression before comma has no effect -#pragma warning(disable : 4548) -#include -#pragma warning(pop) -#include -#include "omaha/mi_exe_stub/mi_step_test.h" -#include "omaha/test/step_test.h" - -class MIWatcher : public StepTestWatcher { -public: - MIWatcher() : StepTestWatcher(MI_STEP_TEST_NAME), - system_should_be_clean_(true) { - } - - ~MIWatcher() { - } - -protected: - bool VerifyStep(DWORD step, bool *testing_complete) { - bool result = true; - switch (step) { - case MI_STEP_VERIFY_PRECONDITIONS: - if (system_should_be_clean_) { - result = VerifyCleanSystem(); - } else { - result = false; - _tprintf(_T("Unexpected state.\r\n")); - } - break; - case MI_STEP_PROGRAM_START: - if (system_should_be_clean_) { - result = VerifyCleanSystem(); - system_should_be_clean_ = false; - } else { - result = false; - _tprintf(_T("Unexpected state.\r\n")); - } - break; - case MI_STEP_PROGRAM_END: - result = VerifyTestFooInstalled(); - result &= VerifyOmahaInstalled(); - break; - } - *testing_complete = (step >= MI_STEP_PROGRAM_END); - return result; - } - - void PrintIntroduction() { - _tprintf( - _T("Ready to test mi.exe.\r\n") - _T("System should not have any version of Omaha installed on it.\r\n") - _T("System should not have any version of \"!!! Test Foo\" installed on it.\r\n") - _T("Please run SampleSetup.exe now.\r\n") - ); - } - - void PrintConclusion() { - _tprintf( - _T("Test of mi.exe is complete.\r\n") - ); - } - -private: - bool system_should_be_clean_; - -#define TEST_FOO_REG_UNINST_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{8EE7241A-9AFD-324A-884C-B62DC5DAE506}" -#define TEST_FOO_V2_REG_UNINST_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{EE93BF87-0CDD-3F0B-A4D6-3B3A1C54E3FA}" - - bool VerifyCleanSystem() { - if (RegistryKeyExists(HKEY_LOCAL_MACHINE, _T(TEST_FOO_REG_UNINST_KEY))) { - _tprintf(_T("Product !!! Test Foo (old version) appears to be installed on this system.\r\n")); - return false; - } - if (RegistryKeyExists(HKEY_LOCAL_MACHINE, _T(TEST_FOO_V2_REG_UNINST_KEY))) { - _tprintf(_T("Product !!! Test Foo appears to be installed on this system.\r\n")); - return false; - } - if (RegistryValueExists(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, _T("Google Update"))) { - _tprintf(_T("Omaha Run key is present on this system.\r\n")); - return false; - } - return true; - } - - bool VerifyTestFooInstalled() { - if (!RegistryKeyExists(HKEY_LOCAL_MACHINE, _T(TEST_FOO_V2_REG_UNINST_KEY))) { - _tprintf(_T("Product !!! Test Foo is not installed on this system.\r\n")); - return false; - } - return true; - } - - bool VerifyOmahaInstalled() { - if (!RegistryValueExists(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, _T("Google Update"))) { - _tprintf(_T("Omaha Run key is missing on this system.\r\n")); - return false; - } - return true; - } -}; - -int main() { - - MIWatcher watcher; - watcher.Go(); - return 0; -} diff --git a/omaha/test/step_test.cc b/omaha/test/step_test.cc deleted file mode 100644 index ed0625d07..000000000 --- a/omaha/test/step_test.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/test/step_test.h" - -const TCHAR *StepTestBase::RESUME_EVENT_TEMPLATE = _T("R %s"); -const TCHAR *StepTestBase::STEP_REACHED_EVENT_TEMPLATE = _T("SR %s"); -const TCHAR *StepTestBase::STATE_REG_KEY_TEMPLATE = _T("SOFTWARE\\GAT %s"); -const TCHAR *StepTestBase::STATE_REG_VALUE_NAME = _T("state"); - -StepTestBase::StepTestBase(const TCHAR*) : step_reached_event_(NULL), -resume_event_(NULL), state_key_(NULL) { -} - -void StepTestBase::Terminate() { - CloseHandle(step_reached_event_); - CloseHandle(resume_event_); - RegCloseKey(state_key_); -} - -StepTestBase::~StepTestBase() { - Terminate(); -} - -StepTestStepper::StepTestStepper(const TCHAR* name) : - StepTestBase(name), - is_someone_listening_(false) { - CString namebuf; - - // A "broadcaster" is a program that notifies the "listener" program that - // the broadcaster has reached an interesting point in its execution, and - // the listener should check state. - // - // Results will be unpredictable if two broadcasters are running at the - // same time. However, since a broadcaster will only open the resume event - // and not create it, we shouldn't see any problems where two broadcasters - // deadlock each other. - namebuf.Format(RESUME_EVENT_TEMPLATE, name); - resume_event_ = OpenEvent(EVENT_ALL_ACCESS, FALSE, namebuf); - if (resume_event_ != NULL) { - is_someone_listening_ = true; - - namebuf.Format(STEP_REACHED_EVENT_TEMPLATE, name); - step_reached_event_ = OpenEvent(EVENT_ALL_ACCESS, FALSE, namebuf); - - namebuf.Format(STATE_REG_KEY_TEMPLATE, name); - RegCreateKeyEx(HKEY_CURRENT_USER, namebuf, 0, NULL, REG_OPTION_VOLATILE, - KEY_READ | KEY_WRITE, NULL, &state_key_, NULL); - } else { - Terminate(); - } -} - -StepTestStepper::~StepTestStepper() { - Terminate(); -} - -void StepTestStepper::Step(DWORD step) { - if (!is_someone_listening_) { - return; - } - RegSetValueEx(state_key_, STATE_REG_VALUE_NAME, 0, REG_DWORD, - reinterpret_cast(&step), sizeof(step)); - SetEvent(step_reached_event_); - WaitForSingleObject(resume_event_, INFINITE); -} - -StepTestWatcher::StepTestWatcher(const TCHAR* name) : StepTestBase(name), - name_(name) { -} - -StepTestWatcher::~StepTestWatcher() { - Terminate(); -} - -void StepTestWatcher::Go() { - DWORD step = 0; - bool done = false; - - PrintIntroduction(); - - printf("Verifying preconditions...\r\n"); - bool passed = VerifyStep(0, &done); - if (passed) { - _tprintf(_T("Preconditions OK.\r\n")); - } else { - _tprintf(_T("*** Preconditions FAILED. ***\r\n")); - } - - CString namebuf; - - namebuf.Format(RESUME_EVENT_TEMPLATE, name_); - resume_event_ = CreateEvent(NULL, FALSE, FALSE, namebuf); - namebuf.Format(STEP_REACHED_EVENT_TEMPLATE, name_); - step_reached_event_ = CreateEvent(NULL, FALSE, FALSE, namebuf); - - namebuf.Format(STATE_REG_KEY_TEMPLATE, name_); - RegCreateKeyEx(HKEY_CURRENT_USER, namebuf, 0, NULL, REG_OPTION_VOLATILE, - KEY_READ, NULL, &state_key_, NULL); - - printf("Begin test now.\r\n"); - - done = false; - while (!done) { - WaitForSingleObject(step_reached_event_, INFINITE); - DWORD step_size = sizeof(step); - RegQueryValueEx(state_key_, STATE_REG_VALUE_NAME, NULL, NULL, - reinterpret_cast(&step), &step_size); - printf("Testing step %d.\r\n", step); - passed = VerifyStep(step, &done); - if (!passed) { - _tprintf(_T("\r\n\r\n*** TEST FAILURE: Step %d. ***\r\nr\n"), step); - } - SetEvent(resume_event_); - } - - PrintConclusion(); -} - -bool StepTestWatcher::RegistryKeyExists(HKEY root, const TCHAR* name) { - HKEY key; - LONG result = RegOpenKeyEx(root, name, 0, KEY_READ, &key); - if (ERROR_SUCCESS == result) { - RegCloseKey(key); - return true; - } - return false; -} - -bool StepTestWatcher::RegistryValueExists(HKEY root, const TCHAR* key_name, - const TCHAR* value_name) { - bool result = false; - HKEY key; - LONG reg_result = RegOpenKeyEx(root, key_name, 0, KEY_READ, &key); - if (ERROR_SUCCESS == reg_result) { - DWORD dummy_size = 0; - reg_result = RegQueryValueEx(key, value_name, NULL, NULL, NULL, - &dummy_size); - if (reg_result == ERROR_SUCCESS && dummy_size > 0) { - result = true; - } - RegCloseKey(key); - } - return result; -} diff --git a/omaha/test/step_test.h b/omaha/test/step_test.h deleted file mode 100644 index 2905a9cb7..000000000 --- a/omaha/test/step_test.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2007-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#pragma warning(push) -// C4548: expression before comma has no effect -#pragma warning(disable : 4548) -#include -#pragma warning(pop) - -// Overview -// -// The program to be tested instantiates a StepTestStepper, passing in a -// unique identifier (by convention, a GUID in registry format). It then -// calls StepTestStepper::Step() with values representing specific points in -// execution of the program. -// -// Then in test/omaha_system_test.cpp, derive a concrete subclass of -// StepTestWatcher. In particular, implement VerifyStep(), which takes each of -// the values passed in through the tested program's StepTestStepper::Step() -// function, and verifies that the state of the system matches what it should -// be. - -class StepTestBase { -public: - StepTestBase(const TCHAR *name); - virtual ~StepTestBase(); - -protected: - HANDLE step_reached_event_; - HANDLE resume_event_; - HKEY state_key_; - - static const TCHAR *RESUME_EVENT_TEMPLATE; - static const TCHAR *STEP_REACHED_EVENT_TEMPLATE; - static const TCHAR *STATE_REG_KEY_TEMPLATE; - static const TCHAR *STATE_REG_VALUE_NAME; - - void Terminate(); -}; - -class StepTestStepper : public StepTestBase { -public: - StepTestStepper(const TCHAR *name); - virtual ~StepTestStepper(); - - void Step(DWORD step); -private: - bool is_someone_listening_; -}; - -class StepTestWatcher : public StepTestBase { -public: - StepTestWatcher(const TCHAR *name); - virtual ~StepTestWatcher(); - - void Go(); - -protected: - // Checks the state of the machine once the tested program has reached the - // given step. - // - // @param step: the step to be verified - // @param testing_complete: pointer to bool that should be set to true iff - // the testing instance is done testing and should now terminate. - // @return true iff the test of this step passed. - virtual bool VerifyStep(DWORD step, bool *testing_complete) = 0; - - // Prints instructions to the console telling the human tester what the - // expected setup of the system should be. - virtual void PrintIntroduction() = 0; - - // Prints a statement to the console indicating that testing is complete. - virtual void PrintConclusion() = 0; - - // Returns true iff the given registry key exists on this machine. - bool RegistryKeyExists(HKEY root, const TCHAR *name); - - // Returns true iff the given registry value exists on this machine. - bool RegistryValueExists(HKEY root, const TCHAR *key_name, - const TCHAR *value_name); - - CString name_; -}; diff --git a/omaha/test/test_bar.cc b/omaha/test/test_bar.cc deleted file mode 100644 index 623624971..000000000 --- a/omaha/test/test_bar.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2006-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include -#include - -// This file implements a tiny program that puts up a MessageBox and exits. -// It's useful for generating test installation targets. -int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { - MessageBox(NULL, _T("I am bar"), _T("bar"), MB_OK | MB_ICONINFORMATION); - return 0; -} diff --git a/omaha/test/user_app.wxs.xml b/omaha/test/user_app.wxs.xml deleted file mode 100644 index fdd023aee..000000000 --- a/omaha/test/user_app.wxs.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - UPGRADING - - - - - - - - - - - - - - NOT (ALLUSERS) - - - - - - NOT (ALLUSERS) - - - - - - - - - - - - - (VersionNT = 500 AND ServicePackLevel = 3) OR VersionNT >= 501 - - - - NOT ALLUSERS - - - - diff --git a/omaha/testing/build.scons b/omaha/testing/build.scons index f40978f50..1db745679 100644 --- a/omaha/testing/build.scons +++ b/omaha/testing/build.scons @@ -15,6 +15,7 @@ # limitations under the License. # ======================================================================== +import binascii import os Import('env') @@ -42,9 +43,6 @@ unittest_support = env.Replicate('$STAGING_DIR/unittest_support/', [ 'unittest_support/declaration.txt', 'unittest_support/manifest.xml', - # Installer files used by the Install Manager unit tests. - 'unittest_support/test_foo_v1.0.101.0.msi', - # Files used by code signing and file verification tests. The string of # bytes in the chrome file names represent the serial of the certificate. # Chrome certificate: (11/13/2011 to 11/13/2014) revoked on 1/28/2014 @@ -76,6 +74,10 @@ unittest_support = env.Replicate('$STAGING_DIR/unittest_support/', [ # serial=14F8FDD167F92402B1570B5DC495C815 'unittest_support/sha1_14F8FDD167F92402B1570B5DC495C815.sys', + # Omaha certificate: sha1 (11/07/2019 to 11/16/2022). + # serial=06aea76bac46a9e8cfe6d29e45aaf033 + 'unittest_support/sha1_06aea76bac46a9e8cfe6d29e45aaf033.sys', + 'unittest_support/Sha1_4c40dba5f988fae57a57d6457495f98b_and_sha2_2a9c21acaaa63a3c58a7b9322bee948d.exe', # Omaha and Chrome certificate: sha256 (12/15/2015 to 12/16/2018). @@ -86,6 +88,11 @@ unittest_support = env.Replicate('$STAGING_DIR/unittest_support/', [ # serial=0c15be4a15bb0903c901b1d6c265302f 'unittest_support/sha2_0c15be4a15bb0903c901b1d6c265302f.msi', + # Google LLC certificate sha256 valid from 07-01-2021 to 07-10-2024. + # thumbprint=2673ea6cc23beffda49ac715b121544098a1284c. + # serial=0e4418e2dede36dd2974c3443afb5ce5. + 'unittest_support/sha2_0e4418e2dede36dd2974c3443afb5ce5.msi', + # The actual Chrome setup signed with sha256 (11/06/2018 to 11/17/2021). # serial=0c15be4a15bb0903c901b1d6c265302f 'unittest_support/chrome_setup.exe', @@ -149,6 +156,44 @@ unittest_support += env.Replicate( '$STAGING_DIR/unittest_support/' + loc_guid, 'unittest_support/%s/gears-win32-opt.msi' % loc_guid) +# Create tagged MSI files for testing. +tags = { + 'brand-only': (None, 'brand=QAQA', None), + 'ampersand-ending': (None, 'brand=QAQA&', None), + 'multiple': (None, + ('appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}&' + 'iid={2D8C18E9-8D3A-4EFC-6D61-AE23E3530EA2}&' + 'lang=en&browser=4&usagestats=0&appname=Google%20Chrome&' + 'needsadmin=prefers&brand=CHMB&' + 'installdataindex=defaultbrowser'), None), + 'empty-key': (None, '=value&brand=QAQA', None), + 'empty-value': (None, 'brand=', None), + 'empty-tag': (None, '', None), + 'invalid-marker': ('Gact2.0Foo', 'brand=QAQA', None), + 'invalid-length': (None, 'brand=QAQA', 3000), + 'invalid-key': (None, 'br*nd=QAQA', None), + 'invalid-value': (None, 'brand=QA*A', None), + 'bad-format': (None, 'brand', None), + 'bad-format2': (None, '=======&=======&&&=&&&&0', None), +} +for tag_name, tag_option in tags.items(): + unittest_support += env.OmahaCertificateTagForTesting( + target = '$STAGING_DIR/unittest_support/tagged_msi/GUH-%s.msi' % tag_name, + source = 'unittest_support/GoogleUpdateHelper.msi', + magic_bytes = tag_option[0], + tag = tag_option[1], + tag_length = tag_option[2]) + +# Create branded test MSIs for install manager testing. +_GUID_NAMESPACE = binascii.a2b_hex('BE19B3E4502845af8B3E67A99FCDCFB1') +_WXS_TEMPLATE_NAME = '$OBJ_ROOT/testing/test_foo.wxs.xml' +(major, minor, build, patch) = (1,0,101,0) +version = '%d.%d.%d.%d' % (major, minor, build, patch) +msi_base_name = 'test_foo_v' + version +test_exe = env.OmahaBuildTestExe(version, major, minor, build, patch) +test_msi = env.OmahaBuildTestMsi(version, _GUID_NAMESPACE, test_exe[0], _WXS_TEMPLATE_NAME, msi_base_name) +env.Replicate('$STAGING_DIR/unittest_support', test_msi) + #=============General Unit Test Dependencies=================================== # Many unit tests rely on string resources. omaha_unittest.cc loads them but # assumes they are in the same directory as the tests. @@ -213,7 +258,6 @@ omaha_unittest_libs = [ 'delayimp.lib', # For delay loading 'imagehlp.lib', 'iphlpapi.lib', - 'msi.lib', 'mstask.lib', 'netapi32.lib', 'ole32.lib', @@ -239,7 +283,6 @@ if omaha_unittest_env.Bit('has_device_management'): omaha_unittest_libs.append('$LIB_DIR/dm_proto.lib') omaha_unittest_env.Append( CPPPATH = [ - '$GOOGLE3', protobuf_src_dir, '$TARGET_ROOT/proto_files', ], @@ -256,23 +299,12 @@ if omaha_unittest_env.IsBuildingModule('plugins'): '$LIB_DIR/update_test_helpers.lib', ] -if omaha_unittest_env.IsBuildingModule('recovery'): - omaha_unittest_libs += [ - '$LIB_DIR/executecustomaction_lib', - '$LIB_DIR/repair_goopdate.lib', - ] - if omaha_unittest_env.IsBuildingModule('enterprise'): omaha_unittest_libs.append('$LIB_DIR/custom_actions_test_lib.lib') omaha_unittest_libs += omaha_unittest_env.GetAllInOneUnittestLibs() omaha_unittest_env.Append( - CPPPATH = [ - '$OBJ_ROOT', # Needed for the generated files - '$THIRD_PARTY/googletest/googlemock/include', - '$THIRD_PARTY/googletest/googletest/include', - ], CCFLAGS = [ '/wd4100', # unreferenced formal parameter '/wd4244', # possible loss of data @@ -353,8 +385,6 @@ unittest_base_env.ComponentLibrary( omaha_unittest_inputs = [ # Base unit tests '../base/app_util_unittest.cc', - '../base/atlassert_unittest.cc', - '../base/atl_regexp_unittest.cc', '../base/browser_utils_unittest.cc', '../base/cgi_unittest.cc', '../base/command_line_parser_unittest.cc', @@ -375,7 +405,6 @@ omaha_unittest_inputs = [ '../base/file_unittest.cc', '../base/firewall_product_detection_unittest.cc', '../base/highres_timer_unittest.cc', - '../base/localization_unittest.cc', '../base/logging_unittest.cc', '../base/omaha_version_unittest.cc', '../base/path_unittest.cc', @@ -448,7 +477,6 @@ omaha_unittest_inputs = [ '../common/stats_uploader_unittest.cc', '../common/update_request_unittest.cc', '../common/url_utils_unittest.cc', - '../common/webplugin_utils_unittest.cc', '../common/web_services_client_unittest.cc', '../common/xml_parser_unittest.cc', @@ -456,7 +484,9 @@ omaha_unittest_inputs = [ '../crashhandler/crash_analyzer_unittest.cc', # Core unit tests + '../core/core_launcher.cc', '../core/core_unittest.cc', + '../core/scheduler_unittest.cc', '../core/system_monitor_unittest.cc', '../core/google_update_core_unittest.cc', @@ -516,7 +546,6 @@ omaha_unittest_inputs = [ '../recovery/client/google_update_recovery_unittest.cc', # Setup unit tests. - '../setup/msi_test_utils.cc', '../setup/setup_unittest.cc', '../setup/setup_files_unittest.cc', '../setup/setup_google_update_unittest.cc', @@ -547,11 +576,14 @@ omaha_unittest_inputs = [ '../third_party/chrome/files/src/base/cpu_unittest.cc', '../third_party/chrome/files/src/base/rand_util_unittest.cc', '../third_party/chrome/files/src/crypto/rsa_private_key_unittest.cc', + '../third_party/chrome/files/src/crypto/signature_creator_unittest.cc', + '../third_party/chrome/files/src/crypto/signature_verifier_win_unittest.cc', ] if omaha_unittest_env.Bit('has_device_management'): omaha_unittest_inputs += [ '../goopdate/dm_client_unittest.cc', + '../goopdate/dm_messages_unittest.cc', '../goopdate/dm_storage_test_utils.cc', '../goopdate/dm_storage_unittest.cc', ] @@ -563,27 +595,6 @@ if omaha_unittest_env.IsBuildingModule('mi_exe_stub'): '../mi_exe_stub/x86_encoder/bcj2_encoder_unittest.cc', ] -if omaha_unittest_env.IsBuildingModule('plugins'): - omaha_unittest_inputs += [ - # Plugin unit tests. - '../plugins/update/omaha_customization_update_apis_unittest.cc', - '../plugins/update/omaha_customization_update_unittest.cc', - '../plugins/update/activex/update3web_control_unittest.cc', - '../plugins/update/npapi/dispatch_host_unittest.cc', - '../plugins/update/npapi/npfunction_host_unittest.cc', - '../plugins/update/npapi/variant_utils_unittest.cc', - '../plugins/update/site_lock_unittest.cc', - '$OBJ_ROOT/plugins/update/npapi/testing/dispatch_host_test.res', - ] - -if omaha_unittest_env.IsBuildingModule('recovery'): - omaha_unittest_inputs += [ - # Recovery unit tests. - '../recovery/repair_exe/custom_action/execute_repair_file_unittest.cc', - '../recovery/repair_exe/mspexecutableelevator_unittest.cc', - '../recovery/repair_exe/repair_goopdate_unittest.cc', - ] - if omaha_unittest_env.IsBuildingModule('enterprise'): omaha_unittest_inputs += [ # Custom action unit tests. @@ -653,17 +664,9 @@ omaha_unittest_env.Depends(test, '$STAGING_DIR/GoogleUpdateSetup_repair.exe') # Customization/UI tests depend on goopdate.dll (for TypeLib/resources) omaha_unittest_env.Depends(test, '$TESTS_DIR/goopdate.dll') -# Customization test uses update plugin DLL (npGoogleUpdate3.dll) for TypeLib. -omaha_unittest_env.Depends( - test, - '$STAGING_DIR/%s' % env['omaha_versions_info'][0].update_plugin_filename) - if env.Bit('all'): save_args_env = env.Clone() save_args_env.Append( - CPPPATH = [ - '$OBJ_ROOT', # Needed for the generated files - ], LIBS = [ save_args_env['atls_libs'][save_args_env.Bit('debug')], save_args_env['crt_libs'][save_args_env.Bit('debug')], @@ -712,7 +715,7 @@ if env.Bit('all'): COMPONENT_TEST_RUNNABLE=False ) - signed_output = save_args_env.DualSignedBinary( + signed_output = save_args_env.SignedBinary( target='SaveArguments.exe', source=unsigned_output, ) diff --git a/omaha/testing/omaha_unittest.cc b/omaha/testing/omaha_unittest.cc index 8dd66b8aa..9d9293fd4 100644 --- a/omaha/testing/omaha_unittest.cc +++ b/omaha/testing/omaha_unittest.cc @@ -224,7 +224,7 @@ int RunTests(bool is_medium_or_large_test, InitializeVersionFromModule(NULL); scoped_co_init co_init(COINIT_MULTITHREADED); - VERIFY1(SUCCEEDED(co_init.hresult())); + VERIFY_SUCCEEDED(co_init.hresult()); const bool is_build_system = IsBuildSystem(); @@ -246,7 +246,7 @@ int RunTests(bool is_medium_or_large_test, _tprintf(_T("\nCopying '%s' to '%s'.\n"), static_cast(source_path), static_cast(target_path)); - VERIFY1(SUCCEEDED(File::Copy(source_path, target_path, false))); + VERIFY_SUCCEEDED(File::Copy(source_path, target_path, false)); } } diff --git a/omaha/test/test_foo.cc b/omaha/testing/test_foo.cc similarity index 100% rename from omaha/test/test_foo.cc rename to omaha/testing/test_foo.cc diff --git a/omaha/test/test_foo.rc b/omaha/testing/test_foo.rc similarity index 98% rename from omaha/test/test_foo.rc rename to omaha/testing/test_foo.rc index c0c454f82..df4cb4a4a 100644 --- a/omaha/test/test_foo.rc +++ b/omaha/testing/test_foo.rc @@ -15,7 +15,7 @@ // Microsoft Visual C++ generated resource script. // -#include "test/test_foo_resource.h" +#include "testing/test_foo_resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// diff --git a/omaha/test/test_foo.wxs.xml b/omaha/testing/test_foo.wxs.xml similarity index 91% rename from omaha/test/test_foo.wxs.xml rename to omaha/testing/test_foo.wxs.xml index e505cd889..2a14ebc47 100644 --- a/omaha/test/test_foo.wxs.xml +++ b/omaha/testing/test_foo.wxs.xml @@ -1,5 +1,6 @@ + @@ -98,11 +99,11 @@ NOT NOTIFY_FAILURE AND NOT DO_NOT_NOTIFY_SUCCESS @@ -111,7 +112,7 @@ REGISTER_LAUNCH_COMMAND AND NOT DO_NOT_NOTIFY_SUCCESS @@ -128,15 +129,15 @@ NOTIFY_FAILURE @@ -144,7 +145,7 @@ PROPBAR=7 diff --git a/omaha/test/test_foo_resource.h b/omaha/testing/test_foo_resource.h similarity index 100% rename from omaha/test/test_foo_resource.h rename to omaha/testing/test_foo_resource.h diff --git a/omaha/testing/ui/build.scons b/omaha/testing/ui/build.scons index 039411f18..acc2dc86a 100644 --- a/omaha/testing/ui/build.scons +++ b/omaha/testing/ui/build.scons @@ -27,7 +27,6 @@ local_env.FilterOut(LINKFLAGS = ['/NODEFAULTLIB']) local_env.Append( CPPPATH = [ - '$OBJ_ROOT', # Needed for the generated files '$MAIN_DIR/goopdate/resources', '$THIRD_PARTY/gtest/include', ], @@ -43,7 +42,6 @@ local_env.Append( '$LIB_DIR/google_update_recovery.lib', '$LIB_DIR/logging.lib', '$LIB_DIR/net.lib', - '$LIB_DIR/repair_goopdate.lib', '$LIB_DIR/security.lib', '$LIB_DIR/service.lib', '$LIB_DIR/statsreport.lib', @@ -55,7 +53,6 @@ local_env.Append( 'comctl32.lib', 'crypt32.lib', 'iphlpapi.lib', - 'msi.lib', 'mstask.lib', 'netapi32.lib', 'psapi.lib', diff --git a/omaha/testing/unit_test.cc b/omaha/testing/unit_test.cc index 9799ce479..e112bf30d 100644 --- a/omaha/testing/unit_test.cc +++ b/omaha/testing/unit_test.cc @@ -14,20 +14,24 @@ // ======================================================================== #include "testing/unit_test.h" + #include "omaha/base/app_util.h" #include "omaha/base/constants.h" #include "omaha/base/error.h" #include "omaha/base/path.h" #include "omaha/base/process.h" #include "omaha/base/reg_key.h" +#include "omaha/base/scope_guard.h" #include "omaha/base/string.h" #include "omaha/base/system.h" -#include "omaha/base/utils.h" #include "omaha/base/user_info.h" +#include "omaha/base/utils.h" #include "omaha/base/vistautil.h" #include "omaha/common/command_line.h" #include "omaha/common/command_line_builder.h" +#include "omaha/common/config_manager.h" #include "omaha/common/const_goopdate.h" +#include "omaha/common/const_group_policy.h" #include "omaha/third_party/smartany/scoped_any.h" namespace omaha { @@ -64,7 +68,7 @@ CString GetLocalAppDataPath() { } CString GetGoogleUserPath() { - return GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\"); + return GetLocalAppDataPath() + PATH_COMPANY_NAME + _T("\\"); } // TODO(omaha): make GetGoogleUpdateUserPath and GetGoogleUpdateMachinePath @@ -76,7 +80,7 @@ CString GetGoogleUpdateUserPath() { CString GetGoogleUpdateMachinePath() { CString program_files; GetFolderPath(CSIDL_PROGRAM_FILES, &program_files); - return program_files + _T("\\") + SHORT_COMPANY_NAME + return program_files + _T("\\") + PATH_COMPANY_NAME + _T("\\") + PRODUCT_NAME; } @@ -368,6 +372,25 @@ void CreateFiles(const TCHAR* parent_dir, } } +HRESULT SetPolicy(const TCHAR* policy_name, DWORD value) { + ON_SCOPE_EXIT_OBJ(*ConfigManager::Instance(), + &ConfigManager::LoadPolicies, + true); + return RegKey::SetValue(kRegKeyGoopdateGroupPolicy, policy_name, value); +} + +HRESULT SetPolicyString(const TCHAR* policy_name, const CString& value) { + ON_SCOPE_EXIT_OBJ(*ConfigManager::Instance(), + &ConfigManager::LoadPolicies, + true); + return RegKey::SetValue(kRegKeyGoopdateGroupPolicy, policy_name, value); +} + +void ClearGroupPolicies() { + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + ConfigManager::Instance()->LoadPolicies(true); +} + } // namespace omaha std::ostream& operator<<(std::ostream& os, const CString& str) { diff --git a/omaha/testing/unit_test.h b/omaha/testing/unit_test.h index fc85e8449..9872daf71 100644 --- a/omaha/testing/unit_test.h +++ b/omaha/testing/unit_test.h @@ -87,7 +87,7 @@ CString GetSzValue(const CString& full_key_name, const CString& value_name); GUID StringToGuid(const CString& str); const TCHAR* const kRegistryHiveOverrideRoot = - _T("HKCU\\Software\\") _T(SHORT_COMPANY_NAME_ANSI) + _T("HKCU\\Software\\") _T(PATH_COMPANY_NAME_ANSI) _T("\\") _T(PRODUCT_NAME_ANSI) _T("\\UnitTest\\"); const TCHAR* const kCsidlSystemIdsRegKey = @@ -199,6 +199,13 @@ void CreateFiles(const TCHAR* parent_dir, const FileStruct files[], size_t number_of_files); +HRESULT SetPolicy(const TCHAR* policy_name, DWORD value); +HRESULT SetPolicyString(const TCHAR* policy_name, const CString& value); + +// Deletes the group policy registry keys for Omaha and reloads the +// ConfigManager policies to reset them. +void ClearGroupPolicies(); + } // namespace omaha // TODO(omaha): Replace custom predicates with EXPECT_HRESULT_SUCCEEDED/FAILED. diff --git a/omaha/testing/unit_test_unittest.cc b/omaha/testing/unit_test_unittest.cc index 6f50e1ff7..72441acc5 100644 --- a/omaha/testing/unit_test_unittest.cc +++ b/omaha/testing/unit_test_unittest.cc @@ -16,6 +16,8 @@ #include "omaha/base/utils.h" #include "omaha/base/user_info.h" #include "omaha/base/vistautil.h" +#include "omaha/common/config_manager.h" +#include "omaha/common/const_group_policy.h" #include "testing/unit_test.h" namespace omaha { @@ -94,4 +96,20 @@ TEST(UnitTestHelpersTest, StringToGuid_ValidString) { StringToGuid(_T("{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}"))); } +TEST(UnitTestHelpersTest, ClearGroupPolicies) { + EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain, + 1UL)); + EXPECT_SUCCEEDED(SetPolicyString(kRegValueDownloadPreference, + kDownloadPreferenceCacheable)); + ConfigManager* cm = ConfigManager::Instance(); + cm->LoadPolicies(true); + EXPECT_STREQ(cm->GetDownloadPreferenceGroupPolicy(nullptr), _T("cacheable")); + ClearGroupPolicies(); + EXPECT_STREQ(cm->GetDownloadPreferenceGroupPolicy(nullptr), _T("")); + RegKey::DeleteKey(kRegKeyGoopdateGroupPolicy); + EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, + kRegValueIsEnrolledToDomain)); +} + } // namespace omaha diff --git a/omaha/testing/unittest_support/SaveArguments.exe b/omaha/testing/unittest_support/SaveArguments.exe index 228ff4bbe..2db52b150 100644 Binary files a/omaha/testing/unittest_support/SaveArguments.exe and b/omaha/testing/unittest_support/SaveArguments.exe differ diff --git a/omaha/testing/unittest_support/sha1_06aea76bac46a9e8cfe6d29e45aaf033.sys b/omaha/testing/unittest_support/sha1_06aea76bac46a9e8cfe6d29e45aaf033.sys new file mode 100644 index 000000000..bbaf12990 Binary files /dev/null and b/omaha/testing/unittest_support/sha1_06aea76bac46a9e8cfe6d29e45aaf033.sys differ diff --git a/omaha/testing/unittest_support/sha2_0e4418e2dede36dd2974c3443afb5ce5.msi b/omaha/testing/unittest_support/sha2_0e4418e2dede36dd2974c3443afb5ce5.msi new file mode 100644 index 000000000..b17d496c0 Binary files /dev/null and b/omaha/testing/unittest_support/sha2_0e4418e2dede36dd2974c3443afb5ce5.msi differ diff --git a/omaha/testing/unittest_support/test_foo_v1.0.101.0.msi b/omaha/testing/unittest_support/test_foo_v1.0.101.0.msi deleted file mode 100644 index 6fca3e6ef..000000000 Binary files a/omaha/testing/unittest_support/test_foo_v1.0.101.0.msi and /dev/null differ diff --git a/omaha/third_party/__init__.py b/omaha/third_party/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/omaha/third_party/build.scons b/omaha/third_party/build.scons index beaa2d2e8..e473e141a 100644 --- a/omaha/third_party/build.scons +++ b/omaha/third_party/build.scons @@ -21,7 +21,8 @@ Import('env') # Build BreakPad library. # breakpad_env = env.Clone() -breakpad_env.Dir('breakpad').addRepository(env.Dir('$THIRD_PARTY/breakpad')) +breakpad_env.Dir('breakpad').addRepository( + env.Dir('$GOOGLE3/third_party/breakpad')) breakpad_env.Append( CCFLAGS = [ '/wd4061', # enumerator in switch is not handled by a case label @@ -54,7 +55,7 @@ breakpad_env.ComponentStaticLibraryMultiarch(target_name, breakpad_inputs) # gtest_env = env.Clone() -_gtest_repository ='$THIRD_PARTY/googletest/googletest' +_gtest_repository ='$GOOGLE3/third_party/googletest/googletest' gtest_env.Dir('gtest').addRepository(env.Dir(_gtest_repository)) gtest_env.Append( CCFLAGS = [ @@ -86,7 +87,7 @@ gtest_env.ComponentLibrary( gmock_env = env.Clone() -_gmock_repository ='$THIRD_PARTY/googletest/googlemock' +_gmock_repository ='$GOOGLE3/third_party/googletest/googlemock' gmock_env.Dir('gmock').addRepository(env.Dir(_gmock_repository)) gmock_env.Append( CCFLAGS = [ diff --git a/omaha/third_party/chrome/files/src/components/crx_file/crx_verifier.cc b/omaha/third_party/chrome/files/src/components/crx_file/crx_verifier.cc index dcb445a15..8c2bc9dde 100644 --- a/omaha/third_party/chrome/files/src/components/crx_file/crx_verifier.cc +++ b/omaha/third_party/chrome/files/src/components/crx_file/crx_verifier.cc @@ -19,11 +19,6 @@ #include "components/crx_file/crx_verifier.h" -#pragma warning(disable : 4245) -// C4245 : conversion from 'type1' to 'type2', signed/unsigned mismatch -#include -#pragma warning(default : 4245) - #include #include #include @@ -38,6 +33,7 @@ #include "omaha/base/scope_guard.h" #include "omaha/base/security/sha256.h" #include "omaha/base/signatures.h" +#include "omaha/base/string.h" #include "omaha/base/utils.h" #include "omaha/net/cup_ecdsa_utils.h" #include "third_party/chrome/files/src/components/crx_file/crx3.pb.h" @@ -51,7 +47,7 @@ namespace { const uint32_t kMaxHeaderSize = 1 << 18; // The context for Crx3 signing, encoded in UTF8. -const unsigned char kSignatureContext[] = u8"CRX3 SignedData"; +const unsigned char kSignatureContext[] = "CRX3 SignedData"; // The SHA256 hash of the "ecdsa_2017_public" Crx3 key. const uint8_t kPublisherKeyHash[] = { @@ -242,11 +238,11 @@ VerifierResult VerifyCrx3( return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED; } - const std::vector v( - public_key_bytes.c_str(), - public_key_bytes.c_str() + public_key_bytes.length()); CStringA encoded; - VERIFY1(SUCCEEDED(omaha::Base64::Encode(v, &encoded, false))); + omaha::Base64Escape(public_key_bytes.c_str(), + public_key_bytes.length(), + &encoded, + true); *public_key = encoded; *crx_id = declared_crx_id; return VerifierResult::OK_FULL; @@ -433,7 +429,7 @@ VerifierResult Verify( } std::shared_ptr file_hash( - omaha::CryptDetails::CreateHasher(true)); + omaha::CryptDetails::CreateHasher()); // Magic number. bool diff = false; diff --git a/omaha/third_party/chrome/files/src/crypto/rsa_private_key.cc b/omaha/third_party/chrome/files/src/crypto/rsa_private_key.cc index fbb76e655..cf9513669 100644 --- a/omaha/third_party/chrome/files/src/crypto/rsa_private_key.cc +++ b/omaha/third_party/chrome/files/src/crypto/rsa_private_key.cc @@ -8,9 +8,41 @@ #include #include "omaha/base/debug.h" +#include "omaha/base/logging.h" namespace crypto { +HRESULT CryptAcquireContextWithFallback(DWORD provider_type, + HCRYPTPROV* provider) { + ASSERT1(provider); + + const TCHAR* kHashCryptoProviders[] = { + NULL, // The default provider. + MS_ENH_RSA_AES_PROV, // The named provider for Vista and up. + MS_ENH_RSA_AES_PROV_XP // The named provider for XP SP3 and up. + }; + + // Try different providers until one of them succeeds. + HRESULT hr = S_OK; + ScopedHCRYPTPROV scoped_provider; + for (auto hash_crypto_provider : kHashCryptoProviders) { + if (::CryptAcquireContext(scoped_provider.receive(), + NULL, + hash_crypto_provider, + provider_type, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + REPORT_LOG(L6, (_T("[CryptAcquireContext succeeded]"))); + *provider = scoped_provider.release(); + return S_OK; + } + + hr = HRESULT_FROM_WIN32(::GetLastError()); + REPORT_LOG(LE, (_T("[CryptAcquireContext failed][%#x]"), hr)); + } + + return hr; +} + // static RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { std::unique_ptr result(new RSAPrivateKey()); @@ -122,8 +154,8 @@ RSAPrivateKey::RSAPrivateKey() : provider_(NULL), key_(NULL) {} RSAPrivateKey::~RSAPrivateKey() {} bool RSAPrivateKey::InitProvider() { - return FALSE != CryptAcquireContext(provider_.receive(), NULL, NULL, - PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + return SUCCEEDED(CryptAcquireContextWithFallback(PROV_RSA_AES, + provider_.receive())); } bool RSAPrivateKey::ExportPrivateKey(std::vector* output) { diff --git a/omaha/third_party/chrome/files/src/crypto/rsa_private_key.h b/omaha/third_party/chrome/files/src/crypto/rsa_private_key.h index 7a58d95d0..00eb6f4e9 100644 --- a/omaha/third_party/chrome/files/src/crypto/rsa_private_key.h +++ b/omaha/third_party/chrome/files/src/crypto/rsa_private_key.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CRYPTO_RSA_PRIVATE_KEY_H_ -#define CRYPTO_RSA_PRIVATE_KEY_H_ +#ifndef OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_RSA_PRIVATE_KEY_H_ +#define OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_RSA_PRIVATE_KEY_H_ #pragma once #include "build/build_config.h" @@ -33,11 +33,18 @@ struct SECKEYPublicKeyStr; namespace crypto { +// Providers implementing SHA256 can be instantiated using different names. +// On Vista and up, both the default provider and the enhanced RSA/AES +// provider support SHA256. On Windows XP, the named provider has a different +// name, therefore, the code falls back to a specific named provider in case +// of errors. +HRESULT CryptAcquireContextWithFallback(DWORD provider_type, + HCRYPTPROV* provider); + // Used internally by RSAPrivateKey for serializing and deserializing // PKCS #8 PrivateKeyInfo and PublicKeyInfo. class PrivateKeyInfoCodec { public: - // ASN.1 encoding of the AlgorithmIdentifier from PKCS #8. static const uint8 kRsaAlgorithmIdentifier[]; @@ -76,14 +83,14 @@ class PrivateKeyInfoCodec { // Accessors to the contents of the integer components of the PrivateKeyInfo // structure. - std::vector* modulus() { return &modulus_; }; - std::vector* public_exponent() { return &public_exponent_; }; - std::vector* private_exponent() { return &private_exponent_; }; - std::vector* prime1() { return &prime1_; }; - std::vector* prime2() { return &prime2_; }; - std::vector* exponent1() { return &exponent1_; }; - std::vector* exponent2() { return &exponent2_; }; - std::vector* coefficient() { return &coefficient_; }; + std::vector* modulus() { return &modulus_; } + std::vector* public_exponent() { return &public_exponent_; } + std::vector* private_exponent() { return &private_exponent_; } + std::vector* prime1() { return &prime1_; } + std::vector* prime2() { return &prime2_; } + std::vector* exponent1() { return &exponent1_; } + std::vector* exponent2() { return &exponent2_; } + std::vector* coefficient() { return &coefficient_; } private: // Utility wrappers for PrependIntegerImpl that use the class's |big_endian_| @@ -270,5 +277,5 @@ class RSAPrivateKey { } // namespace crypto -#endif // CRYPTO_RSA_PRIVATE_KEY_H_ +#endif // OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_RSA_PRIVATE_KEY_H_ diff --git a/omaha/third_party/chrome/files/src/crypto/signature_creator.h b/omaha/third_party/chrome/files/src/crypto/signature_creator.h index ee210fb7b..55d1b4280 100644 --- a/omaha/third_party/chrome/files/src/crypto/signature_creator.h +++ b/omaha/third_party/chrome/files/src/crypto/signature_creator.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CRYPTO_SIGNATURE_CREATOR_H_ -#define CRYPTO_SIGNATURE_CREATOR_H_ +#ifndef OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_SIGNATURE_CREATOR_H_ +#define OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_SIGNATURE_CREATOR_H_ #include @@ -21,7 +21,7 @@ class SignatureCreator { // Create an instance. The caller must ensure that the provided PrivateKey // instance outlives the created SignatureCreator. - static SignatureCreator* Create(RSAPrivateKey* key); + static SignatureCreator* Create(RSAPrivateKey* key, ALG_ID algorithm_id); // Update the signature with more data. bool Update(const uint8* data_part, int data_part_len); @@ -42,5 +42,5 @@ class SignatureCreator { } // namespace crypto -#endif // CRYPTO_SIGNATURE_CREATOR_H_ +#endif // OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_SIGNATURE_CREATOR_H_ diff --git a/omaha/third_party/chrome/files/src/crypto/signature_creator_unittest.cc b/omaha/third_party/chrome/files/src/crypto/signature_creator_unittest.cc index 79b24eef1..247f4c4f8 100644 --- a/omaha/third_party/chrome/files/src/crypto/signature_creator_unittest.cc +++ b/omaha/third_party/chrome/files/src/crypto/signature_creator_unittest.cc @@ -7,7 +7,7 @@ #include #include -#include "crypto/signature_verifier.h" +#include "crypto/signature_verifier_win.h" #include "omaha/testing/unit_test.h" TEST(SignatureCreatorTest, BasicTest) { @@ -16,39 +16,33 @@ TEST(SignatureCreatorTest, BasicTest) { crypto::RSAPrivateKey::Create(1024)); ASSERT_TRUE(key_original.get()); - std::vector key_info; + std::vector key_info; key_original->ExportPrivateKey(&key_info); std::unique_ptr key( crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info)); ASSERT_TRUE(key.get()); std::unique_ptr signer( - crypto::SignatureCreator::Create(key.get())); + crypto::SignatureCreator::Create(key.get(), CALG_SHA1)); ASSERT_TRUE(signer.get()); std::string data("Hello, World!"); - ASSERT_TRUE(signer->Update(reinterpret_cast(data.c_str()), + ASSERT_TRUE(signer->Update(reinterpret_cast(data.c_str()), static_cast(data.size()))); - std::vector signature; + std::vector signature; ASSERT_TRUE(signer->Final(&signature)); - std::vector public_key_info; + std::vector public_key_info; ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info)); - // This is the algorithm ID for SHA-1 with RSA encryption. - // TODO(aa): Factor this out into some shared location. - const uint8 kSHA1WithRSAAlgorithmID[] = { - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00 - }; - crypto::SignatureVerifier verifier; + crypto::SignatureVerifierWin verifier; ASSERT_TRUE(verifier.VerifyInit( - kSHA1WithRSAAlgorithmID, sizeof(kSHA1WithRSAAlgorithmID), + CALG_SHA1, &signature.front(), static_cast(signature.size()), &public_key_info.front(), static_cast(public_key_info.size()))); - verifier.VerifyUpdate(reinterpret_cast(data.c_str()), + verifier.VerifyUpdate(reinterpret_cast(data.c_str()), static_cast(data.size())); ASSERT_TRUE(verifier.VerifyFinal()); } diff --git a/omaha/third_party/chrome/files/src/crypto/signature_creator_win.cc b/omaha/third_party/chrome/files/src/crypto/signature_creator_win.cc index cc8722d55..3ef6be93a 100644 --- a/omaha/third_party/chrome/files/src/crypto/signature_creator_win.cc +++ b/omaha/third_party/chrome/files/src/crypto/signature_creator_win.cc @@ -11,11 +11,12 @@ namespace crypto { // static -SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key, + ALG_ID algorithm_id) { std::unique_ptr result(new SignatureCreator); result->key_ = key; - if (!CryptCreateHash(key->provider(), CALG_SHA1, 0, 0, + if (!CryptCreateHash(key->provider(), algorithm_id, 0, 0, result->hash_object_.receive())) { ASSERT1(false); return NULL; diff --git a/omaha/third_party/chrome/files/src/crypto/signature_verifier.cc b/omaha/third_party/chrome/files/src/crypto/signature_verifier.cc index 0656709ec..fe2ac51b4 100644 --- a/omaha/third_party/chrome/files/src/crypto/signature_verifier.cc +++ b/omaha/third_party/chrome/files/src/crypto/signature_verifier.cc @@ -31,7 +31,7 @@ namespace crypto { SignatureVerifier::SignatureVerifier() : - hasher_(omaha::CryptDetails::CreateHasher(true)) {} + hasher_(omaha::CryptDetails::CreateHasher()) {} SignatureVerifier::~SignatureVerifier() {} diff --git a/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.cc b/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.cc index 71cfe6ce5..efbf44cd4 100644 --- a/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.cc +++ b/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.cc @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "crypto/signature_verifier.h" +#include "crypto/signature_verifier_win.h" +#include "crypto/rsa_private_key.h" #include "omaha/base/debug.h" +#include "omaha/base/error.h" +#include "omaha/base/logging.h" namespace { @@ -22,21 +25,27 @@ void WINAPI MyCryptFree(void* p) { namespace crypto { -SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) { - if (!CryptAcquireContext(provider_.receive(), NULL, NULL, - PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) +SignatureVerifierWin::SignatureVerifierWin() : hash_object_(0), public_key_(0) { + if (FAILED(CryptAcquireContextWithFallback(PROV_RSA_AES, + provider_.receive()))) { provider_.reset(); + } } -SignatureVerifier::~SignatureVerifier() { +SignatureVerifierWin::~SignatureVerifierWin() { } -bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, - int signature_algorithm_len, - const uint8* signature, - int signature_len, - const uint8* public_key_info, - int public_key_info_len) { +bool SignatureVerifierWin::VerifyInit(ALG_ID algorithm_id, + const uint8_t* signature, + size_t signature_len, + const uint8_t* public_key_info, + size_t public_key_info_len) { + if (algorithm_id != CALG_SHA_256 && algorithm_id != CALG_SHA1) { + REPORT_LOG(LE, (_T("[VerifyInit][Invalid signature algorithm][%d]"), + algorithm_id)); + return false; + } + signature_.reserve(signature_len); // CryptoAPI uses big integers in the little-endian byte order, so we need // to first swap the order of signature bytes. @@ -58,71 +67,57 @@ bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, &decode_para, &cert_public_key_info, &struct_len); - if (!ok) + if (!ok) { + HRESULT hr = omaha::HRESULTFromLastError(); + REPORT_LOG(LE, (_T("[VerifyInit][CryptDecodeObjectEx failed][%#x]"), hr)); return false; + } ok = CryptImportPublicKeyInfo(provider_, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert_public_key_info, public_key_.receive()); free(cert_public_key_info); - if (!ok) + if (!ok) { + HRESULT hr = omaha::HRESULTFromLastError(); + REPORT_LOG(LE, (_T("[VerifyInit][CryptImportPublicKeyInfo failed][%#x]"), + hr)); return false; + } - CRYPT_ALGORITHM_IDENTIFIER* signature_algorithm_id; - struct_len = 0; - ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - X509_ALGORITHM_IDENTIFIER, - signature_algorithm, - signature_algorithm_len, - CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, - &decode_para, - &signature_algorithm_id, - &struct_len); - ASSERT1(ok || GetLastError() == ERROR_FILE_NOT_FOUND); - ALG_ID hash_alg_id; - if (ok) { - hash_alg_id = CALG_MD4; // Initialize to a weak hash algorithm that we - // don't support. - if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_SHA1RSA)) - hash_alg_id = CALG_SHA1; - else if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_MD5RSA)) - hash_alg_id = CALG_MD5; - free(signature_algorithm_id); - ASSERT1(static_cast(CALG_MD4) != hash_alg_id); - if (hash_alg_id == CALG_MD4) - return false; // Unsupported hash algorithm. - } else if (GetLastError() == ERROR_FILE_NOT_FOUND) { - // TODO(wtc): X509_ALGORITHM_IDENTIFIER isn't supported on XP SP2. We - // may be able to encapsulate signature_algorithm in a dummy SignedContent - // and decode it with X509_CERT into a CERT_SIGNED_CONTENT_INFO. For now, - // just hardcode the hash algorithm to be SHA-1. - hash_alg_id = CALG_SHA1; - } else { + ok = CryptCreateHash(provider_, algorithm_id, 0, 0, hash_object_.receive()); + if (!ok) { + HRESULT hr = omaha::HRESULTFromLastError(); + REPORT_LOG(LE, (_T("[VerifyFinal][CryptVerifySignature failed][%#x]"), hr)); return false; } - ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, hash_object_.receive()); - if (!ok) - return false; return true; } -void SignatureVerifier::VerifyUpdate(const uint8* data_part, - int data_part_len) { - CryptHashData(hash_object_, data_part, data_part_len, 0); +void SignatureVerifierWin::VerifyUpdate(const uint8_t* data_part, + size_t data_part_len) { + BOOL ok = CryptHashData(hash_object_, data_part, data_part_len, 0); + if (!ok) { + HRESULT hr = omaha::HRESULTFromLastError(); + REPORT_LOG(LE, (_T("[VerifyUpdate][CryptHashData failed][%#x]"), hr)); + } } -bool SignatureVerifier::VerifyFinal() { +bool SignatureVerifierWin::VerifyFinal() { BOOL ok = CryptVerifySignature(hash_object_, &signature_[0], static_cast(signature_.size()), public_key_, NULL, 0); Reset(); - if (!ok) + if (!ok) { + HRESULT hr = omaha::HRESULTFromLastError(); + REPORT_LOG(LE, (_T("[VerifyFinal][CryptVerifySignature failed][%#x]"), hr)); return false; + } + return true; } -void SignatureVerifier::Reset() { +void SignatureVerifierWin::Reset() { hash_object_.reset(); public_key_.reset(); signature_.clear(); diff --git a/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.h b/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.h new file mode 100644 index 000000000..b0eecdd2a --- /dev/null +++ b/omaha/third_party/chrome/files/src/crypto/signature_verifier_win.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_SIGNATURE_VERIFIER_WIN_H_ +#define OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_SIGNATURE_VERIFIER_WIN_H_ + +#include +#include +#include + +#include "base/basictypes.h" +#include "crypto/scoped_capi_types.h" + +// defines from wincrypt.h. These are copied here because we currently compile +// with support for XP, and these defines are valid for XP SP2 and above. +#define ALG_SID_SHA_256 12 +#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) + +namespace crypto { + +// The SignatureVerifierWin class verifies a signature using a bare public key +// (as opposed to a certificate). +class SignatureVerifierWin { + public: + SignatureVerifierWin(); + ~SignatureVerifierWin(); + + // Streaming interface: + + // Initiates a signature verification operation. This should be followed + // by one or more VerifyUpdate calls and a VerifyFinal call. + // + // The |signature| is encoded according to the |algorithm_id|, but it must not + // be further encoded in an ASN.1 BIT STRING. + // Note: An RSA signatures is actually a big integer. It must be in the + // big-endian byte order. + // + // The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo + // structure, which contains not only the public key but also its type + // (algorithm): + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + bool VerifyInit(ALG_ID algorithm_id, + const uint8_t* signature, + size_t signature_len, + const uint8_t* public_key_info, + size_t public_key_info_len); + + // Feeds a piece of the data to the signature verifier. + void VerifyUpdate(const uint8_t* data_part, size_t data_part_len); + + // Concludes a signature verification operation. Returns true if the + // signature is valid. Returns false if the signature is invalid or an + // error occurred. + bool VerifyFinal(); + + private: + void Reset(); + + std::vector signature_; + ScopedHCRYPTPROV provider_; + ScopedHCRYPTHASH hash_object_; + ScopedHCRYPTKEY public_key_; +}; + +} // namespace crypto + +#endif // OMAHA_THIRD_PARTY_CHROME_FILES_SRC_CRYPTO_SIGNATURE_VERIFIER_WIN_H_ diff --git a/omaha/third_party/chrome/files/src/crypto/signature_verifier_win_unittest.cc b/omaha/third_party/chrome/files/src/crypto/signature_verifier_win_unittest.cc new file mode 100644 index 000000000..201ecebd5 --- /dev/null +++ b/omaha/third_party/chrome/files/src/crypto/signature_verifier_win_unittest.cc @@ -0,0 +1,339 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/signature_verifier_win.h" +#include "omaha/testing/unit_test.h" + +TEST(SignatureVerifierWinTest, BasicTest) { + // The input data in this test comes from real certificates. + // + // tbs_certificate ("to-be-signed certificate", the part of a certificate + // that is signed), signature_algorithm, and algorithm come from the + // certificate of bugs.webkit.org. + // + // public_key_info comes from the certificate of the issuer, Go Daddy Secure + // Certification Authority. + // + // The bytes in the array initializers are formatted to expose the DER + // encoding of the ASN.1 structures. + + // The data that is signed is the following ASN.1 structure: + // TBSCertificate ::= SEQUENCE { + // ... -- omitted, not important + // } + const uint8 tbs_certificate[1017] = { + 0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5) + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, + 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, + 0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38, + 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, + 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72, + 0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28, + 0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5, + 0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0, + 0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c, + 0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1, + 0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff, + 0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39, + 0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca, + 0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1, + 0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb, + 0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e, + 0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30, + 0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, + 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, + 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0, + 0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, + 0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, + 0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, + 0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5, + 0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, + 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, + 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67 + }; + + // RSA signature, a big integer in the big-endian byte order. + const uint8 signature[256] = { + 0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f, + 0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37, + 0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82, + 0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01, + 0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01, + 0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98, + 0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56, + 0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8, + 0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff, + 0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d, + 0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84, + 0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8, + 0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9, + 0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2, + 0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94, + 0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3, + 0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a, + 0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e, + 0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08, + 0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd, + 0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8, + 0x3c, 0xd8, 0x48, 0x8a + }; + + // The public key is specified as the following ASN.1 structure: + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + const uint8 public_key_info[294] = { + 0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122) + // algorithm + 0x30, 0x0d, // a SEQUENCE of length 13 + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, // a NULL of length 0 + // subjectPublicKey + 0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f) + 0x00, // number of unused bits + 0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a) + // modulus + 0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101) + 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec, + 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61, + 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7, + 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c, + 0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87, + 0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b, + 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9, + 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95, + 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00, + 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f, + 0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69, + 0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea, + 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48, + 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66, + 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8, + 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7, + 0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a, + 0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b, + 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0, + 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5, + 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81, + 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87, + 0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97, + 0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37, + 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe, + 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f, + // public exponent + 0x02, 0x03, // an INTEGER of length 3 + 0x01, 0x00, 0x01 + }; + + // We use the signature verifier to perform four signature verification + // tests. + crypto::SignatureVerifierWin verifier; + bool ok; + + // Test 1: feed all of the data to the verifier at once (a single + // VerifyUpdate call). + ok = verifier.VerifyInit(CALG_SHA1, + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 2: feed the data to the verifier in three parts (three VerifyUpdate + // calls). + ok = verifier.VerifyInit(CALG_SHA1, + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, 256); + verifier.VerifyUpdate(tbs_certificate + 256, 256); + verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 3: verify the signature with incorrect data. + uint8 bad_tbs_certificate[sizeof(tbs_certificate)]; + memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate)); + bad_tbs_certificate[10] += 1; // Corrupt one byte of the data. + ok = verifier.VerifyInit(CALG_SHA1, + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + + // Test 4: verify a bad signature. + uint8 bad_signature[sizeof(signature)]; + memcpy(bad_signature, signature, sizeof(signature)); + bad_signature[10] += 1; // Corrupt one byte of the signature. + ok = verifier.VerifyInit(CALG_SHA1, + bad_signature, sizeof(bad_signature), + public_key_info, sizeof(public_key_info)); + + // A crypto library (e.g., NSS) may detect that the signature is corrupted + // and cause VerifyInit to return false, so it is fine for 'ok' to be false. + if (ok) { + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + } +} + +TEST(SignatureVerifierWinTest, Sha256RsaTest) { + // SHA256 RSA signature of data256 signed with the private key corresponding + // to sha256PublicKey. This is a big integer in the big-endian byte order. + const uint8 signature256[256] = { + 0x2E, 0xA0, 0x36, 0x86, 0x31, 0x8C, 0x0C, 0x23, 0xC9, 0x05, 0x0A, 0x1D, + 0xDD, 0x1E, 0x46, 0xD1, 0x84, 0x80, 0x3A, 0xD5, 0xB7, 0x88, 0xB0, 0x63, + 0x34, 0xB5, 0xE2, 0xCE, 0x82, 0xA7, 0x44, 0x96, 0xBD, 0x26, 0xBF, 0xB3, + 0xBA, 0xA5, 0x0D, 0x76, 0x99, 0x45, 0x47, 0x59, 0x15, 0xB8, 0x3B, 0x68, + 0xFF, 0x60, 0xB4, 0xE3, 0x33, 0x64, 0x94, 0x7C, 0x9A, 0x81, 0x9F, 0xA7, + 0xB9, 0xF8, 0x2C, 0xE4, 0xBC, 0xC5, 0x74, 0x01, 0x10, 0xA8, 0x6C, 0xB5, + 0x62, 0xFE, 0xEB, 0x68, 0x4E, 0xB3, 0x23, 0x86, 0x44, 0x0C, 0x57, 0xFB, + 0x96, 0x89, 0xE4, 0x06, 0x47, 0x4B, 0xA4, 0x10, 0x7E, 0x2E, 0xF1, 0x89, + 0xBF, 0xA2, 0x29, 0x12, 0x61, 0x7F, 0x67, 0xD5, 0xA2, 0x65, 0x8C, 0xA5, + 0xFF, 0x9F, 0x84, 0xCF, 0x13, 0x40, 0x2E, 0xD5, 0x2E, 0xA2, 0x31, 0x5E, + 0x00, 0xDC, 0x76, 0x39, 0x6C, 0x7B, 0x17, 0xF9, 0x38, 0xD8, 0x09, 0x2C, + 0x58, 0x7B, 0x9C, 0x38, 0x8E, 0x7B, 0xCF, 0xB7, 0x1C, 0x78, 0x9F, 0x4C, + 0x71, 0x8A, 0x76, 0x6C, 0xC5, 0xD8, 0x1D, 0x07, 0x50, 0x3F, 0x24, 0x47, + 0x7E, 0x48, 0xE6, 0xE7, 0x3E, 0x25, 0xD0, 0x3F, 0x08, 0x1F, 0xC9, 0xF8, + 0x9E, 0xB2, 0xCE, 0x2E, 0xFF, 0x24, 0x4F, 0xA2, 0xE4, 0x60, 0x91, 0xCE, + 0xEB, 0x29, 0xED, 0x1F, 0x16, 0xD0, 0x42, 0xBD, 0x99, 0x5A, 0x0F, 0x69, + 0xDC, 0x8F, 0x34, 0xE8, 0xE9, 0x40, 0xCB, 0x1A, 0x0B, 0x73, 0x78, 0x2A, + 0xB9, 0xE9, 0xA6, 0x1F, 0x51, 0x20, 0x9E, 0xFD, 0xE7, 0x89, 0xB4, 0xB2, + 0xA1, 0xC3, 0x5C, 0x75, 0x8C, 0xB2, 0xC3, 0x7D, 0x1E, 0x3F, 0xD1, 0x08, + 0xA6, 0x15, 0xE5, 0xE7, 0xA5, 0x0E, 0xE4, 0x5D, 0x94, 0x9D, 0x9D, 0xD9, + 0xE5, 0x07, 0x95, 0x50, 0x32, 0x4F, 0x39, 0x8F, 0x44, 0x5D, 0x46, 0x34, + 0x7F, 0x42, 0xAA, 0x6A + }; + + // Public key corresponding to the private key used to sign data256. + const uint8_t sha256PublicKey[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, + 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xA7, 0xB3, 0xF9, + 0x0D, 0xC7, 0xC7, 0x8D, 0x84, 0x3D, 0x4B, 0x80, 0xDD, 0x9A, 0x2F, 0xF8, + 0x69, 0xD4, 0xD1, 0x14, 0x5A, 0xCA, 0x04, 0x4B, 0x1C, 0xBC, 0x28, 0xEB, + 0x5E, 0x10, 0x01, 0x36, 0xFD, 0x81, 0xEB, 0xE4, 0x3C, 0x16, 0x40, 0xA5, + 0x8A, 0xE6, 0x08, 0xEE, 0xEF, 0x39, 0x1F, 0x6B, 0x10, 0x29, 0x50, 0x84, + 0xCE, 0xEE, 0x33, 0x5C, 0x48, 0x4A, 0x33, 0xB0, 0xC8, 0x8A, 0x66, 0x0D, + 0x10, 0x11, 0x9D, 0x6B, 0x55, 0x4C, 0x9A, 0x62, 0x40, 0x9A, 0xE2, 0xCA, + 0x21, 0x01, 0x1F, 0x10, 0x1E, 0x7B, 0xC6, 0x89, 0x94, 0xDA, 0x39, 0x69, + 0xBE, 0x27, 0x28, 0x50, 0x5E, 0xA2, 0x55, 0xB9, 0x12, 0x3C, 0x79, 0x6E, + 0xDF, 0x24, 0xBF, 0x34, 0x88, 0xF2, 0x5E, 0xD0, 0xC4, 0x06, 0xEE, 0x95, + 0x6D, 0xC2, 0x14, 0xBF, 0x51, 0x7E, 0x3F, 0x55, 0x10, 0x85, 0xCE, 0x33, + 0x8F, 0x02, 0x87, 0xFC, 0xD2, 0xDD, 0x42, 0xAF, 0x59, 0xBB, 0x69, 0x3D, + 0xBC, 0x77, 0x4B, 0x3F, 0xC7, 0x22, 0x0D, 0x5F, 0x72, 0xC7, 0x36, 0xB6, + 0x98, 0x3D, 0x03, 0xCD, 0x2F, 0x68, 0x61, 0xEE, 0xF4, 0x5A, 0xF5, 0x07, + 0xAE, 0xAE, 0x79, 0xD1, 0x1A, 0xB2, 0x38, 0xE0, 0xAB, 0x60, 0x5C, 0x0C, + 0x14, 0xFE, 0x44, 0x67, 0x2C, 0x8A, 0x08, 0x51, 0x9C, 0xCD, 0x3D, 0xDB, + 0x13, 0x04, 0x57, 0xC5, 0x85, 0xB6, 0x2A, 0x0F, 0x02, 0x46, 0x0D, 0x2D, + 0xCA, 0xE3, 0x3F, 0x84, 0x9E, 0x8B, 0x8A, 0x5F, 0xFC, 0x4D, 0xAA, 0xBE, + 0xBD, 0xE6, 0x64, 0x9F, 0x26, 0x9A, 0x2B, 0x97, 0x69, 0xA9, 0xBA, 0x0B, + 0xBD, 0x48, 0xE4, 0x81, 0x6B, 0xD4, 0x4B, 0x78, 0xE6, 0xAF, 0x95, 0x66, + 0xC1, 0x23, 0xDA, 0x23, 0x45, 0x36, 0x6E, 0x25, 0xF3, 0xC7, 0xC0, 0x61, + 0xFC, 0xEC, 0x66, 0x9D, 0x31, 0xD4, 0xD6, 0xB6, 0x36, 0xE3, 0x7F, 0x81, + 0x87, 0x02, 0x03, 0x01, 0x00, 0x01 + }; + + // Data to be verified, signed with the private key corresponding to + // sha256PublicKey. + const uint8 data256[322] = { + 0x0A, 0xA6, 0x02, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, + 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, + 0x90, 0x58, 0x37, 0x8E, 0xEB, 0x21, 0x1E, 0xD2, 0x13, 0xCA, 0xB6, 0x19, + 0x4C, 0x6E, 0x0E, 0x00, 0xDB, 0x58, 0xB2, 0xFF, 0x61, 0xB1, 0x4F, 0x1F, + 0x13, 0xF7, 0x97, 0x38, 0xA7, 0x41, 0xF2, 0x15, 0x3E, 0x53, 0x87, 0x79, + 0xEF, 0x7B, 0x66, 0xD3, 0x6D, 0x39, 0x63, 0xBC, 0x31, 0xBE, 0xBF, 0x5C, + 0x4B, 0xB5, 0x00, 0x62, 0xE4, 0x36, 0xC4, 0x58, 0x04, 0xAC, 0x36, 0x06, + 0xB0, 0x26, 0xEB, 0x90, 0xEA, 0xFC, 0x17, 0x27, 0xB5, 0xED, 0xFB, 0x2B, + 0xEE, 0x33, 0xF1, 0xBE, 0x99, 0xA9, 0x0B, 0xE7, 0x0B, 0x6D, 0x4A, 0xC6, + 0xE0, 0xB1, 0x07, 0xC8, 0x49, 0xDD, 0x79, 0xCC, 0x32, 0x3E, 0x6F, 0x67, + 0x36, 0x33, 0x78, 0xAD, 0x2B, 0x15, 0x57, 0x96, 0x98, 0x40, 0xB6, 0x67, + 0xD6, 0x90, 0x8C, 0x85, 0xE7, 0x66, 0xAF, 0xC8, 0x84, 0x58, 0xBA, 0xA5, + 0x8C, 0xA7, 0x2C, 0x50, 0xBB, 0x66, 0x5E, 0x7B, 0xAE, 0x13, 0x4E, 0xF3, + 0x61, 0xDB, 0x63, 0x29, 0x53, 0xB4, 0x45, 0x88, 0xE1, 0x09, 0xE2, 0x57, + 0x5B, 0x19, 0xFF, 0x8C, 0xD2, 0x92, 0x4A, 0xEF, 0x0C, 0x86, 0xCF, 0xA1, + 0x8E, 0x68, 0x26, 0x77, 0x88, 0x1F, 0x1B, 0x82, 0x05, 0xFA, 0x01, 0xFE, + 0x5A, 0xD8, 0x47, 0xE3, 0x07, 0x68, 0x4B, 0x86, 0x92, 0x6A, 0xAB, 0x69, + 0xE9, 0x02, 0x81, 0xB9, 0x63, 0xCB, 0x43, 0x22, 0x53, 0xCB, 0xE8, 0xBD, + 0xF4, 0xFF, 0xEC, 0xB8, 0xC3, 0x4F, 0xD1, 0xBC, 0x6F, 0xC7, 0xF8, 0xBE, + 0x0B, 0xDC, 0x22, 0x40, 0xB6, 0x85, 0xC4, 0x88, 0xDF, 0xD4, 0xBD, 0xD7, + 0xAE, 0x85, 0x02, 0xEA, 0xFC, 0xD2, 0x05, 0xEC, 0xBD, 0x53, 0x42, 0x05, + 0xA5, 0xD5, 0x92, 0x09, 0x85, 0xA7, 0xC8, 0x40, 0xBD, 0x8B, 0xE2, 0xF4, + 0x96, 0x68, 0x4F, 0x80, 0x14, 0x29, 0x9A, 0x92, 0x72, 0x39, 0x03, 0x36, + 0x3C, 0x24, 0xD8, 0x19, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12, 0x15, 0x62, + 0x72, 0x6F, 0x77, 0x73, 0x65, 0x72, 0x6D, 0x61, 0x6E, 0x61, 0x67, 0x65, + 0x6D, 0x65, 0x6E, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x18, 0x00 + }; + + crypto::SignatureVerifierWin verifier; + EXPECT_TRUE(verifier.VerifyInit(CALG_SHA_256, + signature256, sizeof(signature256), + sha256PublicKey, sizeof(sha256PublicKey))); + verifier.VerifyUpdate(data256, sizeof(data256)); + EXPECT_TRUE(verifier.VerifyFinal()); +} diff --git a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npapi.h b/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npapi.h deleted file mode 100644 index 1e6e7e2be..000000000 --- a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npapi.h +++ /dev/null @@ -1,932 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -/* - * Netscape client plug-in API spec - */ - - -#ifndef _NPAPI_H_ -#define _NPAPI_H_ - - -/* BEGIN GOOGLE MODIFICATIONS */ - -#ifdef __native_client__ -#include -#else -#include "base/basictypes.h" -#endif /* __native_client__ */ - -/* END GOOGLE MODIFICATIONS */ - -#ifdef INCLUDE_JAVA -#include "jri.h" /* Java Runtime Interface */ -#else -#define jref void * -#define JRIEnv void -#endif - -#ifdef _WIN32 -# ifndef XP_WIN -# define XP_WIN 1 -# endif /* XP_WIN */ -#endif /* _WIN32 */ - -/* BEGIN GOOGLE MODIFICATIONS */ -/* On Linux and Mac, be sure to set Mozilla-specific macros. */ -#if defined(USE_X11) -#define XP_UNIX 1 -#define MOZ_X11 1 -#endif -/* END GOOGLE MODIFICATIONS */ - -#ifdef __MWERKS__ -# define _declspec __declspec -# ifdef macintosh -# ifndef XP_MAC -# define XP_MAC 1 -# endif /* XP_MAC */ -# endif /* macintosh */ -# ifdef __INTEL__ -# undef NULL -# ifndef XP_WIN -# define XP_WIN 1 -# endif /* __INTEL__ */ -# endif /* XP_PC */ -#endif /* __MWERKS__ */ - -#ifdef __SYMBIAN32__ -# ifndef XP_SYMBIAN -# define XP_SYMBIAN 1 -# undef XP_WIN -# endif -#endif /* __SYMBIAN32__ */ - -#if defined(__APPLE_CC__) && !defined(__MACOS_CLASSIC__) && !defined(XP_UNIX) -# define XP_MACOSX -#endif - -#ifdef XP_MAC -#include -#include -#endif - -#if defined(XP_MACOSX) && defined(__LP64__) -#define NP_NO_QUICKDRAW -#define NP_NO_CARBON -#endif - -#ifdef XP_MACOSX -#include -#include -#ifndef NP_NO_CARBON -#include -#endif -#endif - -#if defined(XP_UNIX) -# include -/* BEGIN GOOGLE MODIFICATIONS */ -#if 0 -/* END GOOGLE MODIFICATIONS */ -# if defined(MOZ_X11) -# include -# include -# endif -/* BEGIN GOOGLE MODIFICATIONS */ -#endif -/* END GOOGLE MODIFICATIONS */ -#endif - -#ifdef XP_WIN -#include -#endif - -/*----------------------------------------------------------------------*/ -/* Plugin Version Constants */ -/*----------------------------------------------------------------------*/ - -#define NP_VERSION_MAJOR 0 -/* BEGIN GOOGLE MODIFICATIONS */ -#define NP_VERSION_MINOR 23 /* maximum version currently supported by Chromium */ -/* END GOOGLE MODIFICATIONS */ - - -/*----------------------------------------------------------------------*/ -/* Definition of Basic Types */ -/*----------------------------------------------------------------------*/ - -/* QNX sets the _INT16 and friends defines, but does not typedef the types */ -#ifdef __QNXNTO__ -#undef _UINT16 -#undef _INT16 -#undef _UINT32 -#undef _INT32 -#endif - -#ifndef _UINT16 -#define _UINT16 -typedef unsigned short uint16; -#endif - -#ifndef _UINT32 -#define _UINT32 -#ifdef __LP64__ -typedef unsigned int uint32; -#else /* __LP64__ */ -typedef unsigned long uint32; -#endif /* __LP64__ */ -#endif - -#ifndef _INT16 -#define _INT16 -typedef short int16; -#endif - -#ifndef _INT32 -#define _INT32 -#ifdef __LP64__ -typedef int int32; -#else /* __LP64__ */ -typedef long int32; -#endif /* __LP64__ */ -#endif - -#ifndef FALSE -#define FALSE (0) -#endif -#ifndef TRUE -#define TRUE (1) -#endif -#ifndef NULL -#define NULL (0L) -#endif - -typedef unsigned char NPBool; -typedef int16 NPError; -typedef int16 NPReason; -typedef char* NPMIMEType; - - - -/*----------------------------------------------------------------------*/ -/* Structures and definitions */ -/*----------------------------------------------------------------------*/ - -#if !defined(__LP64__) -#if defined(XP_MAC) || defined(XP_MACOSX) -#pragma options align=mac68k -#endif -#endif /* __LP64__ */ - -/* - * NPP is a plug-in's opaque instance handle - */ -typedef struct _NPP -{ - void* pdata; /* plug-in private data */ - void* ndata; /* netscape private data */ -} NPP_t; - -typedef NPP_t* NPP; - - -typedef struct _NPStream -{ - void* pdata; /* plug-in private data */ - void* ndata; /* netscape private data */ - const char* url; - uint32 end; - uint32 lastmodified; - void* notifyData; - const char* headers; /* Response headers from host. - * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS. - * Used for HTTP only; NULL for non-HTTP. - * Available from NPP_NewStream onwards. - * Plugin should copy this data before storing it. - * Includes HTTP status line and all headers, - * preferably verbatim as received from server, - * headers formatted as in HTTP ("Header: Value"), - * and newlines (\n, NOT \r\n) separating lines. - * Terminated by \n\0 (NOT \n\n\0). */ -} NPStream; - - -typedef struct _NPByteRange -{ - int32 offset; /* negative offset means from the end */ - uint32 length; - struct _NPByteRange* next; -} NPByteRange; - - -typedef struct _NPSavedData -{ - int32 len; - void* buf; -} NPSavedData; - - -typedef struct _NPRect -{ - uint16 top; - uint16 left; - uint16 bottom; - uint16 right; -} NPRect; - - -#ifdef XP_UNIX -/* - * Unix specific structures and definitions - */ - -/* - * Callback Structures. - * - * These are used to pass additional platform specific information. - */ -enum { - NP_SETWINDOW = 1, - NP_PRINT -}; - -typedef struct -{ - int32 type; -} NPAnyCallbackStruct; - -/* BEGIN GOOGLE MODIFICATIONS */ -typedef struct _NPSetWindowCallbackStruct NPSetWindowCallbackStruct; -/* END GOOGLE MODIFICATIONS */ - -typedef struct -{ - int32 type; - FILE* fp; -} NPPrintCallbackStruct; - -#endif /* XP_UNIX */ - - -/* - * The following masks are applied on certain platforms to NPNV and - * NPPV selectors that pass around pointers to COM interfaces. Newer - * compilers on some platforms may generate vtables that are not - * compatible with older compilers. To prevent older plugins from - * not understanding a new browser's ABI, these masks change the - * values of those selectors on those platforms. To remain backwards - * compatible with differenet versions of the browser, plugins can - * use these masks to dynamically determine and use the correct C++ - * ABI that the browser is expecting. This does not apply to Windows - * as Microsoft's COM ABI will likely not change. - */ - -#define NP_ABI_GCC3_MASK 0x10000000 -/* - * gcc 3.x generated vtables on UNIX and OSX are incompatible with - * previous compilers. - */ -#if (defined (XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3)) -#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK -#else -#define _NP_ABI_MIXIN_FOR_GCC3 0 -#endif - -#define NP_ABI_MACHO_MASK 0x01000000 -/* - * On OSX, the Mach-O executable format is significantly - * different than CFM. In addition to having a different - * C++ ABI, it also has has different C calling convention. - * You must use glue code when calling between CFM and - * Mach-O C functions. - */ -#if (defined(TARGET_RT_MAC_MACHO)) -#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK -#else -#define _NP_ABI_MIXIN_FOR_MACHO 0 -#endif - - -#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO) - -/* - * List of variable names for which NPP_GetValue shall be implemented - */ -typedef enum { - NPPVpluginNameString = 1, - NPPVpluginDescriptionString, - NPPVpluginWindowBool, - NPPVpluginTransparentBool, - NPPVjavaClass, /* Not implemented in Mozilla 1.0 */ - NPPVpluginWindowSize, - NPPVpluginTimerInterval, - - NPPVpluginScriptableInstance = (10 | NP_ABI_MASK), - NPPVpluginScriptableIID = 11, - - /* Introduced in Mozilla 0.9.9 */ - NPPVjavascriptPushCallerBool = 12, - - /* Introduced in Mozilla 1.0 */ - NPPVpluginKeepLibraryInMemory = 13, - NPPVpluginNeedsXEmbed = 14, - - /* Get the NPObject for scripting the plugin. Introduced in Firefox - * 1.0 (NPAPI minor version 14). - */ - NPPVpluginScriptableNPObject = 15, - - /* Get the plugin value (as \0-terminated UTF-8 string data) for - * form submission if the plugin is part of a form. Use - * NPN_MemAlloc() to allocate memory for the string data. - */ - NPPVformValue = 16, /* Not implemented in WebKit */ - - NPPVpluginUrlRequestsDisplayedBool = 17, /* Not implemented in WebKit */ - - /* Checks if the plugin is interested in receiving the http body of - * failed http requests (http status != 200). - */ - NPPVpluginWantsAllNetworkStreams = 18, - - /* Checks to see if the plug-in would like the browser to load the "src" attribute. */ - NPPVpluginCancelSrcStream = 20 - -#ifdef XP_MACOSX - /* Used for negotiating drawing models */ - , NPPVpluginDrawingModel = 1000, - /* Used for negotiating event models */ - NPPVpluginEventModel = 1001, - /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */ - NPPVpluginCoreAnimationLayer = 1003 -#endif -} NPPVariable; - -/* - * List of variable names for which NPN_GetValue is implemented by Mozilla - */ -typedef enum { - NPNVxDisplay = 1, - NPNVxtAppContext, - NPNVnetscapeWindow, - NPNVjavascriptEnabledBool, - NPNVasdEnabledBool, - NPNVisOfflineBool, - - /* 10 and over are available on Mozilla builds starting with 0.9.4 */ - NPNVserviceManager = (10 | NP_ABI_MASK), - NPNVDOMElement = (11 | NP_ABI_MASK), /* available in Mozilla 1.2 */ - NPNVDOMWindow = (12 | NP_ABI_MASK), - NPNVToolkit = (13 | NP_ABI_MASK), - NPNVSupportsXEmbedBool = 14, - - /* Get the NPObject wrapper for the browser window. */ - NPNVWindowNPObject = 15, - - /* Get the NPObject wrapper for the plugins DOM element. */ - NPNVPluginElementNPObject = 16, - - NPNVSupportsWindowless = 17, - - NPNVprivateModeBool = 18 - -#ifdef XP_MACOSX - /* Used for negotiating drawing models */ - , NPNVpluginDrawingModel = 1000 -#ifndef NP_NO_QUICKDRAW - , NPNVsupportsQuickDrawBool = 2000 -#endif - , NPNVsupportsCoreGraphicsBool = 2001 - , NPNVsupportsOpenGLBool = 2002 /* TRUE if the browser supports the OpenGL drawing model (CGL on Mac) */ - , NPNVsupportsCoreAnimationBool = 2003 /* TRUE if the browser supports the CoreAnimation drawing model */ - -#ifndef NP_NO_CARBON - , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */ -#endif - , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */ -#endif -} NPNVariable; - -typedef enum { - NPNURLVCookie = 501, - NPNURLVProxy -} NPNURLVariable; - -/* BEGIN GOOGLE MODIFICATIONS */ -/* - * The type of Tookkit the widgets use - */ -typedef enum { - NPNVGtk12 = 1, - NPNVGtk2 -} NPNToolkitType; -/* END GOOGLE MODIFICATIONS */ - -/* - * The type of a NPWindow - it specifies the type of the data structure - * returned in the window field. - */ -typedef enum { - NPWindowTypeWindow = 1, - NPWindowTypeDrawable -} NPWindowType; - -#ifdef XP_MACOSX - -/* - * The drawing model for a Mac OS X plugin. These are the possible values for the NPNVpluginDrawingModel variable. - */ - -typedef enum { -#ifndef NP_NO_QUICKDRAW - NPDrawingModelQuickDraw = 0, -#endif - NPDrawingModelCoreGraphics = 1, - NPDrawingModelOpenGL = 2, - NPDrawingModelCoreAnimation = 3 -} NPDrawingModel; - -/* - * The event model for a Mac OS X plugin. These are the possible values for the NPNVpluginEventModel variable. - */ - -typedef enum { -#ifndef NP_NO_CARBON - NPEventModelCarbon = 0, -#endif - NPEventModelCocoa = 1 -} NPEventModel; - -typedef enum { - NPCocoaEventDrawRect = 1, - NPCocoaEventMouseDown, - NPCocoaEventMouseUp, - NPCocoaEventMouseMoved, - NPCocoaEventMouseEntered, - NPCocoaEventMouseExited, - NPCocoaEventMouseDragged, - NPCocoaEventKeyDown, - NPCocoaEventKeyUp, - NPCocoaEventFlagsChanged, - NPCocoaEventFocusChanged, - NPCocoaEventWindowFocusChanged, - NPCocoaEventScrollWheel, - NPCocoaEventTextInput -} NPCocoaEventType; - -typedef struct _NPNSString NPNSString; -typedef struct _NPNSWindow NPNSWindow; -typedef struct _NPNSMenu NPNSMenu; - -typedef struct _NPCocoaEvent { - NPCocoaEventType type; - uint32 version; - - union { - struct { - uint32 modifierFlags; - double pluginX; - double pluginY; - int32 buttonNumber; - int32 clickCount; - double deltaX; - double deltaY; - double deltaZ; - } mouse; - struct { - uint32 modifierFlags; - NPNSString *characters; - NPNSString *charactersIgnoringModifiers; - NPBool isARepeat; - uint16 keyCode; - } key; - struct { - CGContextRef context; - - double x; - double y; - double width; - double height; - } draw; - struct { - NPBool hasFocus; - } focus; - struct { - NPNSString *text; - } text; - } data; -} NPCocoaEvent; - -#endif - -typedef struct _NPWindow -{ - void* window; /* Platform specific window handle */ - int32 x; /* Position of top left corner relative */ - int32 y; /* to a netscape page. */ - uint32 width; /* Maximum window size */ - uint32 height; - NPRect clipRect; /* Clipping rectangle in port coordinates */ - /* Used by MAC only. */ -#if defined(XP_UNIX) && !defined(XP_MACOSX) - void * ws_info; /* Platform-dependent additonal data */ -#endif /* XP_UNIX */ - NPWindowType type; /* Is this a window or a drawable? */ -} NPWindow; - - -typedef struct _NPFullPrint -{ - NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */ - NPBool printOne; /* TRUE if plugin should print one copy to default printer */ - void* platformPrint; /* Platform-specific printing info */ -} NPFullPrint; - -typedef struct _NPEmbedPrint -{ - NPWindow window; - void* platformPrint; /* Platform-specific printing info */ -} NPEmbedPrint; - -typedef struct _NPPrint -{ - uint16 mode; /* NP_FULL or NP_EMBED */ - union - { - NPFullPrint fullPrint; /* if mode is NP_FULL */ - NPEmbedPrint embedPrint; /* if mode is NP_EMBED */ - } print; -} NPPrint; - -#ifdef XP_MACOSX -/* BEGIN GOOGLE MODIFICATIONS */ -typedef struct _NPNSMenu NPNSMenu; -/* END GOOGLE MODIFICATIONS */ -typedef NPNSMenu NPMenu; -#else -typedef void * NPMenu; -#endif - -typedef enum { - NPCoordinateSpacePlugin = 1, - NPCoordinateSpaceWindow, - NPCoordinateSpaceFlippedWindow, - NPCoordinateSpaceScreen, - NPCoordinateSpaceFlippedScreen -} NPCoordinateSpace; - -#if defined(XP_MAC) || defined(XP_MACOSX) - -#ifndef NP_NO_CARBON -typedef EventRecord NPEvent; -#endif - -#elif defined(XP_WIN) -typedef struct _NPEvent -{ - uint16 event; - uint32 wParam; - uint32 lParam; -} NPEvent; -#elif defined (XP_UNIX) && defined(MOZ_X11) -/* BEGIN GOOGLE MODIFICATIONS */ -typedef union _XEvent XEvent; -/* END GOOGLE MODIFICATIONS */ -typedef XEvent NPEvent; -#else -typedef void* NPEvent; -#endif /* XP_MACOSX */ - -#if defined(XP_MAC) -typedef RgnHandle NPRegion; -#elif defined(XP_MACOSX) -/* - * NPRegion's type depends on the drawing model specified by the plugin (see NPNVpluginDrawingModel). - * NPQDRegion represents a QuickDraw RgnHandle and is used with the QuickDraw drawing model. - * NPCGRegion repesents a graphical region when using any other drawing model. - */ -typedef void *NPRegion; -#ifndef NP_NO_QUICKDRAW -typedef RgnHandle NPQDRegion; -#endif -typedef CGPathRef NPCGRegion; -#elif defined(XP_WIN) -typedef HRGN NPRegion; -#elif defined(XP_UNIX) -/* BEGIN GOOGLE MODIFICATIONS */ -typedef struct _XRegion *Region; -/* END GOOGLE MODIFICATIONS */ -typedef Region NPRegion; -#else -typedef void *NPRegion; -#endif /* XP_MAC */ - -#ifdef XP_MACOSX - -/* - * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics - * as its drawing model. - */ - -typedef struct NP_CGContext -{ - CGContextRef context; -#ifdef NP_NO_CARBON - NPNSWindow *window; -#else - void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */ -#endif -} NP_CGContext; - -/* - * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its - * drawing model. - */ - -typedef struct NP_GLContext -{ - CGLContextObj context; -#ifdef NP_NO_CARBON - NPNSWindow *window; -#else - void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */ -#endif -} NP_GLContext; - -#endif /* XP_MACOSX */ - -#if defined(XP_MAC) || defined(XP_MACOSX) - -/* - * Mac-specific structures and definitions. - */ - -#ifndef NP_NO_QUICKDRAW - -/* - * NP_Port is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelQuickDraw as its - * drawing model, or the plugin does not specify a drawing model. - * - * It is not recommended that new plugins use NPDrawingModelQuickDraw or NP_Port, as QuickDraw has been - * deprecated in Mac OS X 10.5. CoreGraphics is the preferred drawing API. - * - * NP_Port is not available in 64-bit. - */ - -typedef struct NP_Port -{ - CGrafPtr port; /* Grafport */ - int32 portx; /* position inside the topmost window */ - int32 porty; -} NP_Port; - -#endif /* NP_NO_QUICKDRAW */ - -/* - * Non-standard event types that can be passed to HandleEvent - */ -/* BEGIN GOOGLE MODIFICATIONS */ -#ifndef NP_NO_CARBON -enum NPEventType { - NPEventType_GetFocusEvent = (osEvt + 16), - NPEventType_LoseFocusEvent, - NPEventType_AdjustCursorEvent, - NPEventType_MenuCommandEvent, - NPEventType_ClippingChangedEvent, - NPEventType_ScrollingBeginsEvent = 1000, - NPEventType_ScrollingEndsEvent -}; - -#ifdef OBSOLETE -#define getFocusEvent (osEvt + 16) -#define loseFocusEvent (osEvt + 17) -#define adjustCursorEvent (osEvt + 18) -#endif -#endif /* NP_NO_CARBON */ -/* END GOOGLE MODIFICATIONS */ - -#endif /* XP_MACOSX */ - -/* - * Values for mode passed to NPP_New: - */ -#define NP_EMBED 1 -#define NP_FULL 2 - -/* - * Values for stream type passed to NPP_NewStream: - */ -#define NP_NORMAL 1 -#define NP_SEEK 2 -#define NP_ASFILE 3 -#define NP_ASFILEONLY 4 - -#define NP_MAXREADY (((unsigned)(~0)<<1)>>1) - -#if !defined(__LP64__) -#if defined(XP_MAC) || defined(XP_MACOSX) -#pragma options align=reset -#endif -#endif /* __LP64__ */ - - -/*----------------------------------------------------------------------*/ -/* Error and Reason Code definitions */ -/*----------------------------------------------------------------------*/ - -/* - * Values of type NPError: - */ -#define NPERR_BASE 0 -#define NPERR_NO_ERROR (NPERR_BASE + 0) -#define NPERR_GENERIC_ERROR (NPERR_BASE + 1) -#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2) -#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3) -#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4) -#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5) -#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6) -#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7) -#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8) -#define NPERR_INVALID_PARAM (NPERR_BASE + 9) -#define NPERR_INVALID_URL (NPERR_BASE + 10) -#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11) -#define NPERR_NO_DATA (NPERR_BASE + 12) -#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13) - -/* - * Values of type NPReason: - */ -#define NPRES_BASE 0 -#define NPRES_DONE (NPRES_BASE + 0) -#define NPRES_NETWORK_ERR (NPRES_BASE + 1) -#define NPRES_USER_BREAK (NPRES_BASE + 2) - -/* - * Don't use these obsolete error codes any more. - */ -#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR -#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR -#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK - -/* - * Version feature information - */ -#define NPVERS_HAS_STREAMOUTPUT 8 -#define NPVERS_HAS_NOTIFICATION 9 -#define NPVERS_HAS_LIVECONNECT 9 -#define NPVERS_WIN16_HAS_LIVECONNECT 9 -#define NPVERS_68K_HAS_LIVECONNECT 11 -#define NPVERS_HAS_WINDOWLESS 11 -#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 -#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14 -#define NPVERS_HAS_FORM_VALUES 15 -#define NPVERS_HAS_POPUPS_ENABLED_STATE 16 -#define NPVERS_HAS_RESPONSE_HEADERS 17 -#define NPVERS_HAS_NPOBJECT_ENUM 18 -#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19 -#define NPVERS_HAS_ALL_NETWORK_STREAMS 20 -#define NPVERS_HAS_URL_AND_AUTH_INFO 21 -#define NPVERS_HAS_PRIVATE_MODE 22 -#define NPVERS_MACOSX_HAS_EVENT_MODELS 23 -#define NPVERS_HAS_CANCEL_SRC_STREAM 24 - -/*----------------------------------------------------------------------*/ -/* Function Prototypes */ -/*----------------------------------------------------------------------*/ - -#if defined(_WINDOWS) && !defined(WIN32) -#define NP_LOADDS _loadds -#else -#define NP_LOADDS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * NPP_* functions are provided by the plugin and called by the navigator. - */ - -#ifdef XP_UNIX -char* NPP_GetMIMEDescription(void); -#endif /* XP_UNIX */ - -NPError NPP_Initialize(void); -void NPP_Shutdown(void); -NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, - uint16 mode, int16 argc, char* argn[], - char* argv[], NPSavedData* saved); -NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save); -NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window); -NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type, - NPStream* stream, NPBool seekable, - uint16* stype); -NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, - NPReason reason); -int32 NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream); -int32 NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32 offset, - int32 len, void* buffer); -void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream, - const char* fname); -void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint); -int16 NPP_HandleEvent(NPP instance, void* event); -void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, - NPReason reason, void* notifyData); -jref NP_LOADDS NPP_GetJavaClass(void); -NPError NPP_GetValue(NPP instance, NPPVariable variable, - void *value); -NPError NPP_SetValue(NPP instance, NPNVariable variable, - void *value); - -/* - * NPN_* functions are provided by the navigator and called by the plugin. - */ - -void NPN_Version(int* plugin_major, int* plugin_minor, - int* netscape_major, int* netscape_minor); -NPError NPN_GetURLNotify(NPP instance, const char* url, - const char* target, void* notifyData); -NPError NPN_GetURL(NPP instance, const char* url, - const char* target); -NPError NPN_PostURLNotify(NPP instance, const char* url, - const char* target, uint32 len, - const char* buf, NPBool file, - void* notifyData); -NPError NPN_PostURL(NPP instance, const char* url, - const char* target, uint32 len, - const char* buf, NPBool file); -NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList); -NPError NPN_NewStream(NPP instance, NPMIMEType type, - const char* target, NPStream** stream); -int32 NPN_Write(NPP instance, NPStream* stream, int32 len, - void* buffer); -NPError NPN_DestroyStream(NPP instance, NPStream* stream, - NPReason reason); -void NPN_Status(NPP instance, const char* message); -const char* NPN_UserAgent(NPP instance); -void* NPN_MemAlloc(uint32 size); -void NPN_MemFree(void* ptr); -uint32 NPN_MemFlush(uint32 size); -void NPN_ReloadPlugins(NPBool reloadPages); -JRIEnv* NPN_GetJavaEnv(void); -jref NPN_GetJavaPeer(NPP instance); -NPError NPN_GetValue(NPP instance, NPNVariable variable, - void *value); -NPError NPN_SetValue(NPP instance, NPPVariable variable, - void *value); -void NPN_InvalidateRect(NPP instance, NPRect *invalidRect); -void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion); -void NPN_ForceRedraw(NPP instance); -void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); -void NPN_PopPopupsEnabledState(NPP instance); -void NPN_PluginThreadAsyncCall(NPP instance, void (*func) (void *), void *userData); -NPError NPN_GetValueForURL(NPP instance, NPNURLVariable variable, const char* url, char** value, uint32* len); -NPError NPN_SetValueForURL(NPP instance, NPNURLVariable variable, const char* url, const char* value, uint32 len); -NPError NPN_GetAuthenticationInfo(NPP instance, const char* protocol, const char* host, int32 port, const char* scheme, const char *realm, char** username, uint32* ulen, char** password, uint32* plen); -uint32 NPN_ScheduleTimer(NPP instance, uint32 interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32 timerID)); -void NPN_UnscheduleTimer(NPP instance, uint32 timerID); -NPError NPN_PopUpContextMenu(NPP instance, NPMenu* menu); -NPBool NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); - -#ifdef __cplusplus -} /* end extern "C" */ -#endif - -#endif /* _NPAPI_H_ */ diff --git a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npapi_extensions.h b/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npapi_extensions.h deleted file mode 100644 index f8c05f47b..000000000 --- a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npapi_extensions.h +++ /dev/null @@ -1,646 +0,0 @@ -/* Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef _NP_EXTENSIONS_H_ -#define _NP_EXTENSIONS_H_ - -// Use the shorter include path here so that this file can be used in non- -// Chromium projects, such as the Native Client SDK. -#include "npapi.h" - -/* - * A fake "enum" value for getting browser-implemented Pepper extensions. - * The variable returns a pointer to an NPNExtensions structure. */ -#define NPNVPepperExtensions ((NPNVariable) 4000) - -/* - * A fake "enum" value for getting plugin-implemented Pepper extensions. - * The variable returns a pointer to an NPPExtensions structure. */ -#define NPPVPepperExtensions ((NPPVariable) 4001) - -typedef void NPDeviceConfig; -typedef void NPDeviceContext; -typedef void NPUserData; - -/* unique id for each device interface */ -typedef int32 NPDeviceID; - -typedef struct _NPPoint { - uint16 x; - uint16 y; -} NPPoint; - -typedef enum { - NPThemeItemScrollbarDownArrow = 0, - NPThemeItemScrollbarLeftArrow = 1, - NPThemeItemScrollbarRightArrow = 2, - NPThemeItemScrollbarUpArrow = 3, - NPThemeItemScrollbarHorizontalThumb = 4, - NPThemeItemScrollbarVerticalThumb = 5, - NPThemeItemScrollbarHoriztonalTrack = 6, - NPThemeItemScrollbarVerticalTrack = 7 -} NPThemeItem; - -typedef enum { - NPThemeStateDisabled = 0, - // Mouse is over this item. - NPThemeStateHot = 1, - // Mouse is over another part of this component. This is only used on Windows - // Vista and above. The plugin should pass it in, and the host will convert - // it to NPThemeStateNormal if on other platforms or on Windows XP. - NPThemeStateHover = 2, - NPThemeStateNormal = 3, - NPThemeStatePressed = 4 -} NPThemeState; - -typedef struct _NPThemeParams { - NPThemeItem item; - NPThemeState state; - NPRect location; - // Used for scroll bar tracks, needed for classic theme in Windows which draws - // a checkered pattern. - NPPoint align; -} NPThemeParams; - -typedef struct _NPDeviceBuffer { - void* ptr; - size_t size; -} NPDeviceBuffer; - -/* completion callback for flush device */ -typedef void (*NPDeviceFlushContextCallbackPtr)( - NPP instance, - NPDeviceContext* context, - NPError err, - NPUserData* userData); - -/* query single capabilities of device */ -typedef NPError ( - *NPDeviceQueryCapabilityPtr)(NPP instance, - int32 capability, - int32 *value); -/* query config (configuration == a set of capabilities) */ -typedef NPError ( - *NPDeviceQueryConfigPtr)(NPP instance, - const NPDeviceConfig* request, - NPDeviceConfig* obtain); -/* device initialization */ -typedef NPError (*NPDeviceInitializeContextPtr)( - NPP instance, - const NPDeviceConfig* config, - NPDeviceContext* context); -/* peek at device state */ -typedef NPError (*NPDeviceGetStateContextPtr) ( - NPP instance, - NPDeviceContext* context, - int32 state, - intptr_t* value); -/* poke device state */ -typedef NPError (*NPDeviceSetStateContextPtr) ( - NPP instance, - NPDeviceContext* context, - int32 state, - intptr_t value); -/* flush context, if callback, userData are NULL */ -/* this becomes a blocking call */ -typedef NPError (*NPDeviceFlushContextPtr)( - NPP instance, - NPDeviceContext* context, - NPDeviceFlushContextCallbackPtr callback, - void* userData); -/* destroy device context. Application responsible for */ -/* freeing context, if applicable */ -typedef NPError (*NPDeviceDestroyContextPtr)( - NPP instance, - NPDeviceContext* context); -/* Create a buffer associated with a particular context. The usage of the */ -/* buffer is device specific. The lifetime of the buffer is scoped with the */ -/* lifetime of the context. */ -typedef NPError (*NPDeviceCreateBufferPtr)( - NPP instance, - NPDeviceContext* context, - size_t size, - int32* id); -/* Destroy a buffer associated with a particular context. */ -typedef NPError (*NPDeviceDestroyBufferPtr)( - NPP instance, - NPDeviceContext* context, - int32 id); -/* Map a buffer id to its address. */ -typedef NPError (*NPDeviceMapBufferPtr)( - NPP instance, - NPDeviceContext* context, - int32 id, - NPDeviceBuffer* buffer); -/* Gets the size of the given theme component. For variable sized items like */ -/* vertical scrollbar tracks, the width will be the required width of the */ -/* track while the height will be the minimum height. */ -typedef NPError (*NPDeviceThemeGetSize)( - NPP instance, - NPThemeItem item, - int* width, - int* height); -/* Draw a themed item (i.e. scrollbar arrow). */ -typedef NPError (*NPDeviceThemePaint)( - NPP instance, - NPDeviceContext* context, - NPThemeParams* params); - - -/* forward decl typdef structs */ -typedef struct NPDevice NPDevice; -typedef struct NPNExtensions NPNExtensions; - -// DEPRECATED: this typedef is just for the NaCl code until they switch to NPNExtensions. -// PLEASE REMOVE THIS WHEN THE NACL CODE IS UPDATED. -typedef struct NPNExtensions NPExtensions; - -/* generic device interface */ -struct NPDevice { - NPDeviceQueryCapabilityPtr queryCapability; - NPDeviceQueryConfigPtr queryConfig; - NPDeviceInitializeContextPtr initializeContext; - NPDeviceSetStateContextPtr setStateContext; - NPDeviceGetStateContextPtr getStateContext; - NPDeviceFlushContextPtr flushContext; - NPDeviceDestroyContextPtr destroyContext; - NPDeviceCreateBufferPtr createBuffer; - NPDeviceDestroyBufferPtr destroyBuffer; - NPDeviceMapBufferPtr mapBuffer; - NPDeviceThemeGetSize themeGetSize; - NPDeviceThemePaint themePaint; -}; - -/* returns NULL if deviceID unavailable / unrecognized */ -typedef NPDevice* (*NPAcquireDevicePtr)( - NPP instance, - NPDeviceID device); - -/* Copy UTF-8 string into clipboard */ -typedef void (*NPCopyTextToClipboardPtr)( - NPP instance, - const char* content); - -/* Updates the number of find results for the current search term. If - * there are no matches 0 should be passed in. Only when the plugin has - * finished searching should it pass in the final count with finalResult set to - * true. */ -typedef void (*NPNumberOfFindResultsChangedPtr)( - NPP instance, - int total, - bool finalResult); - - /* Updates the index of the currently selected search item. */ -typedef void (*NPSelectedFindResultChangedPtr)( - NPP instance, - int index); - -/* Supports opening files anywhere on the system after prompting the user to - * pick one. - * - * This API is asynchronous. It will return immediately and the user will be - * prompted in parallel to pick a file. The plugin may continue to receive - * events while the open file dialog is up, and may continue to paint. Plugins - * may want to ignore input events between the call and the callback to avoid - * reentrant behavior. If the return value is not NPERR_NO_ERROR, the callback - * will NOT be executed. - * - * It is an error to call BrowseForFile before a previous call has executed - * the callback. - * - * Setting the flags to "Open" requires that the file exist to allow picking. - * Setting the flags to "Save" allows selecting nonexistant files (which will - * then be created), and will prompt the user if they want to overwrite an - * existing file if it exists. - * - * The plugin may specify a comma-separated list of possible mime types in - * the "extensions" parameter. If no extensions are specified, the dialog box - * will default to allowing all extensions. The first extension in the list - * will be the default. - * - * TODO(brettw) On Windows the extensions traditionally include a text - * description with the extension in the popup, do we want to allow this? - * We should probably also allow the ability to put "All files" in the - * list on Windows. - * - * Once the user has picked a file or has canceled the dialog box, the given - * callback will be called with the results of the operation and the passed in - * "user data" pointer. If the user successfully picked a file, the filename - * will be non-NULL and will contain a pointer to an array of strings, one for - * each file picked (the first file will be file_paths[0]). This buffer will - * become invalid as soon as the call completes, so it is the plugin's - * responsibility to copy the filename(sp if it needs future access to them. - * A NULL file_paths in the callback means the user canceled the dialog box. - * - * The filename will be in UTF-8. It may not actually correspond to the actual - * file on disk on a Linux system, because we'll do our best to convert it from - * the filesystem's locale to UTF-8. Instead, the string will be appropriate for - * displaying to the user which file they picked. - * */ -typedef enum { - NPChooseFile_Open = 1, - NPChooseFile_OpenMultiple = 2, - NPChooseFile_Save = 3, -} NPChooseFileMode; -typedef void (*NPChooseFileCallback)(const char** filePaths, - uint32 pathCount, - void* userData); -typedef NPError (*NPChooseFilePtr)( - NPP instance, - const char* mimeTypes, - NPChooseFileMode mode, - NPChooseFileCallback callback, - void* userData); - -/* Pepper extensions */ -struct NPNExtensions { - /* Device interface acquisition */ - NPAcquireDevicePtr acquireDevice; - /* Clipboard functionality */ - NPCopyTextToClipboardPtr copyTextToClipboard; - /* Find */ - NPNumberOfFindResultsChangedPtr numberOfFindResultsChanged; - NPSelectedFindResultChangedPtr selectedFindResultChanged; - /* File I/O extensions */ - NPChooseFilePtr chooseFile; -}; - -/* Events -------------------------------------------------------------------*/ - -typedef enum { - NPMouseButton_None = -1, - NPMouseButton_Left = 0, - NPMouseButton_Middle = 1, - NPMouseButton_Right = 2 -} NPMouseButtons; - -typedef enum { - NPEventType_Undefined = -1, - NPEventType_MouseDown = 0, - NPEventType_MouseUp = 1, - NPEventType_MouseMove = 2, - NPEventType_MouseEnter = 3, - NPEventType_MouseLeave = 4, - NPEventType_MouseWheel = 5, - NPEventType_RawKeyDown = 6, - NPEventType_KeyDown = 7, - NPEventType_KeyUp = 8, - NPEventType_Char = 9, - NPEventType_Minimize = 10, - NPEventType_Focus = 11, - NPEventType_Device = 12 -} NPEventTypes; - -typedef enum { - NPEventModifier_ShiftKey = 1 << 0, - NPEventModifier_ControlKey = 1 << 1, - NPEventModifier_AltKey = 1 << 2, - NPEventModifier_MetaKey = 1 << 3, - NPEventModifier_IsKeyPad = 1 << 4, - NPEventModifier_IsAutoRepeat = 1 << 5, - NPEventModifier_LeftButtonDown = 1 << 6, - NPEventModifier_MiddleButtonDown = 1 << 7, - NPEventModifier_RightButtonDown = 1 << 8 -} NPEventModifiers; - -typedef struct _NPKeyEvent -{ - uint32 modifier; - uint32 normalizedKeyCode; -} NPKeyEvent; - -typedef struct _NPCharacterEvent -{ - uint32 modifier; - uint16 text[4]; - uint16 unmodifiedText[4]; -} NPCharacterEvent; - -typedef struct _NPMouseEvent -{ - uint32 modifier; - int32 button; - int32 x; - int32 y; - int32 clickCount; -} NPMouseEvent; - -typedef struct _NPMouseWheelEvent -{ - uint32 modifier; - float deltaX; - float deltaY; - float wheelTicksX; - float wheelTicksY; - uint32 scrollByPage; -} NPMouseWheelEvent; - -typedef struct _NPDeviceEvent { - uint32 device_uid; - uint32 subtype; - /* uint8 generic[0]; */ -} NPDeviceEvent; - -typedef struct _NPMinimizeEvent { - int32 value; -} NPMinimizeEvent; - -typedef struct _NPFocusEvent { - int32 value; -} NPFocusEvent; - -typedef struct _NPPepperEvent -{ - uint32 size; - int32 type; - double timeStampSeconds; - union { - NPKeyEvent key; - NPCharacterEvent character; - NPMouseEvent mouse; - NPMouseWheelEvent wheel; - NPMinimizeEvent minimize; - NPFocusEvent focus; - NPDeviceEvent device; - } u; -} NPPepperEvent; - -/* 2D -----------------------------------------------------------------------*/ - -#define NPPepper2DDevice 1 - -typedef struct _NPDeviceContext2DConfig { -} NPDeviceContext2DConfig; - -typedef struct _NPDeviceContext2D -{ - /* Internal value used by the browser to identify this device. */ - void* reserved; - - /* A pointer to the pixel data. This data is 8-bit values in BGRA order in - * memory. Each row will start |stride| bytes after the previous one. - * - * THIS DATA USES PREMULTIPLIED ALPHA. This means that each color channel has - * been multiplied with the corresponding alpha, which makes compositing - * easier. If any color channels have a value greater than the alpha value, - * you'll likely get crazy colors and weird artifacts. */ - void* region; - - /* Length of each row of pixels in bytes. This may be larger than width * 4 - * if there is padding at the end of each row to help with alignment. */ - int32 stride; - - /* The dirty region that the plugin has painted into the buffer. This - * will be initialized to the size of the plugin image in - * initializeContextPtr. The plugin can change the values to only - * update portions of the image. */ - struct { - int32 left; - int32 top; - int32 right; - int32 bottom; - } dirty; -} NPDeviceContext2D; - -/* 3D -----------------------------------------------------------------------*/ - -#define NPPepper3DDevice 2 - -typedef struct _NPDeviceContext3DConfig { - int32 commandBufferSize; -} NPDeviceContext3DConfig; - -typedef enum _NPDeviceContext3DError { - // No error has ocurred. - NPDeviceContext3DError_NoError, - - // The size of a command was invalid. - NPDeviceContext3DError_InvalidSize, - - // An offset was out of bounds. - NPDeviceContext3DError_OutOfBounds, - - // A command was not recognized. - NPDeviceContext3DError_UnknownCommand, - - // The arguments to a command were invalid. - NPDeviceContext3DError_InvalidArguments, - - // The 3D context was lost, for example due to a power management event. The - // context must be destroyed and a new one created. - NPDeviceContext3DError_LostContext, - - // Any other error. - NPDeviceContext3DError_GenericError -} NPDeviceContext3DError; - -typedef struct _NPDeviceContext3D NPDeviceContext3D; - -typedef void (*NPDeviceContext3DRepaintPtr)(NPP npp, - NPDeviceContext3D* context); - -typedef struct _NPDeviceContext3D -{ - void* reserved; - - // If true, then a flush will only complete once the get offset has advanced - // on the GPU thread. If false, then the get offset might have changed but - // the GPU thread will respond as quickly as possible without guaranteeing - // having made any progress in executing pending commands. Set to true - // to ensure that progress is made or when flushing in a loop waiting for the - // GPU to reach a certain state, for example in advancing beyond a particular - // token. Set to false when flushing to query the current state, for example - // whether an error has occurred. - bool waitForProgress; - - // Buffer in which commands are stored. - void* commandBuffer; - int32 commandBufferSize; - - // Offset in command buffer reader has reached. Synchronized on flush. - int32 getOffset; - - // Offset in command buffer writer has reached. Synchronized on flush. - int32 putOffset; - - // Last processed token. Synchronized on flush. - int32 token; - - // Callback invoked on the main thread when the context must be repainted. - // TODO(apatrick): move this out of the context struct like the rest of the - // fields. - NPDeviceContext3DRepaintPtr repaintCallback; - - // Error status. Synchronized on flush. - NPDeviceContext3DError error; -} NPDeviceContext3D; - -/* Audio --------------------------------------------------------------------*/ - -#define NPPepperAudioDevice 3 - -/* min & max sample frame count */ -typedef enum { - NPAudioMinSampleFrameCount = 64, - NPAudioMaxSampleFrameCount = 32768 -} NPAudioSampleFrameCounts; - -/* supported sample rates */ -typedef enum { - NPAudioSampleRate44100Hz = 44100, - NPAudioSampleRate48000Hz = 48000, - NPAudioSampleRate96000Hz = 96000 -} NPAudioSampleRates; - -/* supported sample formats */ -typedef enum { - NPAudioSampleTypeInt16 = 0, - NPAudioSampleTypeFloat32 = 1 -} NPAudioSampleTypes; - -/* supported channel layouts */ -/* there is code that depends on these being the actual number of channels */ -typedef enum { - NPAudioChannelNone = 0, - NPAudioChannelMono = 1, - NPAudioChannelStereo = 2, - NPAudioChannelThree = 3, - NPAudioChannelFour = 4, - NPAudioChannelFive = 5, - NPAudioChannelFiveOne = 6, - NPAudioChannelSeven = 7, - NPAudioChannelSevenOne = 8 -} NPAudioChannels; - -/* audio context states */ -typedef enum { - NPAudioContextStateCallback = 0, - NPAudioContextStateUnderrunCounter = 1 -} NPAudioContextStates; - -/* audio context state values */ -typedef enum { - NPAudioCallbackStop = 0, - NPAudioCallbackStart = 1 -} NPAudioContextStateValues; - -/* audio query capabilities */ -typedef enum { - NPAudioCapabilitySampleRate = 0, - NPAudioCapabilitySampleType = 1, - NPAudioCapabilitySampleFrameCount = 2, - NPAudioCapabilitySampleFrameCount44100Hz = 3, - NPAudioCapabilitySampleFrameCount48000Hz = 4, - NPAudioCapabilitySampleFrameCount96000Hz = 5, - NPAudioCapabilityOutputChannelMap = 6, - NPAudioCapabilityInputChannelMap = 7 -} NPAudioCapabilities; - -typedef struct _NPDeviceContextAudio NPDeviceContextAudio; - -/* user supplied callback function */ -typedef void (*NPAudioCallback)(NPDeviceContextAudio *context); - -typedef struct _NPDeviceContextAudioConfig { - int32 sampleRate; - int32 sampleType; - int32 outputChannelMap; - int32 inputChannelMap; - int32 sampleFrameCount; - uint32 startThread; - uint32 flags; - NPAudioCallback callback; - void *userData; -} NPDeviceContextAudioConfig; - -struct _NPDeviceContextAudio { - NPDeviceContextAudioConfig config; - void *outBuffer; - void *inBuffer; - void *reserved; -}; - -/* Printing related APIs ---------------------------------------------------*/ - -/* Being a print operation. Returns the total number of pages to print at the - * given printableArea size and DPI. printableArea is in points (a point is 1/72 - * of an inch). The plugin is expected to remember the values of printableArea - * and printerDPI for use in subsequent print interface calls. These values - * should be cleared in printEnd. */ -typedef NPError (*NPPPrintBeginPtr) ( - NPP instance, - NPRect* printableArea, - int32 printerDPI, - int32* numPages); -/* Returns the required raster dimensions for the given page. */ -typedef NPError (*NPPGetRasterDimensionsPtr) ( - NPP instance, - int32 pageNumber, - int32* widthInPixels, - int32* heightInPixels); -/* Prints the specified page This allows the plugin to print a raster output. */ -typedef NPError (*NPPPrintPageRasterPtr) ( - NPP instance, - int32 pageNumber, - NPDeviceContext2D* printSurface); -/* Ends the print operation */ -typedef NPError (*NPPPrintEndPtr) (NPP instance); - -/* TODO(sanjeevr) : Provide a vector interface for printing. We need to decide - * on a vector format that can support embedded fonts. A vector format will - * greatly reduce the size of the required output buffer. */ - -typedef struct _NPPPrintExtensions { - NPPPrintBeginPtr printBegin; - NPPGetRasterDimensionsPtr getRasterDimensions; - NPPPrintPageRasterPtr printPageRaster; - NPPPrintEndPtr printEnd; -} NPPPrintExtensions; - -/* Returns NULL if the plugin does not support print extensions */ -typedef NPPPrintExtensions* (*NPPGetPrintExtensionsPtr)(NPP instance); - -/* Find ---------------------------------------------------------------------*/ - -/* Finds the given UTF-8 text starting at the current selection. The number of - * results will be updated asynchronously via numberOfFindResultsChanged. Note - * that multiple StartFind calls can happen before StopFind is called in the - * case of the search term changing. */ -typedef NPError (*NPPStartFindPtr) ( - NPP instance, - const char* text, - bool caseSensitive); - -/* Go to the next/previous result. */ -typedef NPError (*NPPSelectFindResultPtr) ( - NPP instance, - bool forward); - -/* Tells the plugin that the find operation has stopped, so it should clear - * any highlighting. */ -typedef NPError (*NPPStopFindPtr) ( - NPP instance); - -typedef struct _NPPFindExtensions { - NPPStartFindPtr startFind; - NPPSelectFindResultPtr selectFindResult; - NPPStopFindPtr stopFind; -} NPPFindExtensions; - -/* Returns NULL if the plugin does not support find extensions. */ -typedef NPPFindExtensions* (*NPPGetFindExtensionsPtr)(NPP instance); - -/* Zooms plugins. 0 means reset, -1 means zoom out, and +1 means zoom in. */ -typedef NPError (*NPPZoomPtr) ( - NPP instance, - int factor); - -typedef struct _NPPExtensions { - NPPGetPrintExtensionsPtr getPrintExtensions; - NPPGetFindExtensionsPtr getFindExtensions; - NPPZoomPtr zoom; -} NPPExtensions; - -#endif /* _NP_EXTENSIONS_H_ */ diff --git a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/nphostapi.h b/omaha/third_party/chrome/files/src/third_party/npapi/bindings/nphostapi.h deleted file mode 100644 index 69bad70ef..000000000 --- a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/nphostapi.h +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// TODO: Did not implement JRIGlobalRef function yet. Not sure if this is used? - -#ifndef _NPHOSTAPI_H_ -#define _NPHOSTAPI_H_ - -#include "base/port.h" -#include "third_party/npapi/bindings/npapi.h" -#include "third_party/npapi/bindings/npapi_extensions.h" -#include "third_party/npapi/bindings/npruntime.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// -// NPAPI NPP Function Pointers -// -typedef NPError (*NPP_NewProcPtr)(NPMIMEType pluginType, - NPP instance, - uint16 mode, - int16 argc, - char* argn[], - char* argv[], - NPSavedData* saved); -typedef NPError (*NPP_DestroyProcPtr)(NPP instance, - NPSavedData** save); -typedef NPError (*NPP_SetWindowProcPtr)(NPP instance, - NPWindow* window); -typedef NPError (*NPP_NewStreamProcPtr)(NPP instance, - NPMIMEType type, - NPStream* stream, - NPBool seekable, - uint16* stype); -typedef NPError (*NPP_DestroyStreamProcPtr)(NPP instance, - NPStream* stream, - NPReason reason); -typedef int32 (*NPP_WriteReadyProcPtr)(NPP instance, - NPStream* stream); -typedef int32 (*NPP_WriteProcPtr)(NPP instance, - NPStream* stream, - int32 offset, - int32 len, - void* buffer); -typedef void (*NPP_StreamAsFileProcPtr)(NPP instance, - NPStream* stream, - const char* fname); -typedef void (*NPP_PrintProcPtr)(NPP instance, - NPPrint* platformPrint); -typedef int16 (*NPP_HandleEventProcPtr)(NPP instance, - void* event); -typedef void (*NPP_URLNotifyProcPtr)(NPP instance, - const char* url, - NPReason reason, - void* notifyData); -typedef void* JRIGlobalRef; //not using this right now -typedef NPError (*NPP_GetValueProcPtr)(NPP instance, - NPPVariable variable, - void *ret_alue); -typedef NPError (*NPP_SetValueProcPtr)(NPP instance, - NPNVariable variable, - void *ret_alue); - -// -// NPAPI NPN Function Pointers -// -typedef NPError (*NPN_GetURLProcPtr)(NPP instance, - const char* URL, - const char* window); -typedef NPError (*NPN_PostURLProcPtr)(NPP instance, - const char* URL, - const char* window, - uint32 len, - const char* buf, - NPBool file); -typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, - NPByteRange* rangeList); -typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, - NPMIMEType type, - const char* window, - NPStream** stream); -typedef int32 (*NPN_WriteProcPtr)(NPP instance, - NPStream* stream, - int32 len, - void* buffer); -typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, - NPStream* stream, - NPReason reason); -typedef void (*NPN_StatusProcPtr)(NPP instance, - const char* message); -typedef const char* (*NPN_UserAgentProcPtr)(NPP instance); -typedef void* (*NPN_MemAllocProcPtr)(uint32 size); -typedef void (*NPN_MemFreeProcPtr)(void* ptr); -typedef uint32 (*NPN_MemFlushProcPtr)(uint32 size); -typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages); - -typedef void* (*NPN_GetJavaEnvProcPtr)(void); -typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance); - -typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, - const char* URL, - const char* window, - void* notifyData); -typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, - const char* URL, - const char* window, - uint32 len, - const char* buf, - NPBool file, - void* notifyData); -typedef NPError (*NPN_GetValueProcPtr)(NPP instance, - NPNVariable variable, - void *ret_value); -typedef NPError (*NPN_SetValueProcPtr)(NPP instance, - NPPVariable variable, - void *value); -typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, - NPRect *rect); -typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, - NPRegion region); -typedef void (*NPN_ForceRedrawProcPtr)(NPP instance); - -typedef void (*NPN_ReleaseVariantValueProcPtr) (NPVariant *variant); - -typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr) (const NPUTF8 *name); -typedef void (*NPN_GetStringIdentifiersProcPtr) (const NPUTF8 **names, - int32_t nameCount, - NPIdentifier *identifiers); -typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr) (int32_t intid); -typedef int32_t (*NPN_IntFromIdentifierProcPtr) (NPIdentifier identifier); -typedef bool (*NPN_IdentifierIsStringProcPtr) (NPIdentifier identifier); -typedef NPUTF8 * (*NPN_UTF8FromIdentifierProcPtr) (NPIdentifier identifier); - -typedef NPObject* (*NPN_CreateObjectProcPtr) (NPP, - NPClass *aClass); -typedef NPObject* (*NPN_RetainObjectProcPtr) (NPObject *obj); -typedef void (*NPN_ReleaseObjectProcPtr) (NPObject *obj); -typedef bool (*NPN_InvokeProcPtr) (NPP npp, - NPObject *obj, - NPIdentifier methodName, - const NPVariant *args, - unsigned argCount, - NPVariant *result); -typedef bool (*NPN_InvokeDefaultProcPtr) (NPP npp, - NPObject *obj, - const NPVariant *args, - unsigned argCount, - NPVariant *result); -typedef bool (*NPN_EvaluateProcPtr) (NPP npp, - NPObject *obj, - NPString *script, - NPVariant *result); -typedef bool (*NPN_GetPropertyProcPtr) (NPP npp, - NPObject *obj, - NPIdentifier propertyName, - NPVariant *result); -typedef bool (*NPN_SetPropertyProcPtr) (NPP npp, - NPObject *obj, - NPIdentifier propertyName, - const NPVariant *value); -typedef bool (*NPN_HasPropertyProcPtr) (NPP, - NPObject *npobj, - NPIdentifier propertyName); -typedef bool (*NPN_HasMethodProcPtr) (NPP npp, - NPObject *npobj, - NPIdentifier methodName); -typedef bool (*NPN_RemovePropertyProcPtr) (NPP npp, - NPObject *obj, - NPIdentifier propertyName); -typedef void (*NPN_SetExceptionProcPtr) (NPObject *obj, - const NPUTF8 *message); -typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp, - NPBool enabled); -typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp); -typedef bool (*NPN_EnumerateProcPtr)(NPP npp, - NPObject *obj, - NPIdentifier **identifier, - uint32_t *count); -typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance, - void (*func)(void *), - void *userData); -typedef bool (*NPN_ConstructProcPtr)(NPP npp, - NPObject* obj, - const NPVariant *args, - uint32_t argCount, - NPVariant *result); -typedef NPError (*NPN_GetValueForURLPtr)(NPP npp, - NPNURLVariable variable, - const char *url, - char **value, - uint32_t *len); -typedef NPError (*NPN_SetValueForURLPtr)(NPP npp, - NPNURLVariable variable, - const char *url, - const char *value, - uint32_t len); -typedef NPError (*NPN_GetAuthenticationInfoPtr)(NPP npp, - const char *protocol, - const char *host, - int32_t port, - const char *scheme, - const char *realm, - char **username, - uint32_t *ulen, - char **password, - uint32_t *plen); -typedef uint32 (*NPN_ScheduleTimerPtr)(NPP npp, - uint32 interval, - NPBool repeat, - void (*timerFunc)(NPP npp, uint32 timerID)); -typedef void (*NPN_UnscheduleTimerPtr)(NPP npp, - uint32 timerID); -typedef NPError (*NPN_PopUpContextMenuPtr)(NPP npp, - NPMenu* menu); -typedef NPBool (*NPN_ConvertPointPtr)(NPP npp, - double sourceX, - double sourceY, - NPCoordinateSpace sourceSpace, - double *destX, - double *destY, - NPCoordinateSpace destSpace); - -// -// NPAPI Function table of NPP functions (functions provided by plugin to host) -// -typedef struct _NPPluginFuncs { - unsigned short size; - unsigned short version; - NPP_NewProcPtr newp; - NPP_DestroyProcPtr destroy; - NPP_SetWindowProcPtr setwindow; - NPP_NewStreamProcPtr newstream; - NPP_DestroyStreamProcPtr destroystream; - NPP_StreamAsFileProcPtr asfile; - NPP_WriteReadyProcPtr writeready; - NPP_WriteProcPtr write; - NPP_PrintProcPtr print; - NPP_HandleEventProcPtr event; - NPP_URLNotifyProcPtr urlnotify; - JRIGlobalRef javaClass; - NPP_GetValueProcPtr getvalue; - NPP_SetValueProcPtr setvalue; -} NPPluginFuncs; - -// -// NPAPI Function table NPN functions (functions provided by host to plugin) -// -typedef struct _NPNetscapeFuncs { - uint16 size; - uint16 version; - NPN_GetURLProcPtr geturl; - NPN_PostURLProcPtr posturl; - NPN_RequestReadProcPtr requestread; - NPN_NewStreamProcPtr newstream; - NPN_WriteProcPtr write; - NPN_DestroyStreamProcPtr destroystream; - NPN_StatusProcPtr status; - NPN_UserAgentProcPtr uagent; - NPN_MemAllocProcPtr memalloc; - NPN_MemFreeProcPtr memfree; - NPN_MemFlushProcPtr memflush; - NPN_ReloadPluginsProcPtr reloadplugins; - NPN_GetJavaEnvProcPtr getJavaEnv; - NPN_GetJavaPeerProcPtr getJavaPeer; - NPN_GetURLNotifyProcPtr geturlnotify; - NPN_PostURLNotifyProcPtr posturlnotify; - NPN_GetValueProcPtr getvalue; - NPN_SetValueProcPtr setvalue; - NPN_InvalidateRectProcPtr invalidaterect; - NPN_InvalidateRegionProcPtr invalidateregion; - NPN_ForceRedrawProcPtr forceredraw; - - NPN_GetStringIdentifierProcPtr getstringidentifier; - NPN_GetStringIdentifiersProcPtr getstringidentifiers; - NPN_GetIntIdentifierProcPtr getintidentifier; - NPN_IdentifierIsStringProcPtr identifierisstring; - NPN_UTF8FromIdentifierProcPtr utf8fromidentifier; - NPN_IntFromIdentifierProcPtr intfromidentifier; - NPN_CreateObjectProcPtr createobject; - NPN_RetainObjectProcPtr retainobject; - NPN_ReleaseObjectProcPtr releaseobject; - NPN_InvokeProcPtr invoke; - NPN_InvokeDefaultProcPtr invokeDefault; - NPN_EvaluateProcPtr evaluate; - NPN_GetPropertyProcPtr getproperty; - NPN_SetPropertyProcPtr setproperty; - NPN_RemovePropertyProcPtr removeproperty; - NPN_HasPropertyProcPtr hasproperty; - NPN_HasMethodProcPtr hasmethod; - NPN_ReleaseVariantValueProcPtr releasevariantvalue; - NPN_SetExceptionProcPtr setexception; - NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate; - NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate; - NPN_EnumerateProcPtr enumerate; - NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall; - NPN_ConstructProcPtr construct; - NPN_GetValueForURLPtr getvalueforurl; - NPN_SetValueForURLPtr setvalueforurl; - NPN_GetAuthenticationInfoPtr getauthenticationinfo; - NPN_ScheduleTimerPtr scheduletimer; - NPN_UnscheduleTimerPtr unscheduletimer; - NPN_PopUpContextMenuPtr popupcontextmenu; - NPN_ConvertPointPtr convertpoint; -} NPNetscapeFuncs; - -// -// NPAPI library entry points -// -#if defined(OS_POSIX) && !defined(OS_MACOSX) -typedef NPError (API_CALL * NP_InitializeFunc)(NPNetscapeFuncs* pNFuncs, - NPPluginFuncs* pPFuncs); -#else -typedef NPError (API_CALL * NP_InitializeFunc)(NPNetscapeFuncs* pFuncs); -typedef NPError (API_CALL * NP_GetEntryPointsFunc)(NPPluginFuncs* pFuncs); -#endif -typedef NPError (API_CALL * NP_ShutdownFunc)(void); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _NPHOSTAPI_H_ diff --git a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npruntime.h b/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npruntime.h deleted file mode 100644 index e050b21f7..000000000 --- a/omaha/third_party/chrome/files/src/third_party/npapi/bindings/npruntime.h +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (C) 2004, Apple Computer, Inc. and The Mozilla Foundation. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla - * Foundation ("Mozilla") nor the names of their contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR - * THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Revision 1 (March 4, 2004): - * Initial proposal. - * - * Revision 2 (March 10, 2004): - * All calls into script were made asynchronous. Results are - * provided via the NPScriptResultFunctionPtr callback. - * - * Revision 3 (March 10, 2004): - * Corrected comments to not refer to class retain/release FunctionPtrs. - * - * Revision 4 (March 11, 2004): - * Added additional convenience NPN_SetExceptionWithUTF8(). - * Changed NPHasPropertyFunctionPtr and NPHasMethodFunctionPtr to take NPClass - * pointers instead of NPObject pointers. - * Added NPIsValidIdentifier(). - * - * Revision 5 (March 17, 2004): - * Added context parameter to result callbacks from ScriptObject functions. - * - * Revision 6 (March 29, 2004): - * Renamed functions implemented by user agent to NPN_*. Removed _ from - * type names. - * Renamed "JavaScript" types to "Script". - * - * Revision 7 (April 21, 2004): - * NPIdentifier becomes a void*, was int32_t - * Remove NP_IsValidIdentifier, renamed NP_IdentifierFromUTF8 to NP_GetIdentifier - * Added NPVariant and modified functions to use this new type. - * - * Revision 8 (July 9, 2004): - * Updated to joint Apple-Mozilla license. - * - * Revision 9 (August 12, 2004): - * Changed NPVariantType enum values to form PVariantType_XXX - * Added NPP arguments to NPObject functions. - * Replaced NPVariant functions with macros. - */ -#ifndef _NP_RUNTIME_H_ -#define _NP_RUNTIME_H_ - - -// BEGIN GOOGLE MODIFICATIONS -#include "npapi.h" -#ifndef __native_client__ -typedef uint8 uint8_t; -typedef int8 int8_t; -typedef uint16 uint16_t; -typedef int16 int16_t; -typedef uint32 uint32_t; -typedef int32 int32_t; -typedef int64 int64_t; -typedef uint64 uint64_t; -#endif /* __native_client__ */ -// END GOOGLE MODIFICATIONS - - -#ifdef __cplusplus -extern "C" { -#endif - -/* - This API is used to facilitate binding code written in C to script - objects. The API in this header does not assume the presence of a - user agent. That is, it can be used to bind C code to scripting - environments outside of the context of a user agent. - - However, the normal use of the this API is in the context of a - scripting environment running in a browser or other user agent. - In particular it is used to support the extended Netscape - script-ability API for plugins (NP-SAP). NP-SAP is an extension - of the Netscape plugin API. As such we have adopted the use of - the "NP" prefix for this API. - - The following NP{N|P}Variables were added to the Netscape plugin - API (in npapi.h): - - NPNVWindowNPObject - NPNVPluginElementNPObject - NPPVpluginScriptableNPObject - - These variables are exposed through NPN_GetValue() and - NPP_GetValue() (respectively) and are used to establish the - initial binding between the user agent and native code. The DOM - objects in the user agent can be examined and manipulated using - the NPN_ functions that operate on NPObjects described in this - header. - - To the extent possible the assumptions about the scripting - language used by the scripting environment have been minimized. -*/ - - -/* - Objects (non-primitive data) passed between 'C' and script is - always wrapped in an NPObject. The 'interface' of an NPObject is - described by an NPClass. -*/ -typedef struct NPObject NPObject; -typedef struct NPClass NPClass; - -typedef char NPUTF8; -typedef struct _NPString { - const NPUTF8 *UTF8Characters; - uint32_t UTF8Length; -} NPString; - -typedef enum { - NPVariantType_Void, - NPVariantType_Null, - NPVariantType_Bool, - NPVariantType_Int32, - NPVariantType_Double, - NPVariantType_String, - NPVariantType_Object -} NPVariantType; - -typedef struct _NPVariant { - NPVariantType type; - union { - bool boolValue; - int32_t intValue; - double doubleValue; - NPString stringValue; - NPObject *objectValue; - } value; -} NPVariant; - -/* - NPN_ReleaseVariantValue is called on all 'out' parameters references. - Specifically it is called on variants that are resultant out parameters - in NPGetPropertyFunctionPtr and NPInvokeFunctionPtr. Resultant variants - from these two functions should be initialized using the - NPN_InitializeVariantXXX() functions. - - After calling NPReleaseVariantValue, the type of the variant will - be set to NPVariantUndefinedType. -*/ -void NPN_ReleaseVariantValue (NPVariant *variant); - -#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void) -#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null) -#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool) -#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32) -#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double) -#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String) -#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object) - -#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue) -#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue) -#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue) -#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue) -#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue) - -#define NP_BEGIN_MACRO do { -#define NP_END_MACRO } while (0) - -#define VOID_TO_NPVARIANT(_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Void; (_v).value.objectValue = NULL; NP_END_MACRO -#define NULL_TO_NPVARIANT(_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Null; (_v).value.objectValue = NULL; NP_END_MACRO -#define BOOLEAN_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Bool; (_v).value.boolValue = !!(_val); NP_END_MACRO -#define INT32_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Int32; (_v).value.intValue = _val; NP_END_MACRO -#define DOUBLE_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Double; (_v).value.doubleValue = _val; NP_END_MACRO -#define STRINGZ_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_String; NPString str = { _val, strlen(_val) }; (_v).value.stringValue = str; NP_END_MACRO -#define STRINGN_TO_NPVARIANT(_val, _len, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_String; NPString str = { _val, _len }; (_v).value.stringValue = str; NP_END_MACRO -#define OBJECT_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_Object; (_v).value.objectValue = _val; NP_END_MACRO - -/* - Type mappings (JavaScript types have been used for illustration - purposes): - - JavaScript to C (NPVariant with type:) - undefined NPVariantType_Void - null NPVariantType_Null - Boolean NPVariantType_Bool - Number NPVariantType_Double or NPVariantType_Int32 - String NPVariantType_String - Object NPVariantType_Object - - C (NPVariant with type:) to JavaScript - NPVariantType_Void undefined - NPVariantType_Null null - NPVariantType_Bool Boolean - NPVariantType_Int32 Number - NPVariantType_Double Number - NPVariantType_String String - NPVariantType_Object Object -*/ - -typedef void *NPIdentifier; - -/* - NPObjects have methods and properties. Methods and properties are - identified with NPIdentifiers. These identifiers may be reflected - in script. NPIdentifiers can be either strings or integers, IOW, - methods and properties can be identified by either strings or - integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be - compared using ==. In case of any errors, the requested - NPIdentifier(s) will be NULL. -*/ -NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); -void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers); -NPIdentifier NPN_GetIntIdentifier(int32_t intid); -bool NPN_IdentifierIsString(NPIdentifier identifier); - -/* - The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed. -*/ -NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier); - -/* - Get the integer represented by identifier. If identifier is not an - integer identifier, the behaviour is undefined. -*/ -int32_t NPN_IntFromIdentifier(NPIdentifier identifier); - -/* - NPObject behavior is implemented using the following set of - callback functions. - - The NPVariant *result argument of these functions (where - applicable) should be released using NPN_ReleaseVariantValue(). -*/ -typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass); -typedef void (*NPDeallocateFunctionPtr)(NPObject *obj); -typedef void (*NPInvalidateFunctionPtr)(NPObject *obj); -typedef bool (*NPHasMethodFunctionPtr)(NPObject *obj, NPIdentifier name); -typedef bool (*NPInvokeFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); -typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); -typedef bool (*NPHasPropertyFunctionPtr)(NPObject *obj, NPIdentifier name); -typedef bool (*NPGetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, NPVariant *result); -typedef bool (*NPSetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *value); -typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj, NPIdentifier name); -typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value, uint32_t *count); -typedef bool (*NPConstructFunctionPtr)(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); - -/* - NPObjects returned by create have a reference count of one. It is the caller's responsibility - to release the returned object. - - NPInvokeFunctionPtr function may return false to indicate a the method could not be invoked. - - NPGetPropertyFunctionPtr and NPSetPropertyFunctionPtr may return false to indicate a property doesn't - exist. - - NPInvalidateFunctionPtr is called by the scripting environment when the native code is - shutdown. Any attempt to message a NPObject instance after the invalidate - callback has been called will result in undefined behavior, even if the - native code is still retaining those NPObject instances. - (The runtime will typically return immediately, with 0 or NULL, from an attempt to - dispatch to a NPObject, but this behavior should not be depended upon.) - - The NPEnumerationFunctionPtr function may pass an array of - NPIdentifiers back to the caller. The callee allocs the memory of - the array using NPN_MemAlloc(), and it's the caller's responsibility - to release it using NPN_MemFree(). -*/ -struct NPClass -{ - uint32_t structVersion; - NPAllocateFunctionPtr allocate; - NPDeallocateFunctionPtr deallocate; - NPInvalidateFunctionPtr invalidate; - NPHasMethodFunctionPtr hasMethod; - NPInvokeFunctionPtr invoke; - NPInvokeDefaultFunctionPtr invokeDefault; - NPHasPropertyFunctionPtr hasProperty; - NPGetPropertyFunctionPtr getProperty; - NPSetPropertyFunctionPtr setProperty; - NPRemovePropertyFunctionPtr removeProperty; - NPEnumerationFunctionPtr enumerate; - NPConstructFunctionPtr construct; -}; - -#define NP_CLASS_STRUCT_VERSION 3 -#define NP_CLASS_STRUCT_VERSION_ENUM 2 -#define NP_CLASS_STRUCT_VERSION_CTOR 3 - -#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \ - ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) -#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \ - ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) - -struct NPObject { - NPClass *_class; - uint32_t referenceCount; - // Additional space may be allocated here by types of NPObjects -}; - -/* - If the class has an allocate function, NPN_CreateObject invokes that function, - otherwise a NPObject is allocated and returned. If a class has an allocate - function it is the responsibility of that implementation to set the initial retain - count to 1. -*/ -NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); - -/* - Increment the NPObject's reference count. -*/ -NPObject *NPN_RetainObject (NPObject *obj); - -/* - Decremented the NPObject's reference count. If the reference - count goes to zero, the class's destroy function is invoke if - specified, otherwise the object is freed directly. -*/ -void NPN_ReleaseObject (NPObject *obj); - -/* - Functions to access script objects represented by NPObject. - - Calls to script objects are synchronous. If a function returns a - value, it will be supplied via the result NPVariant - argument. Successful calls will return true, false will be - returned in case of an error. - - Calls made from plugin code to script must be made from the thread - on which the plugin was initialized. -*/ -bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result); -bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); -bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, NPVariant *result); -bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result); -bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value); -bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); -bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); -bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); -bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count); -bool NPN_Construct(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result); - -// Helper function for evaluating a script in the scope of the NPObject passed in. -// Parameters -// npp -// The plugin's opaque instance handle (Can be NULL) -// popups_allowed -// Indicates if popups created in the context of the script being executed are -// blocked or not. -// npobj -// The NPObject. -// npscript -// The script being executed. -// result -// On return contains the value returned by the script. -// Returns true on success. -bool NPN_EvaluateHelper(NPP npp, bool popups_allowed, NPObject* npobj, - NPString* npscript, NPVariant *result); - -// BEGIN GOOGLE MODIFICATIONS - -void* NPP_GetJavaClass(void); -void* NPN_GetJavaEnv(void); -void* NPN_GetJavaPeer(NPP instance); -void NPN_PluginThreadAsyncCall(NPP id, void (*func)(void *), void *userData); - -// END GOOGLE MODIFICATIONS - -/* - NPN_SetException may be called to trigger a script exception upon return - from entry points into NPObjects. -*/ -void NPN_SetException (NPObject *obj, const NPUTF8 *message); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/omaha/third_party/hashlib/__init__.py b/omaha/third_party/hashlib/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/omaha/third_party/hashlib/_hashlib.pyd b/omaha/third_party/hashlib/_hashlib.pyd deleted file mode 100644 index 9ecb5c19d..000000000 Binary files a/omaha/third_party/hashlib/_hashlib.pyd and /dev/null differ diff --git a/omaha/third_party/smartany/scoped_any.h b/omaha/third_party/smartany/scoped_any.h index ab89feef0..c805b4670 100644 --- a/omaha/third_party/smartany/scoped_any.h +++ b/omaha/third_party/smartany/scoped_any.h @@ -118,7 +118,7 @@ class scoped_any { static detail::smartany_static_assert::value> const cannot_dereference_a_handle; assert( valid() ); - return smart_types::to_reference( m_t ); + return safe_types::to_reference( m_t ); } #endif diff --git a/omaha/third_party/smartany/smart_any_fwd.h b/omaha/third_party/smartany/smart_any_fwd.h index 8808cd4b0..bae20b550 100644 --- a/omaha/third_party/smartany/smart_any_fwd.h +++ b/omaha/third_party/smartany/smart_any_fwd.h @@ -146,13 +146,13 @@ namespace detail typedef char (&yes)[1]; typedef char (&no) [2]; - struct dummy_struct + struct sample_struct { - void dummy_method() {} + void sample_method() {} }; - typedef void (dummy_struct::*safe_bool)(); - safe_bool const safe_true = &dummy_struct::dummy_method; + typedef void (sample_struct::*safe_bool)(); + safe_bool const safe_true = &sample_struct::sample_method; safe_bool const safe_false = 0; // Because of older compilers, we can't always use @@ -188,7 +188,7 @@ namespace detail template struct inner { - static T const get() + static T get() { return static_init::value; } @@ -201,7 +201,7 @@ namespace detail template struct inner { - static T const get() + static T get() { return 0; } @@ -370,7 +370,7 @@ namespace detail static bool const value = true; }; - // dummy type, don't define + // sample type, don't define struct smart_any_cannot_dereference; // For use in implementing unary operator* @@ -720,7 +720,7 @@ struct null_t template struct value_const { - operator T const() const + operator T () const { return value; } @@ -1022,7 +1022,9 @@ typedef close_fun(&free)> clos { (void) dwCoInit; # if (_WIN32_WINNT >= 0x0400 ) | defined(_WIN32_DCOM) - return ::CoInitializeEx(0,dwCoInit); + // COINIT_DISABLE_OLE1DDE is always added based on: + // https://docs.microsoft.com/en-us/windows/desktop/learnwin32/initializing-the-com-library + return ::CoInitializeEx(0, dwCoInit | COINIT_DISABLE_OLE1DDE); # else return ::CoInitialize(0); # endif diff --git a/omaha/tools/CrashHandlerClient/crash_handler_client_tool.cc b/omaha/tools/CrashHandlerClient/crash_handler_client_tool.cc index c35912c9f..9fddc897c 100644 --- a/omaha/tools/CrashHandlerClient/crash_handler_client_tool.cc +++ b/omaha/tools/CrashHandlerClient/crash_handler_client_tool.cc @@ -77,7 +77,7 @@ class BreakpadConnection { if (!eh_->IsOutOfProcess()) { _tprintf(_T("*** ExceptionHandler tried to run in-process! Aborting.") - _T(" (Check that GoogleCrashHandler.exe is running.)\n")); + _T(" (Check that ") CRASH_HANDLER_NAME _T(".exe is running.)\n")); Disconnect(); return E_UNEXPECTED; } diff --git a/omaha/tools/MsiTagger/msi_tag_tool.cc b/omaha/tools/MsiTagger/msi_tag_tool.cc index f77123760..0040f5ba5 100644 --- a/omaha/tools/MsiTagger/msi_tag_tool.cc +++ b/omaha/tools/MsiTagger/msi_tag_tool.cc @@ -13,8 +13,8 @@ // limitations under the License. // ======================================================================== // -// This is a naive tool to write tags to MSI packages. It just blindly adds -// tags without checking: +// This is a tool to write tags to MSI packages. It just adds tags without +// checking: // 1. Whether the input file is a valid MSI package. // 2. Whether the file is tagged already. @@ -84,7 +84,7 @@ int WriteMsiTag( // Actual tag. // Formerly: std::string tag_ansi(tag, tag + tag_length); - // That code converted UTF-16 to iso-8859-1 (Latin1) via blindly + // That code converted UTF-16 to iso-8859-1 (Latin1) via // chopping all but the lowest eight bits. This is not utf-8; it's // Latin1, with garbage for anything outside that repertoire. Newer // compilers rightly complain about data loss. Apparently that is diff --git a/omaha/tools/build.scons b/omaha/tools/build.scons index 7dc85d162..3cc9e6a8d 100644 --- a/omaha/tools/build.scons +++ b/omaha/tools/build.scons @@ -25,6 +25,7 @@ if not env.Bit('min'): 'MsiTagger', 'performondemand', 'ReadTag', + 'runupdate3web', 'SetShutDownEvent' ] diff --git a/omaha/tools/eckeytool/README.md b/omaha/tools/eckeytool/README.md index ab98d1c61..8ed3e48ce 100644 --- a/omaha/tools/eckeytool/README.md +++ b/omaha/tools/eckeytool/README.md @@ -1,7 +1,13 @@ -This tool converts an ECDSA public key in a PEM format to a CUP-ECDSA public key in a text format, which can be included as source code in the Omaha project tree. +This tool converts an ECDSA public key in a PEM format to a CUP-ECDSA public key +in a text format, which can be included as source code in the Omaha project +tree. -This directory is not built as part of the Omaha tree. Instead, build this code on Linux, using the provide makefile. The code provided has a dependency on openssl. To generate a key, use the following steps: -* generate an EC private key using openssl: `openssl ecparam -out priv.pem -name prime256v1 -genkey` -* extract the corresponding EC public key: `openssl ec -in priv.pem -pubout -out public.pem` +This directory is not built as part of the Omaha tree. Instead, build this code +on Linux, using the provide makefile. The code provided has a dependency on +openssl. To generate a key, use the following steps: +* generate an EC private key using openssl: + `openssl ecparam -out priv.pem -name prime256v1 -genkey` +* extract the corresponding EC public key: + `openssl ec -in priv.pem -pubout -out public.pem` * convert the EC public to a CUP ECDSA key: `./eckeytool public.pem 1` * take the output of the eckeytool and add it to your project tree. diff --git a/omaha/tools/eckeytool/eckeytool.c b/omaha/tools/eckeytool/eckeytool.c index 309c7ef26..5cbcf3367 100644 --- a/omaha/tools/eckeytool/eckeytool.c +++ b/omaha/tools/eckeytool/eckeytool.c @@ -69,8 +69,8 @@ int print_octets(const unsigned char* octets, size_t len, int version) { "// * Gy coordinate (256-bit integer, big-endian)\n", get_curr_year()); - printf("{0x%02d,\n", (unsigned int) version); - printf("0x%02d,", (unsigned int) octets[0]); + printf("{0x%02x,\n", (unsigned int) version); + printf("0x%02x,", (unsigned int) octets[0]); const unsigned char* point_octets = octets + 1; const size_t point_octets_len = len - 1; diff --git a/omaha/tools/generate_omaha3_idl.py b/omaha/tools/generate_omaha3_idl.py index 7e0445d6f..9a4bb8e7f 100644 --- a/omaha/tools/generate_omaha3_idl.py +++ b/omaha/tools/generate_omaha3_idl.py @@ -17,7 +17,7 @@ """Generates IDL file Omaha3 internfaces.""" -import commands +import subprocess import getopt import os import sys @@ -33,7 +33,7 @@ def _GetStatusOutput(cmd): if text[-1:] == "\n": text = text[:-1] return sts, text else: - return commands.getstatusoutput(cmd) + return subprocess.getstatusoutput(cmd) def _GenerateGuid(): @@ -67,7 +67,7 @@ def _GenerateIDLFile(idl_template_filename, idl_output_filename): def _Usage(): """Prints out script usage information.""" - print """ + print(""" generate_omaha3_idl.py: Write out the given IDL file. Usage: @@ -79,7 +79,7 @@ def _Usage(): --help Show this information. --idl_output_file filename Path/name of output IDL filename. --idl_template_file filename Path/name of input IDL template. -""" +""") def _Main(): diff --git a/omaha/tools/goopdump/build.scons b/omaha/tools/goopdump/build.scons index 4ebcb3864..4644ce4bd 100644 --- a/omaha/tools/goopdump/build.scons +++ b/omaha/tools/goopdump/build.scons @@ -33,7 +33,6 @@ lib_inputs = [ 'data_dumper_app_manager.cc', 'data_dumper_goopdate.cc', 'data_dumper_network.cc', - 'data_dumper_oneclick.cc', 'data_dumper_osdata.cc', 'dump_log.cc', 'goopdump.cc', @@ -84,7 +83,6 @@ exe_env.Append( 'crypt32.lib', 'delayimp.lib', 'Iphlpapi.lib', - 'msi.lib', 'mstask.lib', 'netapi32.lib', 'ole32.lib', diff --git a/omaha/tools/goopdump/data_dumper_app_manager.cc b/omaha/tools/goopdump/data_dumper_app_manager.cc index a07c818e7..fac10f094 100644 --- a/omaha/tools/goopdump/data_dumper_app_manager.cc +++ b/omaha/tools/goopdump/data_dumper_app_manager.cc @@ -64,7 +64,7 @@ CString GuidToFriendlyAppName(const GUID& guid) { // to customers. MapGuidToName guid_to_name[] = { {_T("{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}"), _T("Gears")}, - {_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), _T("Google Update")}, + {GOOPDATE_APP_ID, _T("Google Update")}, {_T("{8A69D345-D564-463C-AFF1-A69D9E530F96}"), _T("Chrome")}, }; diff --git a/omaha/tools/goopdump/data_dumper_goopdate.cc b/omaha/tools/goopdump/data_dumper_goopdate.cc index ade55337e..131f7364e 100644 --- a/omaha/tools/goopdump/data_dumper_goopdate.cc +++ b/omaha/tools/goopdump/data_dumper_goopdate.cc @@ -204,7 +204,7 @@ HRESULT DataDumperGoopdate::GetDllDir(bool is_machine, CString* dll_path) { } void DataDumperGoopdate::DumpGoogleUpdateIniFile(const DumpLog& dump_log) { - DumpHeader header(dump_log, _T("GoogleUpdate.ini File Contents")); + DumpHeader header(dump_log, MAIN_EXE_BASE_NAME _T(".ini File Contents")); DumpFileContents(dump_log, _T("c:\\googleupdate.ini"), 0); } @@ -229,7 +229,7 @@ void DataDumperGoopdate::DumpHostsFile(const DumpLog& dump_log) { void DataDumperGoopdate::DumpUpdateDevKeys(const DumpLog& dump_log) { DumpHeader header(dump_log, _T("UpdateDev Keys")); - DumpRegistryKeyData(dump_log, _T("HKLM\\Software\\Google\\UpdateDev")); + DumpRegistryKeyData(dump_log, _T("HKLM\\Software\\") PATH_COMPANY_NAME _T("\\UpdateDev")); } void DataDumperGoopdate::DumpLogFile(const DumpLog& dump_log) { @@ -340,7 +340,7 @@ void DataDumperGoopdate::DumpEventLog(const DumpLog& dump_log) { } void DataDumperGoopdate::DumpGoogleUpdateProcessInfo(const DumpLog& dump_log) { - DumpHeader header(dump_log, _T("GoogleUpdate.exe Process Info")); + DumpHeader header(dump_log, MAIN_EXE_BASE_NAME _T(".exe Process Info")); EnableDebugPrivilege(); @@ -364,7 +364,9 @@ void DataDumperGoopdate::DumpGoogleUpdateProcessInfo(const DumpLog& dump_log) { CString exe_file_name = process_entry32.szExeFile; exe_file_name.MakeLower(); - if (exe_file_name.Find(_T("googleupdate.exe")) >= 0) { + CString main_exe_file_name(MAIN_EXE_BASE_NAME _T(".exe")); + main_exe_file_name.MakeLower(); + if (exe_file_name.Find(main_exe_file_name) >= 0) { if (first) { first = false; } else { diff --git a/omaha/tools/goopdump/data_dumper_oneclick.cc b/omaha/tools/goopdump/data_dumper_oneclick.cc deleted file mode 100644 index 0af6a5cd7..000000000 --- a/omaha/tools/goopdump/data_dumper_oneclick.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2008-2009 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ======================================================================== - -#include "omaha/tools/goopdump/data_dumper_oneclick.h" - -#include "omaha/common/reg_key.h" -#include "omaha/tools/goopdump/dump_log.h" -#include "omaha/tools/goopdump/goopdump_cmd_line_parser.h" - -namespace omaha { - -DataDumperOneClick::DataDumperOneClick() { -} - -DataDumperOneClick::~DataDumperOneClick() { -} - -void DataDumperOneClick::DumpOneClickDataForVersion(const DumpLog& dump_log, - int plugin_version) { - - dump_log.WriteLine(_T("Trying Plugin Version: %d"), plugin_version); - - CString oneclick_name; - oneclick_name.Format(_T("Google.OneClickCtrl.%d"), plugin_version); - - CString reg_str; - reg_str.Format(_T("HKCR\\%s"), oneclick_name); - DumpRegValueStr(dump_log, reg_str, NULL); - - CString clsid; - reg_str.Append(_T("\\CLSID")); - DumpRegValueStrRet(dump_log, reg_str, NULL, &clsid); - - reg_str.Format(_T("HKCR\\MIME\\DataBase\\Content Type\\application/%s"), - oneclick_name); - DumpRegValueStr(dump_log, reg_str, _T("CLSID")); - - CString key; - key.Format(_T("HKCR\\CLSID\\%s"), clsid); - - if (!clsid.IsEmpty()) { - DumpRegistryKeyData(dump_log, key); - } - - CString typelib; - CString key2 = key; - key2.Append(_T("\\TypeLib")); - if (SUCCEEDED(RegKey::GetValue(key2, NULL, &typelib))) { - if (!typelib.IsEmpty()) { - key.Format(_T("HKCR\\Typelib\\%s\\1.0\\0\\win32"), typelib); - DumpRegistryKeyData(dump_log, key); - } - } - - dump_log.WriteLine(_T("")); -} - -HRESULT DataDumperOneClick::Process(const DumpLog& dump_log, - const GoopdumpCmdLineArgs& args) { - UNREFERENCED_PARAMETER(args); - - DumpHeader header(dump_log, _T("OneClick Data")); - - CString activex_version_str(ACTIVEX_VERSION_ANSI); - int activex_version = _ttoi(activex_version_str); - - for (int i = 1; i <= activex_version; ++i) { - DumpOneClickDataForVersion(dump_log, i); - } - - return S_OK; -} - -} // namespace omaha - diff --git a/omaha/tools/goopdump/goopdump.cc b/omaha/tools/goopdump/goopdump.cc index 58ac16826..eaaf46d4b 100644 --- a/omaha/tools/goopdump/goopdump.cc +++ b/omaha/tools/goopdump/goopdump.cc @@ -122,7 +122,9 @@ HRESULT Goopdump::Main(const TCHAR* cmd_line, int argc, TCHAR** argv) { ProcessMonitor process_monitor; GoopdateProcessMonitorCallback callback(dump_log_); std::vector patterns; - patterns.push_back(CString(_T("googleupdate.exe"))); + CString main_exe_file_name(MAIN_EXE_BASE_NAME _T(".exe")); + main_exe_file_name.MakeLower(); + patterns.push_back(main_exe_file_name); patterns.push_back(CString(_T("notepad.exe"))); process_monitor.StartWithPatterns(&callback, patterns); getchar(); diff --git a/omaha/tools/goopdump/goopdump_cmd_line_parser.cc b/omaha/tools/goopdump/goopdump_cmd_line_parser.cc index 12048d596..fe8988f47 100644 --- a/omaha/tools/goopdump/goopdump_cmd_line_parser.cc +++ b/omaha/tools/goopdump/goopdump_cmd_line_parser.cc @@ -38,7 +38,6 @@ HRESULT ParseGoopdumpCmdLine(int argc, std::vector valid_params; valid_params.push_back(_T("dumpapps")); - valid_params.push_back(_T("oneclick")); valid_params.push_back(_T("monitor")); valid_params.push_back(_T("file")); @@ -72,7 +71,6 @@ HRESULT ParseGoopdumpCmdLine(int argc, // If you don't pass anything, give them everything except monitoring. args->is_dump_general = true; args->is_dump_app_manager = true; - args->is_dump_oneclick = true; args->is_machine = true; args->is_user = true; } @@ -84,13 +82,6 @@ HRESULT ParseGoopdumpCmdLine(int argc, args->is_user = true; } - if (parser.HasSwitch(_T("oneclick"))) { - args->is_dump_general = true; - args->is_dump_oneclick = true; - args->is_machine = true; - args->is_user = true; - } - if (parser.HasSwitch(_T("monitor"))) { args->is_monitor = true; } diff --git a/omaha/tools/goopdump/goopdump_cmd_line_parser.h b/omaha/tools/goopdump/goopdump_cmd_line_parser.h index 96105061a..95cb581b9 100644 --- a/omaha/tools/goopdump/goopdump_cmd_line_parser.h +++ b/omaha/tools/goopdump/goopdump_cmd_line_parser.h @@ -29,14 +29,12 @@ struct GoopdumpCmdLineArgs { is_user(false), is_monitor(false), is_dump_app_manager(false), - is_dump_oneclick(false), is_dump_general(false), is_write_to_file(false) {} bool is_machine; // Display per-machine data. bool is_user; // Display per-user data (for current user only). bool is_monitor; // Is monitor mode (monitors real time activity). bool is_dump_app_manager; // Dump AppManager data. - bool is_dump_oneclick; // Dump OneClick data. bool is_dump_general; // Dump general OS and Omaha data. bool is_write_to_file; // Dump data to file. CString log_filename; // Filename of log to write to. diff --git a/omaha/tools/goopdump/process_commandline.cc b/omaha/tools/goopdump/process_commandline.cc index 62976f622..bd8b3bbb6 100644 --- a/omaha/tools/goopdump/process_commandline.cc +++ b/omaha/tools/goopdump/process_commandline.cc @@ -15,6 +15,7 @@ #include "omaha/tools/goopdump/process_commandline.h" +#include "omaha/base/utils.h" #include "omaha/common/debug.h" #include "omaha/common/error.h" #include "omaha/common/scoped_any.h" @@ -93,7 +94,7 @@ HRESULT GetCommandLineFromHandleWithMemory(HANDLE process_handle, } // Initialize data area for remote process. - scoped_library hmodule_kernel32(::LoadLibrary(L"kernel32.dll")); + scoped_library hmodule_kernel32(LoadSystemLibrary(L"kernel32.dll"); if (!hmodule_kernel32) { return HRESULTFromLastError(); } diff --git a/omaha/tools/performondemand/build.scons b/omaha/tools/performondemand/build.scons index fcc50fade..5cc1f32bb 100644 --- a/omaha/tools/performondemand/build.scons +++ b/omaha/tools/performondemand/build.scons @@ -25,9 +25,6 @@ def BuildPerformOnDemand(local_env): 'UNICODE', '_UNICODE' ], - CPPPATH = [ - '$OBJ_ROOT', - ], LIBS = [ local_env['atls_libs'][local_env.Bit('debug')], local_env['crt_libs'][local_env.Bit('debug')], diff --git a/omaha/tools/performondemand/performondemand.cc b/omaha/tools/performondemand/performondemand.cc index 4a4e599c0..0e68d42a7 100644 --- a/omaha/tools/performondemand/performondemand.cc +++ b/omaha/tools/performondemand/performondemand.cc @@ -191,15 +191,15 @@ class VistaProxyRegistrar { ~VistaProxyRegistrar() { if (googleupdate_cookie_) { - VERIFY1(SUCCEEDED(::CoRevokeClassObject(googleupdate_cookie_))); + VERIFY_SUCCEEDED(::CoRevokeClassObject(googleupdate_cookie_)); } if (jobobserver_cookie_) { - VERIFY1(SUCCEEDED(::CoRevokeClassObject(jobobserver_cookie_))); + VERIFY_SUCCEEDED(::CoRevokeClassObject(jobobserver_cookie_)); } if (progresswndevents_cookie_) { - VERIFY1(SUCCEEDED(::CoRevokeClassObject(progresswndevents_cookie_))); + VERIFY_SUCCEEDED(::CoRevokeClassObject(progresswndevents_cookie_)); } if (is_impersonated) { diff --git a/omaha/tools/proxy_clsid_utils.py b/omaha/tools/proxy_clsid_utils.py index 827a914fa..d4244a927 100644 --- a/omaha/tools/proxy_clsid_utils.py +++ b/omaha/tools/proxy_clsid_utils.py @@ -17,7 +17,7 @@ """Generates Omaha build file and Omaha customization unit test file.""" -import commands +import subprocess import os @@ -37,7 +37,7 @@ def _GetStatusOutput(cmd): if text[-1:] == "\n": text = text[:-1] return sts, text else: - return commands.getstatusoutput(cmd) + return subprocess.getstatusoutput(cmd) def _GenerateGuid(): diff --git a/omaha/tools/retry.py b/omaha/tools/retry.py index 6c6756ca2..3c63efb35 100644 --- a/omaha/tools/retry.py +++ b/omaha/tools/retry.py @@ -11,11 +11,11 @@ cmd = sys.argv[3:] # Open stdout with no buffering. -sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) +sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0) for i in range(times): if i: - print 'Retrying %d...' % i + print('Retrying %d...' % i) retcode = subprocess.call(cmd, stderr=subprocess.STDOUT) if retcode == 0: sys.exit(0) diff --git a/omaha/tools/runupdate3web/build.scons b/omaha/tools/runupdate3web/build.scons new file mode 100644 index 000000000..ac75b59f6 --- /dev/null +++ b/omaha/tools/runupdate3web/build.scons @@ -0,0 +1,74 @@ +#!/usr/bin/python2.4 +# +# Copyright 2009-2010 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ======================================================================== + + +Import('env') + +run_as_invoker = env.RES('run_as_invoker.res', '$MAIN_DIR/base/run_as_invoker.rc') +env.Depends(run_as_invoker, '$MAIN_DIR/base/run_as_invoker.manifest') + +def Buildrunupdate3web(local_env): + local_env.Append( + CPPDEFINES = [ + 'UNICODE', + '_UNICODE' + ], + LIBS = [ + local_env['atls_libs'][local_env.Bit('debug')], + local_env['crt_libs'][local_env.Bit('debug')], + 'comctl32.lib', + 'crypt32.lib', + 'Iphlpapi.lib', + 'mstask.lib', + 'netapi32.lib', + 'psapi.lib', + 'shlwapi.lib', + 'urlmon.lib', + 'userenv.lib', + 'version.lib', + 'wininet.lib', + 'wtsapi32.lib', + + local_env.GetMultiarchLibName('base'), + local_env.GetMultiarchLibName('common'), + local_env.GetMultiarchLibName('logging'), + local_env.GetMultiarchLibName('net'), + local_env.GetMultiarchLibName('statsreport'), + ], + ) + + local_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS']) + local_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE'] + + target_name = 'runupdate3web%s' % ('', '64')[local_env.Bit('x64')] + inputs = [ + 'runupdate3web.cc', + run_as_invoker, + ] + if local_env.Bit('x64'): + inputs.append('$OBJ_ROOT/goopdate/omaha3_idl_64_i.obj64') + else: + inputs.append('$OBJ_ROOT/goopdate/omaha3_idl_i.obj') + + local_env.ComponentTestProgram( + prog_name=target_name, + source=inputs, + COMPONENT_TEST_RUNNABLE=False + ) + +Buildrunupdate3web(env.Clone()) +Buildrunupdate3web(env.CloneAndMake64Bit()) diff --git a/omaha/tools/runupdate3web/runupdate3web.cc b/omaha/tools/runupdate3web/runupdate3web.cc new file mode 100644 index 000000000..461ca1ecb --- /dev/null +++ b/omaha/tools/runupdate3web/runupdate3web.cc @@ -0,0 +1,590 @@ +// Copyright 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== +// +// A test tool for interacting with the Update3Web interfaces. + +#include +#include + +#include + +#include "goopdate/omaha3_idl.h" +#include "omaha/base/debug.h" +#include "omaha/base/utils.h" +#include "omaha/third_party/smartany/scoped_any.h" + +namespace omaha { + +namespace { + +enum Operation { + CHECK_FOR_UPDATE = 0, + DOWNLOAD = 1, + INSTALL = 2, + UPDATE = 3, + LAUNCH_COMMAND = 4, +}; + +const size_t kLaunchCmdNumberOfParameters = 9; + +} // namespace + +HRESULT InitializeBundle(bool is_machine, CComPtr& bundle_web) { + CComPtr update3web; + HRESULT hr = update3web.CoCreateInstance( + is_machine ? __uuidof(GoogleUpdate3WebMachineClass) + : __uuidof(GoogleUpdate3WebUserClass)); + if (FAILED(hr)) { + wprintf(_T("Could not create COM instance [0x%x]\n"), hr); + return hr; + } + + CComPtr bundle_dispatch; + hr = update3web->createAppBundleWeb(&bundle_dispatch); + if (FAILED(hr)) { + wprintf(_T("createAppBundleWeb failed [0x%x]\n"), hr); + return hr; + } + + CComPtr bundle; + hr = bundle_dispatch.QueryInterface(&bundle); + if (FAILED(hr)) { + wprintf(_T("bundle_dispatch.QueryInterface failed [0x%x]\n"), hr); + return hr; + } + + hr = bundle->initialize(); + if (FAILED(hr)) { + wprintf(_T("bundle->initialize failed [0x%x]\n"), hr); + return hr; + } + + bundle_web = bundle; + return S_OK; +} + +HRESULT DoLoopUntilDone(Operation operation, CComPtr bundle) { + bool done = false; + while (!done) { + if (!bundle) { + wprintf(_T("No bundle is defined!\n")); + return E_UNEXPECTED; + } + + CComPtr app_dispatch; + HRESULT hr = bundle->get_appWeb(0, &app_dispatch); + if (FAILED(hr)) { + wprintf(_T("bundle->get_appWeb failed [0x%x]\n"), hr); + return hr; + } + + CComPtr app; + hr = app_dispatch.QueryInterface(&app); + if (FAILED(hr)) { + wprintf(_T("app_dispatch.QueryInterface failed [0x%x]\n"), hr); + return hr; + } + + CComPtr state_dispatch; + hr = app->get_currentState(&state_dispatch); + if (FAILED(hr)) { + wprintf(_T("app->currentState failed [0x%x]\n"), hr); + return hr; + } + + CComPtr state; + hr = state_dispatch.QueryInterface(&state); + if (FAILED(hr)) { + wprintf(_T("state_dispatch.QueryInterface failed [0x%x]\n"), hr); + return hr; + } + + CString stateDescription; + CString extraData; + + LONG state_value = 0; + hr = state->get_stateValue(&state_value); + if (FAILED(hr)) { + wprintf(_T("state->get_stateValue failed [0x%x]\n"), hr); + return hr; + } + + switch (state_value) { + case STATE_INIT: + stateDescription = _T("Initializating..."); + break; + + case STATE_WAITING_TO_CHECK_FOR_UPDATE: + case STATE_CHECKING_FOR_UPDATE: { + stateDescription = _T("Checking for update..."); + + CComPtr current_version_dispatch; + hr = app->get_currentVersionWeb(¤t_version_dispatch); + if (FAILED(hr)) { + wprintf(_T("app->get_currentVersionWeb failed [0x%x]\n"), hr); + return hr; + } + + CComPtr current_version; + hr = current_version_dispatch.QueryInterface(¤t_version); + if (FAILED(hr)) { + wprintf(_T("current_version_dispatch.QueryInterface failed [0x%x]\n"), + hr); + return hr; + } + + CComBSTR version; + hr = current_version->get_version(&version); + if (FAILED(hr)) { + wprintf(_T("current_version->get_version failed [0x%x]\n"), hr); + return hr; + } + + extraData.Format(_T("[Current Version][%s]"), version); + break; + } + + case STATE_UPDATE_AVAILABLE: { + stateDescription = _T("Update available!"); + + CComPtr next_version_dispatch; + hr = app->get_nextVersionWeb(&next_version_dispatch); + if (FAILED(hr)) { + wprintf(_T("app->get_nextVersionWeb failed [0x%x]\n"), hr); + return hr; + } + + CComPtr next_version; + hr = next_version_dispatch.QueryInterface(&next_version); + if (FAILED(hr)) { + wprintf(_T("next_version_dispatch.QueryInterface failed [0x%x]\n"), + hr); + return hr; + } + + CComBSTR version; + hr = next_version->get_version(&version); + if (FAILED(hr)) { + wprintf(_T("next_version->get_version failed [0x%x]\n"), hr); + return hr; + } + + extraData.Format(_T("[Next Version][%s]"), version); + if (operation == CHECK_FOR_UPDATE) { + done = true; + break; + } + + hr = bundle->download(); + if (FAILED(hr)) { + wprintf(_T("bundle->download failed [0x%x]\n"), hr); + return hr; + } + + break; + } + + case STATE_WAITING_TO_DOWNLOAD: + case STATE_RETRYING_DOWNLOAD: + stateDescription = _T("Contacting server..."); + break; + + case STATE_DOWNLOADING: { + stateDescription = _T("Downloading..."); + + ULONG bytes_downloaded = 0; + hr = state->get_bytesDownloaded(&bytes_downloaded); + if (FAILED(hr)) { + wprintf(_T("state->get_bytesDownloaded failed [0x%x]\n"), hr); + return hr; + } + + ULONG total_bytes_to_download = 0; + hr = state->get_totalBytesToDownload(&total_bytes_to_download); + if (FAILED(hr)) { + wprintf(_T("state->get_totalBytesToDownload failed [0x%x]\n"), hr); + return hr; + } + + LONG download_time_remaining_ms = 0; + hr = state->get_downloadTimeRemainingMs(&download_time_remaining_ms); + if (FAILED(hr)) { + wprintf(_T("state->get_downloadTimeRemainingMs failed [0x%x]\n"), hr); + return hr; + } + + extraData.Format( + _T("[Bytes downloaded][%d][Bytes total][%d][Time remaining][%d]"), + bytes_downloaded, total_bytes_to_download, + download_time_remaining_ms); + break; + } + + case STATE_DOWNLOAD_COMPLETE: + case STATE_EXTRACTING: + case STATE_APPLYING_DIFFERENTIAL_PATCH: + case STATE_READY_TO_INSTALL: { + stateDescription = _T("Download completed!"); + + CComPtr current_version_dispatch; + hr = app->get_currentVersionWeb(¤t_version_dispatch); + if (FAILED(hr)) { + wprintf(_T("app->get_currentVersionWeb failed [0x%x]\n"), hr); + return hr; + } + + CComPtr current_version; + hr = current_version_dispatch.QueryInterface(¤t_version); + if (FAILED(hr)) { + wprintf(_T("current_version_dispatch.QueryInterface failed [0x%x]\n"), + hr); + return hr; + } + + CComBSTR version; + hr = current_version->get_version(&version); + if (FAILED(hr)) { + wprintf(_T("current_version->get_version failed [0x%x]\n"), hr); + return hr; + } + + ULONG bytes_downloaded = 0; + hr = state->get_bytesDownloaded(&bytes_downloaded); + if (FAILED(hr)) { + wprintf(_T("state->get_bytesDownloaded failed [0x%x]\n"), hr); + return hr; + } + + ULONG total_bytes_to_download = 0; + hr = state->get_totalBytesToDownload(&total_bytes_to_download); + if (FAILED(hr)) { + wprintf(_T("state->get_totalBytesToDownload failed [0x%x]\n"), hr); + return hr; + } + + extraData.Format(_T("[Current Version][%s]"), version); + extraData.AppendFormat(_T("[Bytes downloaded][%d][Bytes total][%d]"), + bytes_downloaded, total_bytes_to_download); + if (operation == DOWNLOAD) { + done = true; + break; + } + + hr = bundle->install(); + if (FAILED(hr)) { + wprintf(_T("bundle->install failed [0x%x]\n"), hr); + return hr; + } + + break; + } + + case STATE_WAITING_TO_INSTALL: + case STATE_INSTALLING: { + stateDescription = _T("Installing..."); + + LONG install_progress = 0; + hr = state->get_installProgress(&install_progress); + if (FAILED(hr)) { + wprintf(_T("state->get_installProgress failed [0x%x]\n"), hr); + return hr; + } + + LONG install_time_remaining_ms = 0; + hr = state->get_installTimeRemainingMs(&install_time_remaining_ms); + if (FAILED(hr)) { + wprintf(_T("state->get_installTimeRemainingMs failed [0x%x]\n"), hr); + return hr; + } + + extraData.Format(_T("[Install Progress][%d][Time remaining][%d]"), + install_progress, install_time_remaining_ms); + break; + } + + case STATE_INSTALL_COMPLETE: + stateDescription = _T("Done!"); + done = true; + break; + + case STATE_PAUSED: + stateDescription = _T("Paused..."); + break; + + case STATE_NO_UPDATE: + stateDescription = _T("No update available!"); + done = true; + break; + + case STATE_ERROR: { + stateDescription = _T("Error!"); + + LONG error_code = 0; + hr = state->get_errorCode(&error_code); + if (FAILED(hr)) { + wprintf(_T("state->get_errorCode failed [0x%x]\n"), hr); + return hr; + } + + CComBSTR completion_message; + hr = state->get_completionMessage(&completion_message); + if (FAILED(hr)) { + wprintf(_T("state->get_completionMessage failed [0x%x]\n"), hr); + return hr; + } + + LONG installer_result_code = 0; + hr = state->get_installerResultCode(&installer_result_code); + if (FAILED(hr)) { + wprintf(_T("state->get_installerResultCode failed [0x%x]\n"), hr); + return hr; + } + + extraData.Format( + _T("[errorCode][%d][completionMessage][%s][installerResultCode][%") + _T("d]"), + error_code, completion_message, installer_result_code); + done = true; + break; + } + + default: + stateDescription = _T("Unhandled state..."); + break; + } + + CString state_out; + state_out.Format(_T("[State][%d][%s][%s]\n"), state_value, stateDescription, + extraData); + wprintf(state_out); + + ::Sleep(100); + } + + return S_OK; +} + +HRESULT DoAppOperation(Operation operation, const CComBSTR& appid, + bool is_machine) { + CComPtr bundle; + HRESULT hr = InitializeBundle(is_machine, bundle); + if (FAILED(hr)) { + wprintf(_T("InitializeBundle failed [0x%x]\n"), hr); + return hr; + } + + hr = (operation == CHECK_FOR_UPDATE || operation == UPDATE) + ? bundle->createInstalledApp(appid) + : bundle->createApp(appid, CComBSTR(_T("GPEZ")), CComBSTR(_T("en")), + CComBSTR(_T(""))); + if (FAILED(hr)) { + wprintf(_T("App Creation failed [0x%x]\n"), hr); + return hr; + } + + hr = bundle->checkForUpdate(); + if (FAILED(hr)) { + wprintf(_T("bundle->checkForUpdate failed [0x%x]\n"), hr); + return hr; + } + + return DoLoopUntilDone(operation, bundle); +} + +HRESULT DoLaunchCommand(const CComBSTR& appid, bool is_machine, + const CComBSTR& command, + const std::vector& argument_list) { + CComPtr bundle; + HRESULT hr = InitializeBundle(is_machine, bundle); + if (FAILED(hr)) { + wprintf(_T("InitializeBundle failed [0x%x]\n"), hr); + return hr; + } + + hr = bundle->createInstalledApp(appid); + if (FAILED(hr)) { + wprintf(_T("bundle->createInstalledApp failed [0x%x]\n"), hr); + return hr; + } + + CComPtr app_dispatch; + hr = bundle->get_appWeb(0, &app_dispatch); + if (FAILED(hr)) { + wprintf(_T("bundle->appWeb failed [0x%x]\n"), hr); + return hr; + } + + CComPtr app; + hr = app_dispatch.QueryInterface(&app); + if (FAILED(hr)) { + wprintf(_T("app_dispatch.QueryInterface failed [0x%x]\n"), hr); + return hr; + } + + CComPtr cmd_dispatch; + hr = app->get_command(command, &cmd_dispatch); + if (FAILED(hr)) { + wprintf(_T("app->command failed [0x%x]\n"), hr); + return hr; + } + + CComPtr cmd; + hr = cmd_dispatch.QueryInterface(&cmd); + if (FAILED(hr)) { + wprintf(_T("cmd_dispatch.QueryInterface failed [0x%x]\n"), hr); + return hr; + } + + wprintf(_T("Launching command.\n")); + + if (argument_list.size() != kLaunchCmdNumberOfParameters) { + wprintf(_T("Invalid number of LaunchCommand arguments, needs to be %zd.\n"), + kLaunchCmdNumberOfParameters); + return E_INVALIDARG; + } + + hr = cmd->execute(argument_list[0], argument_list[1], argument_list[2], + argument_list[3], argument_list[4], argument_list[5], + argument_list[6], argument_list[7], argument_list[8]); + + if (FAILED(hr)) { + wprintf(_T("cmd->execute failed [0x%x]\n"), hr); + return hr; + } + + wprintf(_T("Command launched.\n")); + + while (true) { + UINT status = 0; + hr = cmd->get_status(&status); + if (FAILED(hr)) { + wprintf(_T("cmd->get_status failed [0x%x]\n"), hr); + return hr; + } + + CString stateDescription = _T(""); + + switch (status) { + case COMMAND_STATUS_RUNNING: + stateDescription = _T("Running..."); + break; + + case COMMAND_STATUS_ERROR: + stateDescription = _T("Error!"); + break; + + case COMMAND_STATUS_COMPLETE: { + DWORD exit_code = 0; + hr = cmd->get_exitCode(&exit_code); + if (FAILED(hr)) { + wprintf(_T("cmd->get_exitCode failed [0x%x]\n"), hr); + return hr; + } + + stateDescription.Format(_T("Exited with code %d."), exit_code); + break; + } + + default: + stateDescription = _T("Unhandled state!"); + break; + } + + CString state_out; + state_out.Format(_T("[State][%d][%s]\n"), status, stateDescription); + wprintf(state_out); + + if (status != COMMAND_STATUS_RUNNING) { + return E_UNEXPECTED; + } + + ::Sleep(100); + } + + return hr; +} + +bool ParseAndRun(int argc, TCHAR* argv[]) { + ASSERT1(argc); + ASSERT1(argv); + + scoped_co_init co_init; + VERIFY1(SUCCEEDED(co_init.hresult())); + + if (argc < 4) { + return false; + } + + CString guid = argv[1]; + + // Verify that the guid is valid. + GUID parsed; + if (FAILED(StringToGuidSafe(guid, &parsed))) { + return false; + } + + CComBSTR app_guid = argv[1]; + + bool is_machine = !!_ttoi(argv[2]); + Operation operation = static_cast(_ttoi(argv[3])); + + switch (operation) { + case CHECK_FOR_UPDATE: + case DOWNLOAD: + case INSTALL: + case UPDATE: + if (argc != 4) { + return false; + } + DoAppOperation(operation, app_guid, is_machine); + break; + + case LAUNCH_COMMAND: { + if (argc < 5) { + return false; + } + int command = _ttoi(argv[4]); + std::vector argument_list; + if (argc > 5) { + argument_list.insert(argument_list.begin(), argv + 5, argv + argc); + } + argument_list.resize(kLaunchCmdNumberOfParameters); + + DoLaunchCommand(app_guid, is_machine, command, argument_list); + break; + } + + default: + return false; + } + + return true; +} + +} // namespace omaha + +int _tmain(int argc, TCHAR* argv[]) { + if (!omaha::ParseAndRun(argc, argv)) { + wprintf( + _T("Usage: {GUID} ") + _T("{is_machine: 0|1} ") + _T("{0|1|2|3|4==CheckForUpdate|Download|Install|Update|LaunchCommand} ") + _T("{command_id?}")); + return -1; + } + + return 0; +} + diff --git a/omaha/ui/build.scons b/omaha/ui/build.scons index b19d8c72b..821ae31e3 100644 --- a/omaha/ui/build.scons +++ b/omaha/ui/build.scons @@ -28,12 +28,6 @@ inputs = [ 'yes_no_dialog.cc', ] -# Need to look in output dir to find .h files generated by midl compiler. -# This also allows Hammer to understand dependencies between this subdir -# and the .idl files in the goopdate folder. -local_env['CPPPATH'] += ['$OBJ_ROOT'] - - # Build these into a static library. local_env.ComponentLibrary('ui', inputs) diff --git a/omaha/ui/complete_wnd.cc b/omaha/ui/complete_wnd.cc index c46d01120..ec1287f6d 100644 --- a/omaha/ui/complete_wnd.cc +++ b/omaha/ui/complete_wnd.cc @@ -94,7 +94,7 @@ LRESULT CompleteWnd::OnClickedButton(WORD notify_code, ASSERT1(is_complete()); handled = true; - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return 0; } @@ -128,7 +128,7 @@ LRESULT CompleteWnd::OnClickedGetHelp(WORD notify_code, } bool CompleteWnd::MaybeCloseWindow() { - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return true; } @@ -167,7 +167,7 @@ void CompleteWnd::DisplayCompletionDialog(bool is_success, } } - VERIFY1(SUCCEEDED(SetControlState(is_success))); + VERIFY_SUCCEEDED(SetControlState(is_success)); return; } diff --git a/omaha/ui/owner_draw_title_bar.cc b/omaha/ui/owner_draw_title_bar.cc index b6ec7506b..ffbe8d635 100644 --- a/omaha/ui/owner_draw_title_bar.cc +++ b/omaha/ui/owner_draw_title_bar.cc @@ -1,4 +1,4 @@ - // Copyright 2013 Google Inc. +// Copyright 2013 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,6 +25,39 @@ namespace omaha { +namespace { + +// This function returns |true| if the system is in high contrast mode. +bool IsHighContrastOn() { + HIGHCONTRAST hc = {0}; + hc.cbSize = sizeof(HIGHCONTRAST); + + if (!::SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &hc, 0)) { + return false; + } + + return hc.dwFlags & HCF_HIGHCONTRASTON; +} + +// This function returns the system color corresponding to +// |high_contrast_color_index| if the system is in high contrast mode. +// Otherwise, it returns the |normal_color|. +COLORREF GetColor(COLORREF normal_color, int high_contrast_color_index) { + return IsHighContrastOn() ? ::GetSysColor(high_contrast_color_index) : + normal_color; +} + +// This function returns the system color brush corresponding to +// |high_contrast_color_index| if the system is in high contrast mode. +// Otherwise, it returns the |normal_brush|. +HBRUSH GetColorBrush(const CBrush& normal_brush, + int high_contrast_color_index) { + return IsHighContrastOn() ? ::GetSysColorBrush(high_contrast_color_index) : + HBRUSH{normal_brush}; +} + +} // namespace + CaptionButton::CaptionButton() : bk_color_(RGB(0, 0, 0)), foreground_brush_(::CreateSolidBrush(kCaptionForegroundColor)), @@ -140,7 +173,9 @@ void CaptionButton::DrawItem(LPDRAWITEMSTRUCT draw_item_struct) { CRect button_rect; VERIFY1(GetClientRect(&button_rect)); - COLORREF bk_color(is_mouse_hovering_ ? kCaptionBkHover : bk_color_); + COLORREF bk_color(is_mouse_hovering_ ? GetColor(kCaptionBkHover, + COLOR_ACTIVECAPTION) : + GetColor(bk_color_, COLOR_WINDOW)); dc.FillSolidRect(&button_rect, bk_color); int rgn_width = button_rect.Width() * 12 / 31; @@ -151,7 +186,7 @@ void CaptionButton::DrawItem(LPDRAWITEMSTRUCT draw_item_struct) { rgn.OffsetRgn((button_rect.Width() - rgn_width) / 2, (button_rect.Height() - rgn_height) / 2); - VERIFY1(dc.FillRgn(rgn, foreground_brush_)); + VERIFY1(dc.FillRgn(rgn, GetColorBrush(foreground_brush_, COLOR_WINDOWTEXT))); const UINT button_state(draw_item_struct->itemState); if (button_state & ODS_FOCUS && button_state & ODS_SELECTED) { @@ -330,7 +365,7 @@ LRESULT OwnerDrawTitleBarWindow::OnEraseBkgnd(UINT, CRect rect; VERIFY1(GetClientRect(&rect)); - dc.FillSolidRect(&rect, bk_color_); + dc.FillSolidRect(&rect, GetColor(bk_color_, COLOR_WINDOW)); return 1; } @@ -568,10 +603,10 @@ LRESULT CustomDlgColors::OnCtrlColor(UINT, handled = TRUE; CDCHandle dc(reinterpret_cast(wparam)); - SetBkColor(dc, bk_color_); - SetTextColor(dc, text_color_); + SetBkColor(dc, GetColor(bk_color_, COLOR_WINDOW)); + SetTextColor(dc, GetColor(text_color_, COLOR_WINDOWTEXT)); - return reinterpret_cast(static_cast(bk_brush_)); + return reinterpret_cast(GetColorBrush(bk_brush_, COLOR_WINDOW)); } CustomProgressBarCtrl::CustomProgressBarCtrl() @@ -650,7 +685,7 @@ LRESULT CustomProgressBarCtrl::OnPaint(UINT, ASSERT1(progress_bar_rect.left <= progress_bar_rect.right); } - CRgnHandle rgn(::CreateRectRgnIndirect(&client_rect)); + CRgn rgn(::CreateRectRgnIndirect(&client_rect)); CRgn rgn_temp(::CreateRectRgnIndirect(&progress_bar_rect)); VERIFY1(rgn.CombineRgn(rgn_temp, RGN_DIFF) != RGN_ERROR); @@ -685,7 +720,7 @@ LRESULT CustomProgressBarCtrl::OnPaint(UINT, dc.FrameRect(r, empty_frame_brush_); r.DeflateRect(1, 1); - dc.FillSolidRect(r, empty_fill_color_); + dc.FillSolidRect(r, GetColor(empty_fill_color_, COLOR_WINDOWTEXT)); } } @@ -721,7 +756,10 @@ LRESULT CustomProgressBarCtrl::OnPaint(UINT, kProgressInnerFrameDark); progress_bar_rect.DeflateRect(1, 1); - GradientFill(dc, progress_bar_rect, bar_color_light_, bar_color_dark_); + GradientFill(dc, + progress_bar_rect, + GetColor(bar_color_light_, COLOR_WINDOW), + GetColor(bar_color_dark_, COLOR_WINDOW)); return 0; } @@ -823,4 +861,3 @@ LRESULT CustomProgressBarCtrl::OnSetMarquee(UINT, } } // namespace omaha - diff --git a/omaha/ui/progress_wnd.cc b/omaha/ui/progress_wnd.cc index 2f2ca5135..8a0e39347 100644 --- a/omaha/ui/progress_wnd.cc +++ b/omaha/ui/progress_wnd.cc @@ -93,7 +93,7 @@ InstallStoppedWnd::InstallStoppedWnd(CMessageLoop* message_loop, HWND parent) InstallStoppedWnd::~InstallStoppedWnd() { CORE_LOG(L3, (_T("[InstallStoppedWnd::~InstallStoppedWnd]"))); if (IsWindow()) { - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); } } @@ -189,13 +189,14 @@ LRESULT ProgressWnd::OnInitDialog(UINT message, UNREFERENCED_PARAMETER(handled); InitializeDialog(); + GetWindowText(CStrBuf(base_window_title_, 256), 256); - VERIFY1(SUCCEEDED(SetMarqueeMode(true))); + VERIFY_SUCCEEDED(SetMarqueeMode(true)); CString state_text; VERIFY1(state_text.LoadString(IDS_INITIALIZING)); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), state_text)); - VERIFY1(SUCCEEDED(ChangeControlState())); + DisplayNewState(state_text); + VERIFY_SUCCEEDED(ChangeControlState()); metrics_timer_.reset(new HighresTimer); @@ -247,7 +248,7 @@ bool ProgressWnd::MaybeCloseWindow() { } } - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return true; } @@ -316,7 +317,7 @@ LRESULT ProgressWnd::OnClickedButton(WORD notify_code, // code does anything that might delay the UI response. This should be true // since we won't actually be restarting browsers, etc. from the UI. handled = true; - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return 0; } @@ -360,7 +361,7 @@ LRESULT ProgressWnd::OnInstallStopped(UINT msg, void ProgressWnd::HandleCancelRequest() { CString s; VERIFY1(s.LoadString(IDS_CANCELING)); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); + DisplayNewState(s); if (is_canceled_) { return; @@ -387,9 +388,9 @@ void ProgressWnd::OnCheckingForUpdate() { CString s; VERIFY1(s.LoadString(IDS_WAITING_TO_CONNECT)); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); + DisplayNewState(s); - VERIFY1(SUCCEEDED(ChangeControlState())); + VERIFY_SUCCEEDED(ChangeControlState()); } void ProgressWnd::OnUpdateAvailable(const CString& app_id, @@ -403,21 +404,6 @@ void ProgressWnd::OnUpdateAvailable(const CString& app_id, ASSERT1(thread_id() == ::GetCurrentThreadId()); - if (!app_id.CompareNoCase(kChromeAppId)) { - HBITMAP app_bitmap = reinterpret_cast(::LoadImage( - app_util::GetCurrentModuleHandle(), - MAKEINTRESOURCE(IDB_CHROME), - IMAGE_BITMAP, - 0, - 0, - LR_SHARED)); - ASSERT1(app_bitmap); - SendDlgItemMessage(IDC_APP_BITMAP, - STM_SETIMAGE, - IMAGE_BITMAP, - reinterpret_cast(app_bitmap)); - } - if (!IsWindow()) { return; } @@ -438,9 +424,9 @@ void ProgressWnd::OnWaitingToDownload(const CString& app_id, CString s; VERIFY1(s.LoadString(IDS_WAITING_TO_DOWNLOAD)); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); + DisplayNewState(s); - VERIFY1(SUCCEEDED(ChangeControlState())); + VERIFY_SUCCEEDED(ChangeControlState()); } // May be called repeatedly during download. @@ -491,15 +477,15 @@ void ProgressWnd::OnDownloading(const CString& app_id, // Reduces flicker by only updating the control if the text has changed. CString orig_text; if (!GetDlgItemText(IDC_INSTALLER_STATE_TEXT, orig_text) || s != orig_text) { - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); + DisplayNewState(s); } - VERIFY1(SUCCEEDED(ChangeControlState())); + VERIFY_SUCCEEDED(ChangeControlState()); // When the network is connecting keep the marquee moving, otherwise // the user has no indication something is still going on. // TODO(omaha): when resuming an incomplete download this will not work. - VERIFY1(SUCCEEDED(SetMarqueeMode(pos == 0))); + VERIFY_SUCCEEDED(SetMarqueeMode(pos == 0)); if (pos > 0) { SendDlgItemMessage(IDC_PROGRESS, PBM_SETPOS, pos, 0); } @@ -524,8 +510,8 @@ void ProgressWnd::OnWaitingRetryDownload(const CString& app_id, int retry_time_in_sec = static_cast(CeilingDivide(next_retry_time - now, kSecsTo100ns)); s.FormatMessage(IDS_DOWNLOAD_RETRY, retry_time_in_sec); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); - VERIFY1(SUCCEEDED(ChangeControlState())); + DisplayNewState(s); + VERIFY_SUCCEEDED(ChangeControlState()); } } @@ -547,9 +533,9 @@ void ProgressWnd::OnWaitingToInstall(const CString& app_id, CString s; VERIFY1(s.LoadString(IDS_WAITING_TO_INSTALL)); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); + DisplayNewState(s); - VERIFY1(SUCCEEDED(ChangeControlState())); + VERIFY_SUCCEEDED(ChangeControlState()); } // If we want to instead close the window and start install, call @@ -581,12 +567,12 @@ void ProgressWnd::OnInstalling(const CString& app_id, CString s; VERIFY1(s.LoadString(IDS_INSTALLING)); - VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s)); + DisplayNewState(s); - VERIFY1(SUCCEEDED(ChangeControlState())); + VERIFY_SUCCEEDED(ChangeControlState()); } - VERIFY1(SUCCEEDED(SetMarqueeMode(pos <= 0))); + VERIFY_SUCCEEDED(SetMarqueeMode(pos <= 0)); if (pos > 0) { SendDlgItemMessage(IDC_PROGRESS, PBM_SETPOS, pos, 0); } @@ -616,7 +602,7 @@ void ProgressWnd::OnPause() { // TODO(omaha): implement time left. - VERIFY1(SUCCEEDED(ChangeControlState())); + VERIFY_SUCCEEDED(ChangeControlState()); } void ProgressWnd::DeterminePostInstallUrls(const ObserverCompletionInfo& info) { @@ -635,7 +621,7 @@ void ProgressWnd::DeterminePostInstallUrls(const ObserverCompletionInfo& info) { } // TODO(omaha): We can eliminate this function is we have a better UI that can -// show compeltion status for each app in the bundle. +// show completion status for each app in the bundle. // // Overall completion code is determined by apps' completion codes and bundle // completion code. If bundle installation fails or installation completed after @@ -681,7 +667,8 @@ void ProgressWnd::OnComplete(const ObserverCompletionInfo& observer_info) { // TODO(omaha3): Do we want to avoid launching commands during an interactive // /ua update? If so, we'll need to handle that somehow. Using the observer // handles the silent update and install cases as well as the OnDemand case. - bool launch_commands_succeeded = LaunchCmdLines(observer_info); + bool launch_commands_succeeded = LaunchCommandLines(observer_info, + is_machine()); CString s; CompletionCodes overall_completion_code = @@ -703,7 +690,7 @@ void ProgressWnd::OnComplete(const ObserverCompletionInfo& observer_info) { case COMPLETION_CODE_ERROR: // If all apps are canceled, no need to display any dialog. if (AreAllAppsCanceled(observer_info.apps_info)) { - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return; } else { cur_state_ = STATE_COMPLETE_ERROR; @@ -767,7 +754,7 @@ void ProgressWnd::OnComplete(const ObserverCompletionInfo& observer_info) { case COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND: cur_state_ = STATE_COMPLETE_SUCCESS; if (launch_commands_succeeded) { - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return; } @@ -777,60 +764,14 @@ void ProgressWnd::OnComplete(const ObserverCompletionInfo& observer_info) { break; case COMPLETION_CODE_EXIT_SILENTLY: cur_state_ = STATE_COMPLETE_SUCCESS; - VERIFY1(SUCCEEDED(CloseWindow())); + VERIFY_SUCCEEDED(CloseWindow()); return; default: ASSERT1(false); break; } - VERIFY1(SUCCEEDED(ChangeControlState())); -} - -HRESULT ProgressWnd::LaunchCmdLine(const AppCompletionInfo& app_info) { - CORE_LOG(L3, (_T("[ProgressWnd::LaunchCmdLine][%s]"), - app_info.post_install_launch_command_line)); - if (app_info.post_install_launch_command_line.IsEmpty()) { - return S_OK; - } - - if (app_info.completion_code != COMPLETION_CODE_LAUNCH_COMMAND && - app_info.completion_code != - COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND) { - CORE_LOG(LW, (_T("Launch command line [%s] is not empty but completion ") - _T("code [%d] doesn't require a launch"), - app_info.post_install_launch_command_line.GetString(), - app_info.completion_code)); - return S_OK; - } - - ASSERT1(SUCCEEDED(app_info.error_code)); - ASSERT1(!app_info.is_noupdate); - - HRESULT hr = goopdate_utils::LaunchCmdLine( - is_machine(), app_info.post_install_launch_command_line, NULL, NULL); - if (FAILED(hr)) { - CORE_LOG(LE, (_T("[goopdate_utils::LaunchCmdLine failed][0x%x]"), hr)); - return hr; - } - - return S_OK; -} - -bool ProgressWnd::LaunchCmdLines(const ObserverCompletionInfo& info) { - bool result = true; - - CORE_LOG(L3, (_T("[ProgressWnd::LaunchCmdLines]"))); - for (size_t i = 0; i < info.apps_info.size(); ++i) { - const AppCompletionInfo& app_info = info.apps_info[i]; - if (FAILED(app_info.error_code)) { - continue; - } - result &= SUCCEEDED(LaunchCmdLine(app_info)); - VERIFY1(result); - } - - return result; + VERIFY_SUCCEEDED(ChangeControlState()); } HRESULT ProgressWnd::ChangeControlState() { @@ -855,13 +796,23 @@ HRESULT ProgressWnd::SetMarqueeMode(bool is_marquee) { return S_OK; } +void ProgressWnd::DisplayNewState(const CString& state) { + VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), state)); + + CString title; + title.FormatMessage(IDS_APPLICATION_NAME_CONCATENATION, + state, + base_window_title_); + SetWindowText(title); +} + bool ProgressWnd::IsInstallStoppedWindowPresent() { return install_stopped_wnd_.get() && install_stopped_wnd_->IsWindow(); } bool ProgressWnd::CloseInstallStoppedWindow() { if (IsInstallStoppedWindowPresent()) { - VERIFY1(SUCCEEDED(install_stopped_wnd_->CloseWindow())); + VERIFY_SUCCEEDED(install_stopped_wnd_->CloseWindow()); install_stopped_wnd_.reset(); return true; } else { @@ -870,4 +821,3 @@ bool ProgressWnd::CloseInstallStoppedWindow() { } } // namespace omaha - diff --git a/omaha/ui/progress_wnd.h b/omaha/ui/progress_wnd.h index dcc82fcd0..92704a6fd 100644 --- a/omaha/ui/progress_wnd.h +++ b/omaha/ui/progress_wnd.h @@ -171,10 +171,9 @@ class ProgressWnd virtual bool MaybeCloseWindow(); // Helpers. - HRESULT LaunchCmdLine(const AppCompletionInfo& app_info); - bool LaunchCmdLines(const ObserverCompletionInfo& info); HRESULT ChangeControlState(); HRESULT SetMarqueeMode(bool is_marquee); + void DisplayNewState(const CString& state); bool IsInstallStoppedWindowPresent(); @@ -221,6 +220,8 @@ class ProgressWnd bool is_canceled_; bool is_chrome_appid_; + CString base_window_title_; + struct ControlState { const int id_; const ControlAttributes attr_[ProgressWnd::STATE_END + 1]; @@ -238,4 +239,3 @@ class ProgressWnd } // namespace omaha #endif // OMAHA_UI_PROGRESS_WND_H_ - diff --git a/omaha/ui/splash_screen.cc b/omaha/ui/splash_screen.cc index 8f102da82..f1193af02 100644 --- a/omaha/ui/splash_screen.cc +++ b/omaha/ui/splash_screen.cc @@ -33,7 +33,7 @@ namespace { const int kClosingTimerID = 1; -// Frequency that the window changes alpah blending value during fading stage. +// Frequency that the window changes alpha blending value during fading stage. const int kTimerInterval = 100; // Alpha blending values for the fading effect. diff --git a/omaha/ui/splash_screen.h b/omaha/ui/splash_screen.h index a21dc3099..2314c9749 100644 --- a/omaha/ui/splash_screen.h +++ b/omaha/ui/splash_screen.h @@ -77,7 +77,7 @@ class SplashScreen STATE_CLOSED, }; - // Creates the window and adjusts its size and apperance. + // Creates the window and adjusts its size and appearance. HRESULT Initialize(); void InitProgressBar(); @@ -103,7 +103,7 @@ class SplashScreen BOOL& handled); // NOLINT(runtime/references) LLock lock_; // Lock for access synchronization of this object. - Thread thread_; // Thread that creats the window and runs the message loop. + Thread thread_; // Thread that creates the window and runs the message loop. WindowState state_; // State of the object. // Indicates whether timer for fading effect has been created. diff --git a/omaha/ui/ui.cc b/omaha/ui/ui.cc index 7a12632b0..b1aa1b19d 100644 --- a/omaha/ui/ui.cc +++ b/omaha/ui/ui.cc @@ -83,9 +83,9 @@ void OmahaWnd::InitializeDialog() { // NOLINT VERIFY1(SetWindowText(client_utils::GetInstallerDisplayName(bundle_name_))); VERIFY1(CenterWindow(NULL)); - VERIFY1(SUCCEEDED(WindowUtils::SetWindowIcon(m_hWnd, + VERIFY_SUCCEEDED(WindowUtils::SetWindowIcon(m_hWnd, IDI_APP, - address(hicon_)))); + address(hicon_))); // Disable the Maximize System Menu item. HMENU menu = ::GetSystemMenu(*this, false); @@ -206,7 +206,7 @@ bool OmahaWnd::OnComplete() { is_complete_ = true; - VERIFY1(SUCCEEDED(EnableClose(true))); + VERIFY_SUCCEEDED(EnableClose(true)); return true; } diff --git a/third_party/breakpad b/third_party/breakpad new file mode 160000 index 000000000..bc7ddae23 --- /dev/null +++ b/third_party/breakpad @@ -0,0 +1 @@ +Subproject commit bc7ddae23425cee8999e4e8ed61f77a62f058cbf diff --git a/third_party/googletest b/third_party/googletest new file mode 160000 index 000000000..47f819c3c --- /dev/null +++ b/third_party/googletest @@ -0,0 +1 @@ +Subproject commit 47f819c3ca54fb602f432904443e00a0a1fe2f42 diff --git a/third_party/libzip b/third_party/libzip new file mode 160000 index 000000000..66e496489 --- /dev/null +++ b/third_party/libzip @@ -0,0 +1 @@ +Subproject commit 66e496489bdae81bfda8b0088172871d8fda0032 diff --git a/third_party/zlib b/third_party/zlib new file mode 160000 index 000000000..cacf7f1d4 --- /dev/null +++ b/third_party/zlib @@ -0,0 +1 @@ +Subproject commit cacf7f1d4e3d44d871b605da3b647f07d718623f